Add Heltec RC32, RC52 and RCC6 boards, and LC760CA GNSS support (#11572)

* refactor(graphics): select Arduino_GFX panels with a capability flag

TFTDisplay tested `defined(HACKADAY_COMMUNICATOR)` in a dozen places to mean
"this panel is driven by Arduino_GFX rather than LovyanGFX". Every new
Arduino_GFX board had to be appended to all of them.

Move the decision into the variant as USE_ARDUINO_GFX so the display code
stops naming individual boards. No behaviour change: the Hackaday Communicator
is still the only board that sets it.

* feat(boards): add Heltec RC32, RC52 and RCC6

Three boards around the same 128x220 NV3001B panel: RC32 (ESP32-S3), RCC6
(ESP32-C6) and RC52 (nRF52840). They differ only in how the panel bus is
wired, so they share one branch in TFTDisplay behind TFT_NV3001B.

RC32 and RC52 also carry a rotary encoder on a TCA6408 I2C expander. That
lands as its own input source rather than as board conditionals inside
i2cButton, which is the M5Stack UnitC6L button driver and stays untouched.

On RC52 and RCC6 the panel is an add-on module, so probe it before reporting
a screen. The probe reuses the bit-banged SPI helper that already backs the
T114 ST7789 check.

Arduino_GFX is pinned to the upstream commit that added the NV3001B driver;
it has not shipped in a tagged release yet.

Co-Authored-By: Quency-D <55523105+Quency-D@users.noreply.github.com>

* feat(gps): detect and configure the LC760CA GNSS module

The LC760CA is another Unicore part, so it joins the $PDTINFO probe family
and reuses the CM121 message-rate setup. It answers with CC1161W.

GNSS_MODEL_LC760CA goes immediately before GNSS_MODEL_GENERIC_NMEA: the
sentinel has to stay last because isValidGnssModel() uses it as the exclusive
upper bound on values the probe cache may hold. Placing the new model after
it would leave LC760CA permanently uncacheable.

Co-Authored-By: Quency-D <55523105+Quency-D@users.noreply.github.com>

* fix(graphics): re-init the NV3001B after the panel rail comes back

DISPLAYOFF de-asserts VTFT_CTRL, which cuts power to the panel, so the
controller loses MADCTL, COLMOD and gamma. displayOn() only sends sleep-out
and cannot restore them, leaving the panel dark or in the wrong format after
wake. Re-run begin() once the rail has settled, and repaint in full since the
re-init leaves display RAM undefined.

Also stop the TCA6408 rotary polling from two threads at once. Registering as
an InputPollable meant InputBroker's pollSoon task could call pollOnce() while
runOnce() was mid-transfer on the main thread, with nothing serialising Wire
or the decoder state. Drop InputPollable and have the interrupt wake the
thread instead, the way ButtonThread does, so the bus and the decode stay on
one thread.

* fix(graphics): skip the NV3001B wake when re-init fails

begin() reports whether the bus came up. Ignoring it meant a failed re-init
still lit the backlight and drove a full-screen repaint at a panel that was
never initialised.

* chore(boards): ship the Heltec RC boards at release level

release is the normal level for a variant; the matrix generator still builds
each of these in this PR because they add a new platformio.ini.

---------

Co-authored-by: Quency-D <55523105+Quency-D@users.noreply.github.com>
This commit is contained in:
Ben Meadors
2026-08-23 11:00:37 +00:00
committed by GitHub
co-authored by Quency-D
parent ac330e6a6b
commit bfd1e1a231
23 files changed
+1031 -17

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"psram_type": "opi",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "heltec_rc32"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "Heltec RC32 (16 MB FLASH, 8 MB PSRAM)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
+54
View File
@@ -0,0 +1,54 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x2886", "0x1667"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
"variant": "heltec_rc52",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "Heltec RC52",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
+29
View File
@@ -0,0 +1,29 @@
{
"build": {
"arduino": {
"partitions": "default_16MB.csv"
},
"core": "esp32",
"f_cpu": "160000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32c6",
"variant": "heltec_rcc6"
},
"connectivity": ["bluetooth", "wifi", "lora"],
"debug": {
"openocd_target": "esp32c6.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "Heltec RCC6",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
+8 -2
View File
@@ -742,6 +742,11 @@ bool GPS::verifyCachedProbePresence()
_serial_gps->write("$PDTINFO\r\n");
present = (getACK("CM121", 900) == GNSS_RESPONSE_OK);
break;
case GNSS_MODEL_LC760CA:
cachedProbeModelName = "LC760CA";
_serial_gps->write("$PDTINFO\r\n");
present = (getACK("CC1161W", 900) == GNSS_RESPONSE_OK);
break;
case GNSS_MODEL_UBLOX6:
case GNSS_MODEL_UBLOX7:
case GNSS_MODEL_UBLOX8:
@@ -1115,7 +1120,7 @@ bool GPS::setup()
} else {
LOG_INFO("GNSS module config saved");
}
} else if (gnssModel == GNSS_MODEL_CM121) {
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_CM121, GNSS_MODEL_LC760CA)) {
// only ask for RMC and GGA
// enable GGA
_serial_gps->write("$CFGMSG,0,0,1,1*1B\r\n");
@@ -1716,7 +1721,8 @@ GnssModel_t GPS::probe(int serialSpeed)
std::vector<ChipInfo> unicore = {{"UC6580", "UC6580", GNSS_MODEL_UC6580},
{"UM600", "UM600", GNSS_MODEL_UC6580},
{"CM121", "CM121", GNSS_MODEL_CM121},
{"CC1167Q", "CC1167Q", GNSS_MODEL_CM121}};
{"CC1167Q", "CC1167Q", GNSS_MODEL_CM121},
{"LC760CA", "CC1161W", GNSS_MODEL_LC760CA}};
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
currentDelay = 20;
currentStep = 2;
+3
View File
@@ -52,6 +52,9 @@ typedef enum {
GNSS_MODEL_AG3352,
GNSS_MODEL_LS20031,
GNSS_MODEL_CM121,
GNSS_MODEL_LC760CA,
// Keep GNSS_MODEL_GENERIC_NMEA last: isValidGnssModel() uses it as the exclusive upper bound
// for values the probe cache is allowed to hold.
GNSS_MODEL_GENERIC_NMEA // generic NMEA source (e.g. gpsd); skips chip-specific probe and init
} GnssModel_t;
+50 -12
View File
@@ -205,7 +205,7 @@ static void rak14014_tpIntHandle(void)
_rak14014_touch_int = true;
}
#elif defined(HACKADAY_COMMUNICATOR)
#elif defined(USE_ARDUINO_GFX)
#include <Arduino_GFX_Library.h>
Arduino_GFX *tft = nullptr;
@@ -1397,7 +1397,7 @@ void TFTDisplay::display(bool fromBlank)
}
}
}
#if defined(HACKADAY_COMMUNICATOR)
#if defined(USE_ARDUINO_GFX)
tft->draw16bitBeRGBBitmap(0, yStart, repaintChunkBuffer, displayWidth, rowsThisChunk);
#else
tft->pushImage(0, yStart, displayWidth, rowsThisChunk, repaintChunkBuffer);
@@ -1564,7 +1564,7 @@ void TFTDisplay::display(bool fromBlank)
const uint8_t lines_updated = 1;
#endif
#if defined(HACKADAY_COMMUNICATOR)
#if defined(USE_ARDUINO_GFX)
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate],
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
#else
@@ -1639,12 +1639,24 @@ void TFTDisplay::sendCommand(uint8_t com)
switch (com) {
case DISPLAYON: {
LOG_DEBUG("Display on");
#if defined(TFT_NV3001B)
// DISPLAYOFF cuts the panel rail, so the controller loses its configuration and sleep-out
// alone cannot bring it back. Restore the rail, let it settle, then re-run the init sequence.
digitalWrite(VTFT_CTRL, TFT_EN_ON);
delay(10);
if (!tft->begin(SPI_FREQUENCY)) {
// Nothing below this point can reach the panel, so skip the wake instead of lighting
// the backlight and repainting over a bus that did not come up.
LOG_ERROR("NV3001B re-init failed on wake");
break;
}
#endif
backlightEnable->set(true);
#if ARCH_PORTDUINO
display(true);
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
#elif defined(USE_ARDUINO_GFX)
tft->displayOn();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
!defined(HELTEC_MESH_NODE_T1)
@@ -1652,7 +1664,13 @@ void TFTDisplay::sendCommand(uint8_t com)
tft->powerSaveOff();
#endif
#ifdef VTFT_CTRL
#if defined(TFT_NV3001B)
// Re-init left display RAM undefined, so repaint in full rather than diff against a
// buffer that no longer describes the panel.
display(true);
#endif
#if defined(VTFT_CTRL) && !defined(TFT_NV3001B) // NV3001B panels already powered the rail above
digitalWrite(VTFT_CTRL, LOW);
#endif
#ifdef UNPHONE
@@ -1660,7 +1678,7 @@ void TFTDisplay::sendCommand(uint8_t com)
#endif
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
!defined(USE_ARDUINO_GFX) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
tft->setBrightness(172);
#endif
break;
@@ -1672,7 +1690,7 @@ void TFTDisplay::sendCommand(uint8_t com)
tft->clear();
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
#elif defined(USE_ARDUINO_GFX)
tft->displayOff();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
!defined(HELTEC_MESH_NODE_T1)
@@ -1687,7 +1705,7 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(false); // using unPhone library
#endif
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
#elif !defined(M5STACK) && !defined(USE_ARDUINO_GFX)
tft->setBrightness(0);
#endif
break;
@@ -1703,7 +1721,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
{
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
// todo
#elif !defined(HACKADAY_COMMUNICATOR)
#elif !defined(USE_ARDUINO_GFX)
tft->setBrightness(_brightness);
LOG_DEBUG("Brightness is set to value: %i ", _brightness);
#endif
@@ -1721,7 +1739,7 @@ bool TFTDisplay::hasTouch(void)
{
#ifdef RAK14014
return true;
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(USE_ARDUINO_GFX) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1)
return tft->touch() != nullptr;
#else
return false;
@@ -1740,7 +1758,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
} else {
return false;
}
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(USE_ARDUINO_GFX) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1)
return tft->getTouch(x, y);
#else
return false;
@@ -1768,6 +1786,21 @@ bool TFTDisplay::connect()
tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */,
12 /* col offset 1 */, 0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */,
nv3007_279_init_operations, sizeof(nv3007_279_init_operations));
#elif defined(TFT_NV3001B)
// The Heltec RC panels all use the same controller and differ only in how the bus is wired.
#if defined(HELTEC_RC52)
// nRF52840: the panel sits on SPI1, clear of the LoRa radio on SPI0.
Arduino_DataBus *bus = new Arduino_HWSPI(TFT_RS, TFT_CS, &SPI1, true /* is_shared_interface */);
#elif defined(HELTEC_RCC6)
// ESP32-C6: the panel shares pins with the LoRa host, so bit-bang it rather than claim the peripheral.
Arduino_DataBus *bus = new Arduino_SWSPI(TFT_RS, TFT_CS, TFT_SCL, TFT_SDA, GFX_NOT_DEFINED /* MISO */);
#else
// ESP32-S3: keep the panel off the LoRa FSPI host, since Arduino_GFX reconfigures whichever bus it is handed.
Arduino_DataBus *bus =
new Arduino_ESP32SPI(TFT_RS, TFT_CS, TFT_SCL, TFT_SDA, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
#endif
tft = new Arduino_NV3001B(bus, TFT_RST, 3 /* rotation */, true /* IPS */, TFT_WIDTH, TFT_HEIGHT, 0 /* col offset 1 */,
0 /* row offset 1 */, 0 /* col offset 2 */, 0 /* row offset 2 */);
#else
tft = new LGFX;
#endif
@@ -1779,8 +1812,13 @@ bool TFTDisplay::connect()
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
#endif
#ifdef HACKADAY_COMMUNICATOR
#ifdef USE_ARDUINO_GFX
#if defined(TFT_NV3001B)
// Arduino_SWSPI ignores the clock argument, so this only bites on the hardware-SPI variants.
bool beginStatus = tft->begin(SPI_FREQUENCY);
#else
bool beginStatus = tft->begin();
#endif
if (beginStatus)
LOG_DEBUG("TFT Success");
else
+8
View File
@@ -20,6 +20,7 @@
#include "input/RotaryEncoderImpl.h"
#include "input/RotaryEncoderInterruptImpl1.h"
#include "input/SerialKeyboardImpl.h"
#include "input/TCA6408Rotary.h"
#include "input/UpDownInterruptImpl1.h"
#include "input/i2cButton.h"
#if HAS_TRACKBALL
@@ -468,6 +469,13 @@ void InputBroker::Init()
#if defined(M5STACK_UNITC6L)
i2cButton = new i2cButtonThread("i2cButtonThread");
#endif
#if defined(HAS_TCA6408_ROTARY)
tca6408Rotary = new TCA6408Rotary("TCA6408Rotary");
if (!tca6408Rotary->init()) {
delete tca6408Rotary;
tca6408Rotary = nullptr;
}
#endif
#ifdef INPUTBROKER_MATRIX_TYPE
kbMatrixImpl = new KbMatrixImpl();
kbMatrixImpl->init();
+156
View File
@@ -0,0 +1,156 @@
#include "TCA6408Rotary.h"
#if defined(HAS_TCA6408_ROTARY)
#include "Throttle.h"
#include "main.h"
#include <Wire.h>
namespace
{
constexpr uint8_t TCA6408_ADDR = 0x20;
constexpr uint8_t TCA6408_INPUT_REG = 0x00;
constexpr uint8_t TCA6408_POLARITY_REG = 0x02;
constexpr uint8_t TCA6408_CONFIG_REG = 0x03;
constexpr uint8_t TCA6408_ROTARY_A_MASK = 0x01;
constexpr uint8_t TCA6408_ROTARY_B_MASK = 0x02;
constexpr uint8_t TCA6408_ROTARY_MASK = TCA6408_ROTARY_A_MASK | TCA6408_ROTARY_B_MASK;
constexpr uint32_t TCA6408_DEBOUNCE_MS = 5;
constexpr uint32_t TCA6408_POLL_MS = 100;
enum class RotaryAction : uint8_t { NONE, UP, DOWN };
} // namespace
TCA6408Rotary *tca6408Rotary;
TCA6408Rotary *TCA6408Rotary::instance = nullptr;
TCA6408Rotary::TCA6408Rotary(const char *name)
: concurrency::OSThread(name, TCA6408_POLL_MS), _originName(name), inputState(TCA6408_ROTARY_MASK)
{
}
bool TCA6408Rotary::init()
{
if (!inputBroker)
return false;
powerSensorBus();
pinMode(SENSOR_INT, INPUT_PULLUP);
// No input inversion, all eight pins configured as inputs.
if (!writeRegister(TCA6408_POLARITY_REG, 0x00) || !writeRegister(TCA6408_CONFIG_REG, 0xFF) || !readInput(inputState)) {
LOG_INFO("TCA6408 rotary not detected");
concurrency::OSThread::disable();
return false;
}
inputBroker->registerSource(this);
instance = this;
attachInterrupt(digitalPinToInterrupt(SENSOR_INT), interruptHandler, FALLING);
ready = true;
LOG_INFO("TCA6408 rotary ready at 0x%02x", TCA6408_ADDR);
return true;
}
int32_t TCA6408Rotary::runOnce()
{
if (!ready)
return concurrency::OSThread::disable();
uint8_t newState = 0;
if (!readInput(newState)) {
LOG_DEBUG("TCA6408 rotary read failed");
return TCA6408_POLL_MS;
}
handleTransition(newState);
inputState = newState;
return TCA6408_POLL_MS;
}
// Only wakes the thread. Reading the expander here would put an I2C transfer in interrupt
// context and race runOnce() for the bus and the decoder state.
void TCA6408Rotary::interruptHandler()
{
if (!instance)
return;
instance->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
concurrency::mainDelay.interruptFromISR(&higherWake);
}
void TCA6408Rotary::powerSensorBus()
{
#ifdef SENSOR_POWER_CTRL_PIN
pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON);
#ifdef PERIPHERAL_WARMUP_MS
delay(PERIPHERAL_WARMUP_MS);
#else
delay(20);
#endif
#endif
}
bool TCA6408Rotary::writeRegister(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(TCA6408_ADDR);
Wire.write(reg);
Wire.write(value);
return Wire.endTransmission() == 0;
}
bool TCA6408Rotary::readInput(uint8_t &value)
{
Wire.beginTransmission(TCA6408_ADDR);
Wire.write(TCA6408_INPUT_REG);
if (Wire.endTransmission(false) != 0)
return false;
if (Wire.requestFrom(TCA6408_ADDR, static_cast<uint8_t>(1)) != 1)
return false;
value = Wire.read();
return true;
}
// Whichever of A/B falls first decides the direction for the whole detent; activeLowPhase then
// suppresses further events until both inputs come back high. The rising-edge cases below catch
// detents whose falling edge was missed because the poll landed mid-rotation.
void TCA6408Rotary::handleTransition(uint8_t newState)
{
const uint8_t changed = (inputState ^ newState) & TCA6408_ROTARY_MASK;
const bool aLow = (newState & TCA6408_ROTARY_A_MASK) == 0;
const bool bLow = (newState & TCA6408_ROTARY_B_MASK) == 0;
RotaryAction action = RotaryAction::NONE;
if (!aLow && !bLow)
activeLowPhase = false; // back at the detent, arm for the next turn
if (!activeLowPhase) {
if ((changed & TCA6408_ROTARY_A_MASK) && aLow && !bLow) {
action = RotaryAction::UP;
activeLowPhase = true;
} else if ((changed & TCA6408_ROTARY_B_MASK) && bLow && !aLow) {
action = RotaryAction::DOWN;
activeLowPhase = true;
} else if ((changed & TCA6408_ROTARY_A_MASK) && !aLow && bLow) {
action = RotaryAction::UP;
} else if ((changed & TCA6408_ROTARY_B_MASK) && !bLow && aLow) {
action = RotaryAction::DOWN;
}
}
if (action == RotaryAction::NONE || Throttle::isWithinTimespanMs(lastEventMs, TCA6408_DEBOUNCE_MS))
return;
lastEventMs = millis();
InputEvent event = {};
event.source = _originName;
event.inputEvent = action == RotaryAction::DOWN ? INPUT_BROKER_DOWN : INPUT_BROKER_UP;
LOG_DEBUG("TCA6408 rotary event %d state=0x%02x", event.inputEvent, newState);
notifyObservers(&event);
}
#endif
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "InputBroker.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#if defined(HAS_TCA6408_ROTARY)
/**
* Rotary encoder on the A/B inputs of a TCA6408 I2C GPIO expander. SENSOR_INT goes low on any
* input change; the interrupt only wakes this thread, so all I2C and decoder state stay on it.
*/
class TCA6408Rotary : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
explicit TCA6408Rotary(const char *name);
bool init();
int32_t runOnce() override;
private:
static void interruptHandler();
void powerSensorBus();
bool writeRegister(uint8_t reg, uint8_t value);
bool readInput(uint8_t &value);
void handleTransition(uint8_t newState);
const char *_originName;
uint8_t inputState;
uint32_t lastEventMs = 0;
bool ready = false;
// Latched between the first falling edge of a detent and both inputs returning high,
// so one detent reports exactly one event.
bool activeLowPhase = false;
static TCA6408Rotary *instance;
};
extern TCA6408Rotary *tca6408Rotary;
#endif
+60 -3
View File
@@ -107,7 +107,7 @@ __attribute__((noinline)) void variantDefaultConfig() {}
__attribute__((noinline)) void variantDefaultModuleConfig() __attribute__((weak));
__attribute__((noinline)) void variantDefaultModuleConfig() {}
#ifdef HELTEC_MESH_NODE_T114
#if defined(HELTEC_MESH_NODE_T114) || defined(TFT_NV3001B_DETECT)
uint32_t read8(uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
{
@@ -157,6 +157,10 @@ uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy, uint8_t cs, uint8_
return ret;
}
#endif
#ifdef HELTEC_MESH_NODE_T114
uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
{
pinMode(cs, OUTPUT);
@@ -178,6 +182,57 @@ uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_
#endif
#ifdef TFT_NV3001B_DETECT
// The NV3001B panel is an add-on module on these boards, so probe for it before assuming a screen.
static constexpr uint32_t NV3001B_PANEL_ID = 0x300101;
static constexpr uint32_t NV3001B_RESET_DELAY_MS = 120; // NV3001B_RST_DELAY, per the Arduino_GFX driver
bool nv3001bPanelPresent(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst, uint8_t en, uint8_t bl)
{
pinMode(en, OUTPUT);
digitalWrite(en, TFT_EN_ON);
pinMode(bl, OUTPUT);
digitalWrite(bl, TFT_BACKLIGHT_ON);
delay(NV3001B_RESET_DELAY_MS);
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
pinMode(sck, OUTPUT);
digitalWrite(sck, LOW);
pinMode(mosi, OUTPUT);
pinMode(dc, OUTPUT);
pinMode(rst, OUTPUT);
digitalWrite(rst, HIGH);
delay(NV3001B_RESET_DELAY_MS);
digitalWrite(rst, LOW); // Hardware Reset
delay(NV3001B_RESET_DELAY_MS);
digitalWrite(rst, HIGH);
delay(NV3001B_RESET_DELAY_MS);
// 0x04 reports the whole 24-bit display ID; 0xDA/0xDB/0xDC report it one byte at a time.
// A panel that answers either way is present.
uint32_t rddid = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst);
uint32_t rdid = (readwrite8(0xDA, 8, 0, cs, sck, mosi, dc, rst) << 16) |
(readwrite8(0xDB, 8, 0, cs, sck, mosi, dc, rst) << 8) | readwrite8(0xDC, 8, 0, cs, sck, mosi, dc, rst);
LOG_INFO("NV3001B probe RDDID=0x%06x RDID=0x%06x", (unsigned int)rddid, (unsigned int)rdid);
if (rddid == NV3001B_PANEL_ID || rdid == NV3001B_PANEL_ID) {
LOG_INFO("NV3001B panel detected");
return true;
}
// All ones means the data line floated, all zeroes means something held it low; either way no panel
// answered, so drop the rail again rather than leave an empty header powered.
LOG_INFO("NV3001B panel not detected");
digitalWrite(bl, TFT_BACKLIGHT_OFF);
digitalWrite(en, TFT_EN_OFF);
pinMode(en, INPUT);
return false;
}
#endif
// When armed by loadFromDisk, the decode callback writes satellite entries
// straight into these maps instead of the temp vectors. Nullptr = legacy
// push_back-to-vector path for backup/restore and other decoders.
@@ -1046,12 +1101,14 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
#if defined(USE_EINK) || defined(HAS_SPI_TFT) || defined(USE_SPISSD1306)
bool hasScreen = true;
#ifdef HELTEC_MESH_NODE_T114
#if defined(TFT_NV3001B_DETECT)
hasScreen = nv3001bPanelPresent(TFT_CS, TFT_SCL, TFT_SDA, TFT_RS, TFT_RST, TFT_EN, TFT_BL);
#elif defined(HELTEC_MESH_NODE_T114)
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
if (st7789_id == 0xFFFFFF) {
hasScreen = false;
}
#endif // HELTEC_MESH_NODE_T114
#endif // TFT_NV3001B_DETECT / HELTEC_MESH_NODE_T114
#elif ARCH_PORTDUINO
bool hasScreen = false;
if (portduino_config.displayPanel)
+4
View File
@@ -221,6 +221,10 @@
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V4_R8
#elif defined(MINI_EPAPER_S3)
#define HW_VENDOR meshtastic_HardwareModel_MINI_EPAPER_S3
#elif defined(HELTEC_RC32)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_RC32
#elif defined(HELTEC_RCC6)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_RCC6
#else
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#endif
+2
View File
@@ -149,6 +149,8 @@
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_TOWER_V2
#elif defined(HELTEC_MESH_NODE_T096)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T096
#elif defined(HELTEC_RC52)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_RC52
#else
#define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN
#endif
@@ -0,0 +1,20 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303A
#define USB_PID 0x1001
static const uint8_t TX = 16;
static const uint8_t RX = 17;
static const int8_t SDA = -1;
static const int8_t SCL = -1;
static const uint8_t MISO = 20;
static const uint8_t SCK = 21;
static const uint8_t MOSI = 22;
static const uint8_t SS = 23;
#endif /* Pins_Arduino_h */
@@ -0,0 +1,35 @@
[env:heltec-rcc6]
custom_meshtastic_hw_model = 143
custom_meshtastic_hw_model_slug = HELTEC_RCC6
custom_meshtastic_architecture = esp32-c6
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = Heltec RCC6
custom_meshtastic_images = heltec-rcc6.svg
custom_meshtastic_tags = Heltec
custom_meshtastic_partition_scheme = 16MB
custom_meshtastic_has_mui = false
extends = esp32c6_base
board = heltec_rcc6
board_level = release
board_build.partitions = default_16MB.csv
upload_protocol = esptool
build_flags =
${esp32c6_base.build_flags}
-D HELTEC_RCC6
-I variants/esp32c6/heltec_rcc6
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
-DMESHTASTIC_EXCLUDE_AUDIO=1
lib_deps =
${esp32c6_base.lib_deps}
# renovate: datasource=git-refs depName=moononournation-Arduino_GFX packageName=https://github.com/moononournation/Arduino_GFX gitBranch=master
# Pinned to a commit rather than a tag: the NV3001B driver has not shipped in a release yet.
https://github.com/moononournation/Arduino_GFX/archive/4d5afb05ce70a51895fe65efc7838d606b2a95a7.zip
lib_ignore =
${esp32c6_base.lib_ignore}
ESP32 Codec2
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#define _VARIANT_HELTEC_RCC6_
#define BUTTON_PIN 9
#define HAS_SCREEN 1
#define HAS_SPI_TFT 1
#define USE_TFTDISPLAY 1
#define USE_ARDUINO_GFX 1
#define TFT_NV3001B 1
#define TFT_NV3001B_DETECT 1
#define HAS_GPS 0
#define HAS_WIRE 0
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define USE_SX1262
#define LORA_SCK 21
#define LORA_MISO 20
#define LORA_MOSI 22
#define LORA_CS 23
#define LORA_RESET 8
#define LORA_DIO1 19
#define LORA_BUSY 10
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_BUSY
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define BATTERY_PIN 6
#define ADC_CHANNEL ADC_CHANNEL_6
#define ADC_CTRL 5
#define ADC_CTRL_ENABLED HIGH
#define ADC_MULTIPLIER 4.95f
#define TFT_SCL 4
#define TFT_SDA 15
#define TFT_CS 18
#define TFT_RS 3
#define TFT_DC TFT_RS
#define TFT_RST 0
#define TFT_EN 2
#define TFT_EN_ON LOW
#define TFT_EN_OFF HIGH
#define VTFT_CTRL TFT_EN
#define TFT_BL 1
#define TFT_BLK TFT_BL
#define TFT_BACKLIGHT_ON HIGH
#define TFT_BACKLIGHT_OFF LOW
#define TFT_WIDTH 128
#define TFT_HEIGHT 220
#define TFT_BLACK 0
#define SPI_FREQUENCY 4000000
#define SCREEN_ROTATE
#define SERIAL_PRINT_PORT 1
@@ -12,6 +12,7 @@
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
#define USE_TFTDISPLAY 1
#define HAS_SPI_TFT 1
#define USE_ARDUINO_GFX 1
#define USE_POWERSAVE
#define SLEEP_TIME 120
@@ -0,0 +1,60 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t TX = 43;
static const uint8_t RX = 44;
static const uint8_t SDA = 21;
static const uint8_t SCL = 18;
static const uint8_t SS = 10;
static const uint8_t MOSI = 12;
static const uint8_t MISO = 13;
static const uint8_t SCK = 11;
static const uint8_t A0 = 1;
static const uint8_t A1 = 2;
static const uint8_t A2 = 3;
static const uint8_t A3 = 4;
static const uint8_t A4 = 5;
static const uint8_t A5 = 6;
static const uint8_t A6 = 7;
static const uint8_t A7 = 8;
static const uint8_t A8 = 9;
static const uint8_t A9 = 10;
static const uint8_t A10 = 11;
static const uint8_t A11 = 12;
static const uint8_t A12 = 13;
static const uint8_t A13 = 14;
static const uint8_t A14 = 15;
static const uint8_t A15 = 16;
static const uint8_t A16 = 17;
static const uint8_t A17 = 18;
static const uint8_t A18 = 19;
static const uint8_t A19 = 20;
static const uint8_t T1 = 1;
static const uint8_t T2 = 2;
static const uint8_t T3 = 3;
static const uint8_t T4 = 4;
static const uint8_t T5 = 5;
static const uint8_t T6 = 6;
static const uint8_t T7 = 7;
static const uint8_t T8 = 8;
static const uint8_t T9 = 9;
static const uint8_t T10 = 10;
static const uint8_t T11 = 11;
static const uint8_t T12 = 12;
static const uint8_t T13 = 13;
static const uint8_t T14 = 14;
static const uint8_t RST_LoRa = 9;
static const uint8_t BUSY_LoRa = 1;
static const uint8_t DIO1_LoRa = 14;
#endif /* Pins_Arduino_h */
@@ -0,0 +1,28 @@
[env:heltec-rc32]
custom_meshtastic_hw_model = 141
custom_meshtastic_hw_model_slug = HELTEC_RC32
custom_meshtastic_architecture = esp32-s3
custom_meshtastic_display_name = Heltec RC32
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_images = heltec-rc32.svg
custom_meshtastic_tags = Heltec
custom_meshtastic_requires_dfu = true
custom_meshtastic_partition_scheme = 16MB
custom_meshtastic_has_mui = false
extends = esp32s3_base
board = heltec_rc32
board_level = release
board_build.partitions = default_16MB.csv
build_flags =
${esp32s3_base.build_flags}
-D HELTEC_RC32
-D USE_PIN_BUZZER=PIN_BUZZER
-D BOARD_HAS_PSRAM
-I variants/esp32s3/heltec_rc32
lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=moononournation-Arduino_GFX packageName=https://github.com/moononournation/Arduino_GFX gitBranch=master
# Pinned to a commit rather than a tag: the NV3001B driver has not shipped in a release yet.
https://github.com/moononournation/Arduino_GFX/archive/4d5afb05ce70a51895fe65efc7838d606b2a95a7.zip
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include_next "sdkconfig.h"
// qio_opi enables this, but Meshtastic removes the matching IDF component.
#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI
#undef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI
#endif
#define CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI 0
#ifndef CONFIG_LITTLEFS_PAGE_SIZE
#define CONFIG_LITTLEFS_MAX_PARTITIONS 3
#define CONFIG_LITTLEFS_PAGE_SIZE 256
#define CONFIG_LITTLEFS_OBJ_NAME_LEN 64
#define CONFIG_LITTLEFS_READ_SIZE 128
#define CONFIG_LITTLEFS_WRITE_SIZE 128
#define CONFIG_LITTLEFS_LOOKAHEAD_SIZE 128
#define CONFIG_LITTLEFS_CACHE_SIZE 512
#define CONFIG_LITTLEFS_BLOCK_CYCLES 512
#define CONFIG_LITTLEFS_USE_MTIME 1
#define CONFIG_LITTLEFS_MTIME_USE_SECONDS 1
#define CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT 1
#define CONFIG_LITTLEFS_ASSERTS 1
#endif
+78
View File
@@ -0,0 +1,78 @@
#ifndef _VARIANT_HELTEC_RC32_
#define _VARIANT_HELTEC_RC32_
#define HAS_SCREEN 1
#define HAS_SPI_TFT 1
#define USE_TFTDISPLAY 1
#define USE_ARDUINO_GFX 1
#define TFT_NV3001B 1
#define TFT_NV3001B_DETECT 1
#define TFT_SCL 17
#define TFT_SDA 38
#define TFT_CS 39
#define TFT_RS 16
#define TFT_DC TFT_RS
#define TFT_RST 4
#define TFT_EN 6
#define TFT_EN_ON LOW
#define TFT_EN_OFF HIGH
#define VTFT_CTRL TFT_EN
#define TFT_BL 5
#define TFT_BLK TFT_BL
#define TFT_BACKLIGHT_ON HIGH
#define TFT_BACKLIGHT_OFF LOW
#define TFT_WIDTH 128
#define TFT_HEIGHT 220
#define TFT_BLACK 0
#define SPI_FREQUENCY 8000000
#define SCREEN_ROTATE
#define BUTTON_PIN 0
#define HAS_GPS 1
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 44
#define GPS_TX_PIN 43
#define PIN_GPS_EN 45
#define GPS_EN_ACTIVE HIGH
#define PIN_GPS_RESET 40
#define GPS_RESET_MODE LOW
#define PIN_GPS_PPS 41
#define I2C_SCL 18
#define I2C_SDA 21
#define SENSOR_INT 42
#define SENSOR_RST 2
#define SENSOR_POWER_CTRL_PIN 46
#define SENSOR_POWER_ON HIGH
#define HAS_TCA6408_ROTARY 1
#define PERIPHERAL_WARMUP_MS 100
#define LED_POWER 47
#define PIN_BUZZER 48
#define USE_SX1262
#define LORA_SCK 11
#define LORA_MISO 13
#define LORA_MOSI 12
#define LORA_CS 10
#define LORA_DIO0 RADIOLIB_NC
#define LORA_DIO1 14
#define LORA_RESET 9
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY 1
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define BATTERY_PIN 7
#define ADC_CHANNEL ADC_CHANNEL_6
#define ADC_CTRL 15
#define ADC_CTRL_ENABLED HIGH
#define ADC_MULTIPLIER 4.9
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5
#endif
@@ -0,0 +1,30 @@
; Heltec RC52 nRF52840/SX1262 device
[env:heltec-rc52]
custom_meshtastic_hw_model = 142
custom_meshtastic_hw_model_slug = HELTEC_RC52
custom_meshtastic_architecture = nrf52840
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = Heltec RC52
custom_meshtastic_images = heltec-rc52.svg
custom_meshtastic_tags = Heltec
extends = nrf52840_base
board = heltec_rc52
board_level = release
debug_tool = jlink
build_flags = ${nrf52840_base.build_flags}
-UOLEDDISPLAY_REDUCE_MEMORY ; TFTDisplay.cpp needs the lib's buffer_back for dirty-window diffing
-Ivariants/nrf52840/heltec_rc52
-DHELTEC_RC52
-D PIN_BUZZER=5
-D USE_PIN_BUZZER=PIN_BUZZER
-DCONFIG_NFCT_PINS_AS_GPIOS=1
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_rc52>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=git-refs depName=moononournation-Arduino_GFX packageName=https://github.com/moononournation/Arduino_GFX gitBranch=master
# Pinned to a commit rather than a tag: the NV3001B driver has not shipped in a release yet.
https://github.com/moononournation/Arduino_GFX/archive/4d5afb05ce70a51895fe65efc7838d606b2a95a7.zip
+103
View File
@@ -0,0 +1,103 @@
#include "variant.h"
#include "Arduino.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0 - pins 0 and 1 are hardwired for xtal and should never be enabled
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
pinMode(PIN_LED1, OUTPUT);
digitalWrite(PIN_LED1, LED_STATE_OFF);
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
pinMode(PIN_GPS_PPS, INPUT);
pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON);
pinMode(SENSOR_RST, OUTPUT);
digitalWrite(SENSOR_RST, HIGH);
pinMode(SENSOR_INT, INPUT);
pinMode(PIN_BUZZER, OUTPUT);
digitalWrite(PIN_BUZZER, LOW);
}
void variant_shutdown()
{
detachInterrupt(PIN_BUTTON1);
detachInterrupt(PIN_GPS_PPS);
detachInterrupt(SENSOR_INT);
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_CTRL_PIN, !SENSOR_POWER_ON);
pinMode(PIN_BUZZER, OUTPUT);
digitalWrite(PIN_BUZZER, LOW);
pinMode(PIN_LED1, OUTPUT);
digitalWrite(PIN_LED1, LOW);
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
pinMode(RADIOCORE_FEM_EN, OUTPUT);
digitalWrite(RADIOCORE_FEM_EN, LOW);
pinMode(RADIOCORE_VFEM_CTRL, OUTPUT);
digitalWrite(RADIOCORE_VFEM_CTRL, LOW);
pinMode(PIN_WIRE_SDA, OUTPUT);
digitalWrite(PIN_WIRE_SDA, LOW);
pinMode(PIN_WIRE_SCL, OUTPUT);
digitalWrite(PIN_WIRE_SCL, LOW);
nrf_gpio_cfg_default(PIN_WIRE_SDA);
nrf_gpio_cfg_default(PIN_WIRE_SCL);
nrf_gpio_cfg_default(PIN_GPS_PPS);
nrf_gpio_cfg_default(GPS_RX_PIN);
nrf_gpio_cfg_default(GPS_TX_PIN);
nrf_gpio_cfg_default(PIN_GPS_RESET);
nrf_gpio_cfg_default(SENSOR_POWER_CTRL_PIN);
nrf_gpio_cfg_default(SENSOR_RST);
nrf_gpio_cfg_default(SENSOR_INT);
nrf_gpio_cfg_default(SX126X_DIO1);
nrf_gpio_cfg_default(SX126X_BUSY);
nrf_gpio_cfg_default(SX126X_RESET);
nrf_gpio_cfg_default(PIN_SPI_MISO);
nrf_gpio_cfg_default(PIN_SPI_MOSI);
nrf_gpio_cfg_default(PIN_SPI_SCK);
nrf_gpio_cfg_default(RADIOCORE_FEM_EN);
nrf_gpio_cfg_default(RADIOCORE_VFEM_CTRL);
pinMode(TFT_EN, OUTPUT);
digitalWrite(TFT_EN, HIGH);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, LOW);
pinMode(TFT_CS, OUTPUT);
digitalWrite(TFT_CS, LOW);
pinMode(TFT_RS, OUTPUT);
digitalWrite(TFT_RS, LOW);
pinMode(TFT_SCL, OUTPUT);
digitalWrite(TFT_SCL, LOW);
pinMode(TFT_SDA, OUTPUT);
digitalWrite(TFT_SDA, LOW);
pinMode(TFT_RST, OUTPUT);
digitalWrite(TFT_RST, LOW);
nrf_gpio_cfg_default(TFT_EN);
nrf_gpio_cfg_default(TFT_BL);
nrf_gpio_cfg_default(TFT_CS);
nrf_gpio_cfg_default(TFT_RS);
nrf_gpio_cfg_default(TFT_SCL);
nrf_gpio_cfg_default(TFT_SDA);
nrf_gpio_cfg_default(TFT_RST);
}
+136
View File
@@ -0,0 +1,136 @@
#ifndef _VARIANT_HELTEC_RC52_
#define _VARIANT_HELTEC_RC52_
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32 kHz crystal for LF
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (1)
#define NUM_ANALOG_OUTPUTS (0)
#define PIN_LED1 (0 + 15)
#define LED_POWER PIN_LED1
#define LED_BLUE (-1)
#define LED_GREEN PIN_LED1
#define LED_STATE_ON 1
#define LED_STATE_OFF (LED_STATE_ON ^ 1)
#define HAS_SCREEN 1
#define HAS_SPI_TFT 1
#define USE_TFTDISPLAY 1
#define USE_ARDUINO_GFX 1
#define TFT_NV3001B 1
#define TFT_NV3001B_DETECT 1
#define HAS_GPS 1
#define HAS_WIRE 1
/*
* Optional RS-T108 / NV3001B TFT module on P2
*/
#define TFT_SCL (0 + 30)
#define TFT_SDA (32 + 2)
#define TFT_CS (32 + 4)
#define TFT_RS (0 + 28)
#define TFT_DC TFT_RS
#define TFT_RST (0 + 10)
#define TFT_EN (32 + 13)
#define TFT_EN_ON LOW
#define TFT_EN_OFF HIGH
#define VTFT_CTRL TFT_EN
#define TFT_BL (0 + 9)
#define TFT_BACKLIGHT_ON HIGH
#define TFT_BACKLIGHT_OFF LOW
#define TFT_WIDTH 128
#define TFT_HEIGHT 220
#define SPI_FREQUENCY 8000000
#define SCREEN_ROTATE
/*
* Buttons
*/
#define PIN_BUTTON1 (32 + 10) // P1.10, external pull-up, active low
/*
* Sensor I2C and control pins
*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (32 + 11) // P1.11
#define PIN_WIRE_SCL (0 + 2) // P0.02
#define SENSOR_POWER_CTRL_PIN (0 + 12)
#define SENSOR_POWER_ON HIGH
#define SENSOR_INT (0 + 20)
#define SENSOR_RST (32 + 15)
#define HAS_TCA6408_ROTARY 1
/*
* Serial interfaces
*/
#define GPS_RX_PIN (0 + 8)
#define GPS_TX_PIN (0 + 7)
#define PIN_GPS_EN (32 + 9)
#define GPS_EN_ACTIVE HIGH
#define PIN_GPS_PPS (32 + 1)
#define PIN_GPS_RESET (32 + 6)
#define GPS_RESET_MODE LOW
#define GPS_THREAD_INTERVAL 50
#define PERIPHERAL_WARMUP_MS 100
#define PIN_SERIAL1_RX GPS_RX_PIN
#define PIN_SERIAL1_TX GPS_TX_PIN
#define PIN_SERIAL2_RX (-1)
#define PIN_SERIAL2_TX (-1)
/*
* LoRa radio
*/
#define USE_SX1262
#define SX126X_CS (0 + 13) // P0.13
#define LORA_CS SX126X_CS
#define SX126X_DIO1 (0 + 11) // P0.11
#define SX126X_BUSY (0 + 24) // P0.24
#define SX126X_RESET (32 + 0) // P1.00
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define RADIOCORE_FEM_EN (0 + 26)
#define RADIOCORE_VFEM_CTRL (0 + 16)
/*
* SPI
*/
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (0 + 14)
#define PIN_SPI_MOSI (0 + 22)
#define PIN_SPI_SCK (0 + 25)
#define PIN_SPI1_MISO (-1)
#define PIN_SPI1_MOSI TFT_SDA
#define PIN_SPI1_SCK TFT_SCL
/*
* Battery
*/
#define ADC_CTRL (0 + 4)
#define ADC_CTRL_ENABLED HIGH
#define BATTERY_PIN (0 + 31) // P0.31/AIN7
#define ADC_RESOLUTION 14
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (4.9F)
#ifdef __cplusplus
}
#endif
#endif