From 7a1d5b3011e06be482a2fe5523a0e360ce74b066 Mon Sep 17 00:00:00 2001 From: evelant Date: Tue, 14 Jul 2026 09:19:46 -0400 Subject: [PATCH] Add Heltec Wireless Tracker V2 support --- README.md | 3 + boards/heltec-wireless-tracker-v2.json | 53 +++++++++++++ examples/README.md | 17 ++++ .../common/lora_interface/LoRaInterface.cpp | 79 +++++++++++++++++-- .../common/lora_interface/LoRaInterface.h | 2 +- .../boards/heltec-wireless-tracker-v2.json | 53 +++++++++++++ examples/lora_announce/platformio.ini | 19 +++++ .../boards/heltec-wireless-tracker-v2.json | 53 +++++++++++++ examples/lora_transport/platformio.ini | 19 +++++ platformio.ini | 16 +++- 10 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 boards/heltec-wireless-tracker-v2.json create mode 100644 examples/lora_announce/boards/heltec-wireless-tracker-v2.json create mode 100644 examples/lora_transport/boards/heltec-wireless-tracker-v2.json diff --git a/README.md b/README.md index c01f5b9e..48e4d10f 100644 --- a/README.md +++ b/README.md @@ -186,18 +186,21 @@ pio test -f test_msgpack -e native17 Build a single environment (board): ``` pio run -e ttgo-t-beam +pio run -e heltec-wireless-tracker-v2 pio run -e wiscore_rak4631 ``` Build and upload a single environment (board): ``` pio run -e ttgo-t-beam -t upload +pio run -e heltec-wireless-tracker-v2 -t upload pio run -e wiscore_rak4631 -t upload ``` Build and package a single environment (board): ``` pio run -e ttgo-t-beam -t package +pio run -e heltec-wireless-tracker-v2 -t package pio run -e wiscore_rak4631 -t package ``` diff --git a/boards/heltec-wireless-tracker-v2.json b/boards/heltec-wireless-tracker-v2.json new file mode 100644 index 00000000..6589e184 --- /dev/null +++ b/boards/heltec-wireless-tracker-v2.json @@ -0,0 +1,53 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_HELTEC_WIRELESS_TRACKER_V2", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi", + "bluetooth", + "lora" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Heltec Wireless Tracker V2", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://heltec.org/project/wireless-tracker-v2/", + "vendor": "Heltec" +} diff --git a/examples/README.md b/examples/README.md index 31b73b88..fc44e4bd 100644 --- a/examples/README.md +++ b/examples/README.md @@ -72,3 +72,20 @@ Build and upload to a connected board: ``` pio run -e ttgo-t-beam -t upload ``` + +### Heltec Wireless Tracker V2 + +The Tracker V2 environment uses the board's onboard SX1262 and KCT8103L RF +front end. Attach a suitable LoRa antenna before transmitting. The example's +radio frequency is currently set to 915 MHz in +`common/lora_interface/LoRaInterface.h`; change it there if your hardware and +local band plan require another frequency. + +From the repository root, build and upload the transport example with: + +``` +cd examples/lora_transport +pio run -e heltec-wireless-tracker-v2 +pio run -e heltec-wireless-tracker-v2 -t upload +pio device monitor -b 115200 +``` diff --git a/examples/common/lora_interface/LoRaInterface.cpp b/examples/common/lora_interface/LoRaInterface.cpp index efdbfab2..78f630e8 100644 --- a/examples/common/lora_interface/LoRaInterface.cpp +++ b/examples/common/lora_interface/LoRaInterface.cpp @@ -58,6 +58,21 @@ #define RADIO_FEM_CE 2 // LORA_PA_CSD: FEM chip enable (active HIGH) #define RADIO_PA_MODE 46 // LORA_PA_CPS: PA mode HIGH=TX, LOW=RX +#elif defined(BOARD_HELTEC_TRACKER_V2) +// Heltec Wireless Tracker V2.3 — ESP32-S3FN8 + SX1262 + KCT8103L FEM +// RadioLib Module(cs, irq=DIO1, rst, busy) +#define RADIO_SCLK_PIN 9 +#define RADIO_MISO_PIN 11 +#define RADIO_MOSI_PIN 10 +#define RADIO_CS_PIN 8 +#define RADIO_DIO1_PIN 14 // IRQ +#define RADIO_RST_PIN 12 +#define RADIO_BUSY_PIN 13 +// KCT8103L front-end controls; SX1262 DIO2 is wired to the FEM CPS input +#define RADIO_PA_POWER 7 // VFEM regulator enable (active HIGH) +#define RADIO_PA_CSD 4 // FEM chip enable (active HIGH) +#define RADIO_PA_CTX 5 // HIGH=TX, LOW=RX + #endif using namespace RNS; @@ -65,6 +80,29 @@ using namespace RNS; static inline bool isSplitPacket(uint8_t h) { return (h & LoRaInterface::HEADER_SPLIT) != 0; } static inline uint8_t packetSequence(uint8_t h) { return h & LoRaInterface::HEADER_SEQ_MASK; } +#if defined(BOARD_HELTEC_TRACKER_V2) +// The Tracker V2's KCT8103L adds substantial gain after the SX1262. Convert +// requested antenna output power to SX1262 power using Heltec's V2.3 table. +static int trackerV2RadioPower(int requestedPower) { + static const uint8_t paGain[] = { + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 7 + }; + + for (size_t radioPower = 0; radioPower < sizeof(paGain); ++radioPower) { + if ((int)radioPower + paGain[radioPower] > requestedPower || + radioPower == sizeof(paGain) - 1) { + int adjustedPower = requestedPower - paGain[radioPower]; + if (adjustedPower > 22) adjustedPower = 22; + if (adjustedPower < -9) adjustedPower = -9; + return adjustedPower; + } + } + + return -9; +} +#endif + /* @staticmethod def get_address_for_if(name): @@ -163,8 +201,31 @@ bool LoRaInterface::start() { int state = chip->begin(frequency, bandwidth, spreading, coding, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, power, 20, 1.8, false); +#elif defined(BOARD_HELTEC_TRACKER_V2) + // Heltec Wireless Tracker V2.3 — ESP32-S3FN8 + SX1262 + KCT8103L, 1.8V TCXO + SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN); + // Power the front end and select its receive path before starting the radio. + pinMode(RADIO_PA_POWER, OUTPUT); + pinMode(RADIO_PA_CSD, OUTPUT); + pinMode(RADIO_PA_CTX, OUTPUT); + digitalWrite(RADIO_PA_POWER, HIGH); + delay(1); + digitalWrite(RADIO_PA_CSD, HIGH); + delay(1); + digitalWrite(RADIO_PA_CTX, LOW); + delay(1); + _pa_mode_pin = RADIO_PA_CTX; + _module = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN, SPI); + SX1262* chip = new SX1262(_module); + _radio = chip; + // DIO2 drives the KCT8103L CPS input for antenna-path switching. + chip->setDio2AsRfSwitch(true); + int state = chip->begin(frequency, bandwidth, spreading, coding, + RADIOLIB_SX126X_SYNC_WORD_PRIVATE, + trackerV2RadioPower(power), 20, 1.8, false); + #else - #error "Unsupported board: define BOARD_TBEAM, BOARD_LORA32_V21, BOARD_RAK4631, BOARD_HELTEC_V3, or BOARD_HELTEC_V4" + #error "Unsupported board: define BOARD_TBEAM, BOARD_LORA32_V21, BOARD_RAK4631, BOARD_HELTEC_V3, BOARD_HELTEC_V4, or BOARD_HELTEC_TRACKER_V2" int state = RADIOLIB_ERR_UNKNOWN; #endif @@ -190,6 +251,10 @@ void LoRaInterface::stop() { if (_radio) { _radio->standby(); } +#if defined(BOARD_HELTEC_TRACKER_V2) + digitalWrite(RADIO_PA_CSD, LOW); + digitalWrite(RADIO_PA_POWER, LOW); +#endif #endif _online = false; @@ -262,10 +327,10 @@ void LoRaInterface::loop() { txBuf[0] = rand_nibble; memcpy(txBuf + 1, data.data(), data.size()); - // V4: switch FEM to TX mode before transmitting + // Boards with an external FEM: select TX mode before transmitting if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, HIGH); } int state = _radio->transmit(txBuf, 1 + data.size()); - // V4: return FEM to RX mode, then re-arm receive + // Return the FEM to RX mode, then re-arm receive if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, LOW); } if (state != RADIOLIB_ERR_NONE) { ERRORF("LoRaInterface: transmit failed, code %d", state); @@ -280,10 +345,10 @@ void LoRaInterface::loop() { txBuf[0] = split_hdr; memcpy(txBuf + 1, data.data(), LORA_MAX_PAYLOAD); - // V4: switch FEM to TX mode before transmitting + // Boards with an external FEM: select TX mode before transmitting if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, HIGH); } int state = _radio->transmit(txBuf, 1 + LORA_MAX_PAYLOAD); - // V4: return FEM to RX mode, then re-arm receive + // Return the FEM to RX mode, then re-arm receive if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, LOW); } if (state != RADIOLIB_ERR_NONE) { ERRORF("LoRaInterface: transmit part 1 failed, code %d", state); @@ -295,10 +360,10 @@ void LoRaInterface::loop() { txBuf[0] = split_hdr; memcpy(txBuf + 1, data.data() + LORA_MAX_PAYLOAD, remainder); - // V4: switch FEM to TX mode before transmitting + // Boards with an external FEM: select TX mode before transmitting if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, HIGH); } state = _radio->transmit(txBuf, 1 + remainder); - // V4: return FEM to RX mode, then re-arm receive + // Return the FEM to RX mode, then re-arm receive if (_pa_mode_pin >= 0) { digitalWrite(_pa_mode_pin, LOW); } if (state != RADIOLIB_ERR_NONE) { ERRORF("LoRaInterface: transmit part 2 failed, code %d", state); diff --git a/examples/common/lora_interface/LoRaInterface.h b/examples/common/lora_interface/LoRaInterface.h index fe1011d0..705524fb 100644 --- a/examples/common/lora_interface/LoRaInterface.h +++ b/examples/common/lora_interface/LoRaInterface.h @@ -57,7 +57,7 @@ class LoRaInterface : public RNS::InterfaceImpl { #ifdef ARDUINO Module* _module = nullptr; PhysicalLayer* _radio = nullptr; - int _pa_mode_pin = -1; // V4 FEM PA mode pin; -1 = not present + int _pa_mode_pin = -1; // External FEM TX/RX mode pin; -1 = not present #endif }; diff --git a/examples/lora_announce/boards/heltec-wireless-tracker-v2.json b/examples/lora_announce/boards/heltec-wireless-tracker-v2.json new file mode 100644 index 00000000..6589e184 --- /dev/null +++ b/examples/lora_announce/boards/heltec-wireless-tracker-v2.json @@ -0,0 +1,53 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_HELTEC_WIRELESS_TRACKER_V2", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi", + "bluetooth", + "lora" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Heltec Wireless Tracker V2", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://heltec.org/project/wireless-tracker-v2/", + "vendor": "Heltec" +} diff --git a/examples/lora_announce/platformio.ini b/examples/lora_announce/platformio.ini index bac2d7aa..227f299a 100644 --- a/examples/lora_announce/platformio.ini +++ b/examples/lora_announce/platformio.ini @@ -13,7 +13,11 @@ monitor_speed = 115200 upload_speed = 460800 build_type = debug build_unflags = + -std=gnu++11 + -fno-exceptions build_flags = + -std=gnu++17 + -fexceptions -Wall -Wno-missing-field-initializers -Wno-format @@ -91,6 +95,21 @@ lib_deps = ${env.lib_deps} monitor_filters = esp32_exception_decoder +[env:heltec-wireless-tracker-v2] +framework = arduino +platform = espressif32 +board = heltec-wireless-tracker-v2 +board_build.partitions = no_ota.csv +build_flags = + ${env.build_flags} + -Wextra + -DMCU_ESP32 + -DBOARD_HELTEC_TRACKER_V2 + -DMSGPACK_USE_BOOST=OFF +lib_deps = + ${env.lib_deps} +monitor_filters = esp32_exception_decoder + [env:wiscore_rak4631] framework = arduino platform = nordicnrf52 diff --git a/examples/lora_transport/boards/heltec-wireless-tracker-v2.json b/examples/lora_transport/boards/heltec-wireless-tracker-v2.json new file mode 100644 index 00000000..6589e184 --- /dev/null +++ b/examples/lora_transport/boards/heltec-wireless-tracker-v2.json @@ -0,0 +1,53 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_HELTEC_WIRELESS_TRACKER_V2", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi", + "bluetooth", + "lora" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Heltec Wireless Tracker V2", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://heltec.org/project/wireless-tracker-v2/", + "vendor": "Heltec" +} diff --git a/examples/lora_transport/platformio.ini b/examples/lora_transport/platformio.ini index 5fdcdd46..952c98e6 100644 --- a/examples/lora_transport/platformio.ini +++ b/examples/lora_transport/platformio.ini @@ -13,7 +13,11 @@ monitor_speed = 115200 upload_speed = 460800 build_type = debug build_unflags = + -std=gnu++11 + -fno-exceptions build_flags = + -std=gnu++17 + -fexceptions -Wall -Wno-missing-field-initializers -Wno-format @@ -91,6 +95,21 @@ lib_deps = ${env.lib_deps} monitor_filters = esp32_exception_decoder +[env:heltec-wireless-tracker-v2] +framework = arduino +platform = espressif32 +board = heltec-wireless-tracker-v2 +board_build.partitions = no_ota.csv +build_flags = + ${env.build_flags} + -Wextra + -DMCU_ESP32 + -DBOARD_HELTEC_TRACKER_V2 + -DMSGPACK_USE_BOOST=OFF +lib_deps = + ${env.lib_deps} +monitor_filters = esp32_exception_decoder + [env:wiscore_rak4631] framework = arduino platform = nordicnrf52 diff --git a/platformio.ini b/platformio.ini index a0541743..c7022ba3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,7 @@ default_envs = ttgo-t-beam lilygo_tbeam_supreme heltec_wifi_lora_32_V4 + heltec-wireless-tracker-v2 wiscore_rak4631 [env] @@ -227,7 +228,7 @@ lib_deps = ${env:embedded.lib_deps} monitor_filters = esp32_exception_decoder -; also Heltec Wireless Tracker +; Heltec WiFi LoRa 32 V4 [env:heltec_wifi_lora_32_V4] extends = env:embedded platform = espressif32 @@ -251,6 +252,19 @@ lib_deps = monitor_filters = esp32_exception_decoder upload_speed = 921600 +[env:heltec-wireless-tracker-v2] +extends = env:embedded +platform = espressif32 +board = heltec-wireless-tracker-v2 +board_build.partitions = no_ota.csv +build_flags = + ${env:embedded.build_flags} + -DBOARD_ESP32 + -DMSGPACK_USE_BOOST=OFF +lib_deps = + ${env:embedded.lib_deps} +monitor_filters = esp32_exception_decoder + [env:wiscore_rak4631] extends = env:embedded platform = nordicnrf52