diff --git a/boards/heltec_rc32.json b/boards/heltec_rc32.json new file mode 100644 index 000000000..b9bafa265 --- /dev/null +++ b/boards/heltec_rc32.json @@ -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" +} diff --git a/boards/heltec_rc52.json b/boards/heltec_rc52.json new file mode 100644 index 000000000..2f87529ca --- /dev/null +++ b/boards/heltec_rc52.json @@ -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" +} diff --git a/boards/heltec_rcc6.json b/boards/heltec_rcc6.json new file mode 100644 index 000000000..972d2b864 --- /dev/null +++ b/boards/heltec_rcc6.json @@ -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" +} diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 0bd0f3212..9f29c41f0 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -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 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; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index a9a82795c..b9bd35717 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -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; diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index a3af9e7ff..f5f857797 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -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 *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 diff --git a/src/input/InputBroker.cpp b/src/input/InputBroker.cpp index 7b0f830c5..8e247bfcc 100644 --- a/src/input/InputBroker.cpp +++ b/src/input/InputBroker.cpp @@ -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(); diff --git a/src/input/TCA6408Rotary.cpp b/src/input/TCA6408Rotary.cpp new file mode 100644 index 000000000..3e1f8a9a0 --- /dev/null +++ b/src/input/TCA6408Rotary.cpp @@ -0,0 +1,156 @@ +#include "TCA6408Rotary.h" + +#if defined(HAS_TCA6408_ROTARY) + +#include "Throttle.h" +#include "main.h" +#include + +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(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 diff --git a/src/input/TCA6408Rotary.h b/src/input/TCA6408Rotary.h new file mode 100644 index 000000000..600311bb9 --- /dev/null +++ b/src/input/TCA6408Rotary.h @@ -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, 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 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ca4b30155..87aab5ad9 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -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) diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 48618b641..2c409b0b8 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -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 diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 4a6afaeae..279ad7575 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -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 diff --git a/variants/esp32c6/heltec_rcc6/pins_arduino.h b/variants/esp32c6/heltec_rcc6/pins_arduino.h new file mode 100644 index 000000000..91604ac60 --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/pins_arduino.h @@ -0,0 +1,20 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#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 */ diff --git a/variants/esp32c6/heltec_rcc6/platformio.ini b/variants/esp32c6/heltec_rcc6/platformio.ini new file mode 100644 index 000000000..66042e1cf --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/platformio.ini @@ -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 diff --git a/variants/esp32c6/heltec_rcc6/variant.h b/variants/esp32c6/heltec_rcc6/variant.h new file mode 100644 index 000000000..3436aff05 --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/variant.h @@ -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 diff --git a/variants/esp32s3/hackaday-communicator/variant.h b/variants/esp32s3/hackaday-communicator/variant.h index ab15e3e6d..b34ea861d 100644 --- a/variants/esp32s3/hackaday-communicator/variant.h +++ b/variants/esp32s3/hackaday-communicator/variant.h @@ -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 diff --git a/variants/esp32s3/heltec_rc32/pins_arduino.h b/variants/esp32s3/heltec_rc32/pins_arduino.h new file mode 100644 index 000000000..1df1080bf --- /dev/null +++ b/variants/esp32s3/heltec_rc32/pins_arduino.h @@ -0,0 +1,60 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#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 */ diff --git a/variants/esp32s3/heltec_rc32/platformio.ini b/variants/esp32s3/heltec_rc32/platformio.ini new file mode 100644 index 000000000..827f7c302 --- /dev/null +++ b/variants/esp32s3/heltec_rc32/platformio.ini @@ -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 diff --git a/variants/esp32s3/heltec_rc32/sdkconfig.h b/variants/esp32s3/heltec_rc32/sdkconfig.h new file mode 100644 index 000000000..d7d875010 --- /dev/null +++ b/variants/esp32s3/heltec_rc32/sdkconfig.h @@ -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 diff --git a/variants/esp32s3/heltec_rc32/variant.h b/variants/esp32s3/heltec_rc32/variant.h new file mode 100644 index 000000000..413d39d81 --- /dev/null +++ b/variants/esp32s3/heltec_rc32/variant.h @@ -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 diff --git a/variants/nrf52840/heltec_rc52/platformio.ini b/variants/nrf52840/heltec_rc52/platformio.ini new file mode 100644 index 000000000..f57e5f7f9 --- /dev/null +++ b/variants/nrf52840/heltec_rc52/platformio.ini @@ -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 diff --git a/variants/nrf52840/heltec_rc52/variant.cpp b/variants/nrf52840/heltec_rc52/variant.cpp new file mode 100644 index 000000000..3af5c4032 --- /dev/null +++ b/variants/nrf52840/heltec_rc52/variant.cpp @@ -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); +} \ No newline at end of file diff --git a/variants/nrf52840/heltec_rc52/variant.h b/variants/nrf52840/heltec_rc52/variant.h new file mode 100644 index 000000000..22b0b9a1a --- /dev/null +++ b/variants/nrf52840/heltec_rc52/variant.h @@ -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