diff --git a/.vscode/settings.json b/.vscode/settings.json index 81deca8f999..057e1516e09 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ }, "[powershell]": { "editor.defaultFormatter": "ms-vscode.powershell" - } + }, + "idf.currentSetup": "C:\\esp\\v6.0.1\\esp-idf" } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index e9e8f7d34e3..ed8750ac1b4 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1007,7 +1007,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32); #if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \ - defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT) || defined(RAK_WISMESH_TAP_V2)) && \ + defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT) || defined(HELTEC_V4_R2) || \ + defined(RAK_WISMESH_TAP_V2)) && \ HAS_TFT // switch BT off by default; use TFT programming mode or hotkey to enable config.bluetooth.enabled = false; diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 043bd59eff7..a7eb2be82c9 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -20,6 +20,9 @@ #include "meshUtils.h" // for pow_of_2 #include "sleep.h" #include +#ifdef ARCH_ESP32 +#include "driver/gpio.h" +#endif #include #include #include @@ -374,6 +377,13 @@ std::unique_ptr initLoRa() { std::unique_ptr rIf = nullptr; +#ifdef ARCH_ESP32 + // RadioLib calls detachInterrupt() on the IRQ pin before attaching a new one. On ESP32 this + // invokes gpio_isr_handler_remove() which requires gpio_install_isr_service() to have been + // called first. Install it here so the call is a no-op if it arrives again later. + gpio_install_isr_service(0); +#endif + #if ARCH_PORTDUINO SPISettings loraSpiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0); #else diff --git a/src/mesh/api/PacketAPI.cpp b/src/mesh/api/PacketAPI.cpp index 3f145bfe3f1..a7d6939f5a8 100644 --- a/src/mesh/api/PacketAPI.cpp +++ b/src/mesh/api/PacketAPI.cpp @@ -17,26 +17,19 @@ PacketAPI *PacketAPI::create(PacketServer *_server) } PacketAPI::PacketAPI(PacketServer *_server) - : concurrency::OSThread("PacketAPI"), isConnected(false), programmingMode(false), server(_server) + : concurrency::OSThread("PacketAPI"), isConnected(false), server(_server) { api_type = TYPE_PACKET; } int32_t PacketAPI::runOnce() { - bool success = false; -#ifndef ARCH_PORTDUINO - if (config.bluetooth.enabled) { - if (!programmingMode) { - // in programmingMode we don't send any packets to the client except this one notify - programmingMode = true; - success = notifyProgrammingMode(); - } - } else -#endif - { - success = sendPacket(); - } + // Always run the full config sync regardless of bluetooth state. The device-ui (MUI) must + // receive deviceuiConfig first so its state advances past eBootScreenDone before the + // bluetooth config arrives; only then does updateBluetoothConfig() skip enterProgrammingMode(). + // Sending only the bluetooth config via notifyProgrammingMode() when BT is enabled caused the + // TFT screen to be permanently stuck on the ">> Programming mode <<" screen. + bool success = sendPacket(); success |= receivePacket(); return success ? 10 : 50; } @@ -107,21 +100,6 @@ bool PacketAPI::sendPacket(void) return false; } -bool PacketAPI::notifyProgrammingMode(void) -{ - // tell the client we are in programming mode by sending only the bluetooth config state - LOG_INFO("force client into programmingMode"); - memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); - fromRadioScratch.id = nodeDB->getNodeNum(); - fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_tag; - fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag; - fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; - return server->sendPacket(DataPacket(0, fromRadioScratch)); -} - -/** - * return true if we got (once!) contact from our client and the server send queue is not full - */ bool PacketAPI::checkIsConnected() { isConnected |= server->hasData(); diff --git a/src/mesh/api/PacketAPI.h b/src/mesh/api/PacketAPI.h index aececf85e0d..cfcafd04405 100644 --- a/src/mesh/api/PacketAPI.h +++ b/src/mesh/api/PacketAPI.h @@ -27,10 +27,8 @@ class PacketAPI : public PhoneAPI, public concurrency::OSThread private: bool receivePacket(void); bool sendPacket(void); - bool notifyProgrammingMode(void); bool isConnected; - bool programmingMode; PacketServer *server; uint8_t txBuf[MAX_TO_FROM_RADIO_SIZE] = {0}; // dummy buf to obey PhoneAPI }; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index e58a9b52008..4b7b4125487 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -200,6 +200,8 @@ #define HW_VENDOR meshtastic_HardwareModel_TBEAM_1_WATT #elif defined(T_LORA_PAGER) #define HW_VENDOR meshtastic_HardwareModel_T_LORA_PAGER +#elif defined(HELTEC_V4_R2) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V4 #elif defined(HELTEC_V4) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_V4 #elif defined(M5STACK_UNITC6L) diff --git a/variants/esp32s3/heltec_v4_r2/pins_arduino.h b/variants/esp32s3/heltec_v4_r2/pins_arduino.h new file mode 100644 index 00000000000..d75d10546a8 --- /dev/null +++ b/variants/esp32s3/heltec_v4_r2/pins_arduino.h @@ -0,0 +1,58 @@ +#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 = 17; +static const uint8_t SCL = 18; + +static const uint8_t SS = 8; +static const uint8_t MOSI = 10; +static const uint8_t MISO = 11; +static const uint8_t SCK = 9; + +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 Vext = 36; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32s3/heltec_v4_r2/platformio.ini b/variants/esp32s3/heltec_v4_r2/platformio.ini new file mode 100644 index 00000000000..b1f34cdf36d --- /dev/null +++ b/variants/esp32s3/heltec_v4_r2/platformio.ini @@ -0,0 +1,134 @@ +; Heltec WiFi LoRa 32 V4-R2 — ESP32-S3 + SX1262 +; External 2.8" ILI9341V SPI display (240x320) + FT6336G capacitive touch (I2C) +; No on-board LCD screen +; +; Display SPI wiring (SPI3_HOST): +; GPIO47 → SCK GPIO33 → MOSI GPIO4 → MISO +; GPIO34 → LCD_CS GPIO21 → LCD_RS GPIO48 → LCD_RST GPIO3 → LED/BL +; +; Touch I2C wiring (Wire, port 0): +; GPIO17 → CTP_SDA GPIO18 → CTP_SCL +; GPIO45 → CTP_RST GPIO15 → CTP_INT (polling) +; +; GPIO6 → SD_CS (SD card shares SPI bus with display) +; +; NOTE: this base intentionally extends esp32s3_base (not heltec_v4_base) so +; that only variants/esp32s3/heltec_v4_r2/variant.h is on the include path. +; Inheriting heltec_v4_base would prepend -I variants/esp32s3/heltec_v4 first, +; causing the V4's variant.h (which contains #if HAS_TFT → USE_TFTDISPLAY) to +; shadow the R2 header and incorrectly compile TFTDisplay.cpp. + +[heltec_v4_r2_base] +extends = esp32s3_base +board = heltec_v4 +board_check = true +board_build.partitions = default_16MB.csv +build_flags = + ${esp32s3_base.build_flags} + -D HELTEC_V4 + -D HELTEC_V4_R2 + -D HAS_LORA_FEM=1 + -I variants/esp32s3/heltec_v4_r2 + +[env:heltec-v4-r2] +custom_meshtastic_hw_model = 110 +custom_meshtastic_hw_model_slug = HELTEC_V4 +custom_meshtastic_architecture = esp32-s3 +custom_meshtastic_actively_supported = true +custom_meshtastic_support_level = 2 +custom_meshtastic_display_name = Heltec V4 R2 ILI9341 Touch +custom_meshtastic_tags = Heltec +custom_meshtastic_requires_dfu = true +custom_meshtastic_partition_scheme = 16MB +custom_meshtastic_has_mui = true + +extends = heltec_v4_r2_base +build_flags = + ${heltec_v4_r2_base.build_flags} + ; ---- System ---- + -D CONFIG_ARDUHAL_LOG_COLORS + -D RADIOLIB_DEBUG_SPI=0 + -D RADIOLIB_DEBUG_PROTOCOL=0 + -D RADIOLIB_DEBUG_BASIC=0 + -D RADIOLIB_VERBOSE_ASSERT=0 + -D RADIOLIB_SPI_PARANOID=0 + -D CONFIG_DISABLE_HAL_LOCKS=1 + -D INPUTDRIVER_BUTTON_TYPE=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" + -D USE_PACKET_API + ; ---- Display ---- + -D HAS_SCREEN=0 + -D HAS_TFT=1 + ; ---- LVGL / MUI ---- + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_USE_SYSMON=0 + -D LV_USE_PROFILER=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D LV_USE_LOG=0 + -D LV_BUILD_TEST=0 + ; ---- RAM / tile budget (2 MB PSRAM) ---- + -D MAP_TILES_GREY + -D RAM_SIZE=1432 + -D STBI_ARENA_SIZE=450000 + -D LV_CACHE_DEF_SIZE=0 + ; ---- LGFX Generic driver (device-ui) ---- + -D LGFX_DRIVER_TEMPLATE + -D LGFX_DRIVER=LGFX_GENERIC + -D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\" + -D LGFX_CFG_HOST=SPI3_HOST + -D LGFX_PIN_SCK=47 + -D LGFX_PIN_MOSI=33 + -D LGFX_PIN_MISO=4 + -D LGFX_PIN_DC=21 + -D LGFX_PIN_CS=45 + -D LGFX_PIN_RST=48 + -D LGFX_PIN_BL=3 + -D LGFX_PANEL=ILI9341 + -D LGFX_INVERT_COLOR=false + -D LGFX_SCREEN_WIDTH=240 + -D LGFX_SCREEN_HEIGHT=320 + -D VIEW_320x240 + -D DISPLAY_SET_RESOLUTION + -D DISPLAY_SIZE=320x240 + -D LGFX_ROTATION=0 + -D SPI_FREQUENCY=40000000 + -D SPI_READ_FREQUENCY=16000000 + -D BRIGHTNESS_DEFAULT=130 + ; ---- FT6336G capacitive touch (FT5x06-compatible, I2C address 0x38) ---- + -D HAS_TOUCHSCREEN=1 + -D LGFX_TOUCH=FT5x06 + -D LGFX_TOUCH_I2C_FREQ=400000 + -D LGFX_TOUCH_I2C_PORT=0 + -D LGFX_TOUCH_I2C_ADDR=0x38 + -D LGFX_TOUCH_I2C_SDA=17 + -D LGFX_TOUCH_I2C_SCL=18 + -D LGFX_TOUCH_RST=16 + -D LGFX_TOUCH_INT=15 + -D LGFX_TOUCH_X_MIN=0 + -D LGFX_TOUCH_X_MAX=239 + -D LGFX_TOUCH_Y_MIN=0 + -D LGFX_TOUCH_Y_MAX=319 + -D LGFX_TOUCH_ROTATION=0 + ; ---- SD card ---- + ; SD card support is disabled: GPIO5 shares the SPI3_HOST bus with the ILI9341 + ; display (LovyanGFX uses spi_bus_initialize() directly). Enabling SDCARD_USE_SPI1 + ; causes setupSDCard() to call SPI_HSPI.begin() before LovyanGFX initialises the + ; bus, producing addApbChangeCallback duplicate errors that corrupt the display SPI + ; and prevent the boot screen from advancing to the main UI. + ; -D HAS_SDCARD + ; -D SDCARD_USE_SPI1 + ; -D SDCARD_USER_SPI_BEGIN + ; -D SPI_CS=5 + ; -D SDCARD_CS=SPI_CS + ; -D SD_SPI_FREQUENCY=SPI_FREQUENCY + + +lib_deps = + ${esp32s3_base.lib_deps} + ${device-ui_base.lib_deps} + # renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX + lovyan03/LovyanGFX@1.2.26 diff --git a/variants/esp32s3/heltec_v4_r2/variant.h b/variants/esp32s3/heltec_v4_r2/variant.h new file mode 100644 index 00000000000..a3aeb98bc79 --- /dev/null +++ b/variants/esp32s3/heltec_v4_r2/variant.h @@ -0,0 +1,86 @@ +// Heltec WiFi LoRa 32 V4-R2 — ESP32-S3 + SX1262 +// External 2.8" ILI9341V SPI display (240x320) + FT6336G I2C capacitive touch +// No on-board LCD screen + +#define VEXT_ENABLE 36 // active low, powers the LoRa antenna boost +#define VEXT_ON_VALUE LOW +#define BUTTON_PIN 0 + +#define ADC_CTRL 37 +#define ADC_CTRL_ENABLED HIGH +#define BATTERY_PIN 1 // ADC1_CH0 — battery voltage via divider; pull ADC_CTRL high to read +#define ADC_CHANNEL ADC_CHANNEL_0 +#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 +#define ADC_MULTIPLIER 4.9 * 1.045 + +#define USE_SX1262 + +#define LORA_DIO0 -1 // not connected on SX1262 +#define LORA_RESET 12 // SX1262 RESET +#define LORA_DIO1 14 // SX1262 IRQ +#define LORA_DIO2 13 // SX1262 BUSY +#define LORA_DIO3 // not connected on PCB + +#define LORA_SCK 9 +#define LORA_MISO 11 +#define LORA_MOSI 10 +#define LORA_CS 8 + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET + +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Enable Traffic Management Module +#ifndef HAS_TRAFFIC_MANAGEMENT +#define HAS_TRAFFIC_MANAGEMENT 1 +#endif + +// ---- GC1109 RF FRONT END CONFIGURATION (V4.2) ---- +// CTX -> SX1262 DIO2 (automatic via SX126X_DIO2_AS_RF_SWITCH) +// CSD -> GPIO2, CPS -> GPIO46, VCC -> GPIO7 +#define LORA_PA_POWER 7 // VFEM_Ctrl +#define LORA_GC1109_PA_EN 2 // CSD — chip enable (HIGH=on) +#define LORA_GC1109_PA_TX_EN 46 // CPS — PA mode (HIGH=full PA) + +// ---- KCT8103L RF FRONT END CONFIGURATION (V4.3) ---- +// CPS -> SX1262 DIO2 (automatic via SX126X_DIO2_AS_RF_SWITCH) +// CSD -> GPIO2, CTX -> GPIO5, VCC -> GPIO7 +#define LORA_KCT8103L_PA_CSD 2 // CSD — chip enable (HIGH=on) +#define LORA_KCT8103L_PA_CTX 5 // CTX — RX bypass (HIGH) / RX LNA (LOW) + +/* + * GPS pins — L76K GNSS module + */ +#define GPS_L76K +#define PIN_GPS_RESET (42) // GNSS_RESET +#define GPS_RESET_MODE LOW +#define PIN_GPS_EN (34) // **LISTED AS SPIIO5,FSPICS0, SUBSPICS0 IN DOCUMENTATION** +#define GPS_EN_ACTIVE LOW +#define PERIPHERAL_WARMUP_MS 1000 +#define PIN_GPS_STANDBY (40) // GNSS_WAKEUP +#define PIN_GPS_PPS (41) // GNSS_PPS +#define GPS_TX_PIN (38) // GNSS → CPU (GNSS_RX) +#define GPS_RX_PIN (39) // CPU → GNSS (GNSS_TX) +#define GPS_THREAD_INTERVAL 50 + +/* + * External display connections (set via LGFX_PIN_* in platformio.ini) + * + * Display SPI (SPI3_HOST): + * SCK → GPIO47 LCD_RS/DC → GPIO21 + * MOSI → GPIO33 CS → GPIO45 + * MISO → GPIO04 RST → GPIO48 [GPIO26 is PSRAM CS — do not use] + * BL → GPIO3 + * + * Touch I2C (Wire, port 0 — GPIO3/4): + * SDA → GPIO4 RST → GPIO45 + * SCL → GPIO18 INT → GPIO15 + */ + +// I2C bus used for touch and peripheral scan +#define I2C_SDA 17 +#define I2C_SCL 18