From 2e2e677b0a51dd0e5a36f65619ebceb5ce37a4f9 Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Wed, 27 Aug 2025 00:37:16 -0700 Subject: [PATCH 001/115] Ikoka Stick: Board IDs, LED behavior Updates the manufacturer identifier with the EBYTE module. Makes the LED behave properly. Turns the bright blue LED off after the first time you transmit anything via LoRa. --- variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h | 10 +++++++++- variants/ikoka_stick_nrf/platformio.ini | 3 +++ variants/ikoka_stick_nrf/variant.h | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h index c66f4827..08061c23 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h +++ b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h @@ -16,9 +16,17 @@ public: #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + #if defined(LED_BLUE) + // turn off that annoying blue LED before transmitting + digitalWrite(LED_BLUE, HIGH); + #endif } void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + #if defined(LED_BLUE) + // do it after transmitting too, just in case + digitalWrite(LED_BLUE, HIGH); + #endif } #endif @@ -39,7 +47,7 @@ public: } const char* getManufacturerName() const override { - return "Ikoka Stick (Xiao-nrf52)"; + return MANUFACTURER_STRING; } void reboot() override { diff --git a/variants/ikoka_stick_nrf/platformio.ini b/variants/ikoka_stick_nrf/platformio.ini index 6e6ae101..16bc1253 100644 --- a/variants/ikoka_stick_nrf/platformio.ini +++ b/variants/ikoka_stick_nrf/platformio.ini @@ -61,6 +61,7 @@ extends = ikoka_stick_nrf_baseboard ; No PA in this model, full 22dBm build_flags = ${ikoka_stick_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Stick-E22-22dBm (Xiao_nrf52)"' -D LORA_TX_POWER=22 build_src_filter = ${nrf52840_xiao.build_src_filter} + @@ -75,6 +76,7 @@ extends = ikoka_stick_nrf_baseboard ; cause distortion in the PA output. 20dBm in -> 30dBm out build_flags = ${ikoka_stick_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Stick-E22-30dBm (Xiao_nrf52)"' -D LORA_TX_POWER=20 build_src_filter = ${nrf52840_xiao.build_src_filter} + @@ -89,6 +91,7 @@ extends = ikoka_stick_nrf_baseboard ; to the rf amplifier frontend. 9dBm in -> 33dBm out build_flags = ${ikoka_stick_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Stick-E22-33dBm (Xiao_nrf52)"' -D LORA_TX_POWER=9 build_src_filter = ${nrf52840_xiao.build_src_filter} + diff --git a/variants/ikoka_stick_nrf/variant.h b/variants/ikoka_stick_nrf/variant.h index f94ebe49..ff5a41a6 100644 --- a/variants/ikoka_stick_nrf/variant.h +++ b/variants/ikoka_stick_nrf/variant.h @@ -35,7 +35,7 @@ extern "C" #define LED_GREEN (13) #define LED_BLUE (12) -#define LED_STATE_ON (1) // State when LED is litted +#define LED_STATE_ON (0) // State when LED is litted // Buttons #define PIN_BUTTON1 (PINS_COUNT) From 7b08acf56d820d9811bab51cfa5597f99c3a1774 Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Sun, 7 Sep 2025 21:29:10 -0700 Subject: [PATCH 002/115] Ikoka Stick: Move to unified code naming conventions --- ...stick_nrf_board.cpp => IkokaStickNRFBoard.cpp} | 15 +++++---------- ...oka_stick_nrf_board.h => IkokaStickNRFBoard.h} | 6 +++--- variants/ikoka_stick_nrf/target.cpp | 2 +- variants/ikoka_stick_nrf/target.h | 4 ++-- 4 files changed, 11 insertions(+), 16 deletions(-) rename variants/ikoka_stick_nrf/{ikoka_stick_nrf_board.cpp => IkokaStickNRFBoard.cpp} (91%) rename variants/ikoka_stick_nrf/{ikoka_stick_nrf_board.h => IkokaStickNRFBoard.h} (89%) diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp similarity index 91% rename from variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp rename to variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp index 8634cda1..6b660383 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp +++ b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp @@ -1,28 +1,26 @@ #ifdef XIAO_NRF52 #include -#include "ikoka_stick_nrf_board.h" +#include "IkokaStickNRFBoard.h" #include #include static BLEDfu bledfu; -static void connect_callback(uint16_t conn_handle) -{ +static void connect_callback(uint16_t conn_handle) { (void)conn_handle; MESH_DEBUG_PRINTLN("BLE client connected"); } -static void disconnect_callback(uint16_t conn_handle, uint8_t reason) -{ +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { (void)conn_handle; (void)reason; MESH_DEBUG_PRINTLN("BLE client disconnected"); } -void ikoka_stick_nrf_board::begin() { +void IkokaStickNRFBoard::begin() { // for future use, sub-classes SHOULD call this from their begin() startup_reason = BD_STARTUP_NORMAL; @@ -50,7 +48,7 @@ void ikoka_stick_nrf_board::begin() { delay(10); // give sx1262 some time to power up } -bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) { +bool IkokaStickNRFBoard::startOTAUpdate(const char *id, char reply[]) { // Config the peripheral connection with maximum bandwidth // more SRAM required by SoftDevice // Note: All config***() function must be called before begin() @@ -91,9 +89,6 @@ bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) { strcpy(reply, "OK - started"); return true; - - - return false; } #endif diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h similarity index 89% rename from variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h rename to variants/ikoka_stick_nrf/IkokaStickNRFBoard.h index 08061c23..4a061d42 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h +++ b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h @@ -5,7 +5,7 @@ #ifdef XIAO_NRF52 -class ikoka_stick_nrf_board : public mesh::MainBoard { +class IkokaStickNRFBoard : public mesh::MainBoard { protected: uint8_t startup_reason; @@ -46,7 +46,7 @@ public: return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096; } - const char* getManufacturerName() const override { + const char *getManufacturerName() const override { return MANUFACTURER_STRING; } @@ -54,7 +54,7 @@ public: NVIC_SystemReset(); } - bool startOTAUpdate(const char* id, char reply[]) override; + bool startOTAUpdate(const char *id, char reply[]) override; }; #endif diff --git a/variants/ikoka_stick_nrf/target.cpp b/variants/ikoka_stick_nrf/target.cpp index c2712761..bd803399 100644 --- a/variants/ikoka_stick_nrf/target.cpp +++ b/variants/ikoka_stick_nrf/target.cpp @@ -2,7 +2,7 @@ #include "target.h" #include -ikoka_stick_nrf_board board; +IkokaStickNRFBoard board; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; diff --git a/variants/ikoka_stick_nrf/target.h b/variants/ikoka_stick_nrf/target.h index 8311503a..c276e89f 100644 --- a/variants/ikoka_stick_nrf/target.h +++ b/variants/ikoka_stick_nrf/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include @@ -16,7 +16,7 @@ extern MomentaryButton user_btn; #endif -extern ikoka_stick_nrf_board board; +extern IkokaStickNRFBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; From 9d009074daf9fec303f743180a173a4e45052a7a Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Sun, 7 Sep 2025 21:29:10 -0700 Subject: [PATCH 003/115] Ikoka Stick: Move to unified code naming conventions --- ...stick_nrf_board.cpp => IkokaStickNRFBoard.cpp} | 15 +++++---------- ...oka_stick_nrf_board.h => IkokaStickNRFBoard.h} | 6 +++--- variants/ikoka_stick_nrf/target.cpp | 2 +- variants/ikoka_stick_nrf/target.h | 4 ++-- 4 files changed, 11 insertions(+), 16 deletions(-) rename variants/ikoka_stick_nrf/{ikoka_stick_nrf_board.cpp => IkokaStickNRFBoard.cpp} (91%) rename variants/ikoka_stick_nrf/{ikoka_stick_nrf_board.h => IkokaStickNRFBoard.h} (89%) diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp similarity index 91% rename from variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp rename to variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp index 8634cda1..6b660383 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp +++ b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.cpp @@ -1,28 +1,26 @@ #ifdef XIAO_NRF52 #include -#include "ikoka_stick_nrf_board.h" +#include "IkokaStickNRFBoard.h" #include #include static BLEDfu bledfu; -static void connect_callback(uint16_t conn_handle) -{ +static void connect_callback(uint16_t conn_handle) { (void)conn_handle; MESH_DEBUG_PRINTLN("BLE client connected"); } -static void disconnect_callback(uint16_t conn_handle, uint8_t reason) -{ +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { (void)conn_handle; (void)reason; MESH_DEBUG_PRINTLN("BLE client disconnected"); } -void ikoka_stick_nrf_board::begin() { +void IkokaStickNRFBoard::begin() { // for future use, sub-classes SHOULD call this from their begin() startup_reason = BD_STARTUP_NORMAL; @@ -50,7 +48,7 @@ void ikoka_stick_nrf_board::begin() { delay(10); // give sx1262 some time to power up } -bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) { +bool IkokaStickNRFBoard::startOTAUpdate(const char *id, char reply[]) { // Config the peripheral connection with maximum bandwidth // more SRAM required by SoftDevice // Note: All config***() function must be called before begin() @@ -91,9 +89,6 @@ bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) { strcpy(reply, "OK - started"); return true; - - - return false; } #endif diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h similarity index 89% rename from variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h rename to variants/ikoka_stick_nrf/IkokaStickNRFBoard.h index 08061c23..4a061d42 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h +++ b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h @@ -5,7 +5,7 @@ #ifdef XIAO_NRF52 -class ikoka_stick_nrf_board : public mesh::MainBoard { +class IkokaStickNRFBoard : public mesh::MainBoard { protected: uint8_t startup_reason; @@ -46,7 +46,7 @@ public: return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096; } - const char* getManufacturerName() const override { + const char *getManufacturerName() const override { return MANUFACTURER_STRING; } @@ -54,7 +54,7 @@ public: NVIC_SystemReset(); } - bool startOTAUpdate(const char* id, char reply[]) override; + bool startOTAUpdate(const char *id, char reply[]) override; }; #endif diff --git a/variants/ikoka_stick_nrf/target.cpp b/variants/ikoka_stick_nrf/target.cpp index c2712761..bd803399 100644 --- a/variants/ikoka_stick_nrf/target.cpp +++ b/variants/ikoka_stick_nrf/target.cpp @@ -2,7 +2,7 @@ #include "target.h" #include -ikoka_stick_nrf_board board; +IkokaStickNRFBoard board; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; diff --git a/variants/ikoka_stick_nrf/target.h b/variants/ikoka_stick_nrf/target.h index 8311503a..c276e89f 100644 --- a/variants/ikoka_stick_nrf/target.h +++ b/variants/ikoka_stick_nrf/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include @@ -16,7 +16,7 @@ extern MomentaryButton user_btn; #endif -extern ikoka_stick_nrf_board board; +extern IkokaStickNRFBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; From 76aa7cf488e8d4195d1ec88b4497da0e3fdc79d0 Mon Sep 17 00:00:00 2001 From: Florent Date: Tue, 23 Sep 2025 10:39:43 +0200 Subject: [PATCH 004/115] ui_task: initial gps page --- examples/companion_radio/ui-new/UITask.cpp | 62 +++++++++++++++++++ examples/companion_radio/ui-new/UITask.h | 1 + src/helpers/SensorManager.h | 2 + .../sensors/EnvironmentSensorManager.h | 1 + src/helpers/sensors/LocationProvider.h | 8 +-- variants/lilygo_techo/platformio.ini | 2 + variants/t1000-e/target.h | 1 + variants/wio-tracker-l1-eink/platformio.ini | 1 + 8 files changed, 74 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 248b9bd5..c48c6972 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -75,6 +75,9 @@ class HomeScreen : public UIScreen { RADIO, BLUETOOTH, ADVERT, +#if UI_GPS_PAGE == 1 + GPS, +#endif #if UI_SENSORS_PAGE == 1 SENSORS, #endif @@ -250,6 +253,47 @@ public: display.setColor(DisplayDriver::GREEN); display.drawXbm((display.width() - 32) / 2, 18, advert_icon, 32, 32); display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL); +#if UI_GPS_PAGE == 1 + } else if (_page == HomePage::GPS) { + LocationProvider* nmea = sensors.getLocationProvider(); + int y = 18; + display.setCursor(0, y); + display.print(_task->getGPSState() ? "gps on" : "gps off"); + if (nmea == NULL) { + y = y + 12; + display.setCursor(0, y); + display.print("Can't access GPS"); + } else { + char buf[50]; + strcpy(buf, nmea->isValid()?"fix":"no fix"); + display.setCursor( + display.width()-display.getTextWidth(buf)-1, y); + display.print(buf); + y = y + 12; + display.setCursor(0,y); + display.print("sat"); + sprintf(buf, "%d", nmea->satellitesCount()); + display.setCursor( + display.width()-display.getTextWidth(buf)-1, y); + display.print(buf); + y = y + 12; + display.setCursor(0,y); + display.print("pos"); + sprintf(buf, "%.4f %.4f", + nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.); + display.setCursor( + display.width()-display.getTextWidth(buf)-1, y); + display.print(buf); + y = y + 12; + display.setCursor(0,y); + display.print("alt"); + sprintf(buf, "%.2f", nmea->getAltitude()/1000.); + display.setCursor( + display.width()-display.getTextWidth(buf)-1, y); + display.print(buf); + y = y + 12; + } +#endif #if UI_SENSORS_PAGE == 1 } else if (_page == HomePage::SENSORS) { int y = 18; @@ -364,6 +408,12 @@ public: } return true; } +#if UI_GPS_PAGE == 1 + if (c == KEY_ENTER && _page == HomePage::GPS) { + _task->toggleGPS(); + return true; + } +#endif #if UI_SENSORS_PAGE == 1 if (c == KEY_ENTER && _page == HomePage::SENSORS) { _task->toggleGPS(); @@ -773,6 +823,18 @@ char UITask::handleTripleClick(char c) { return c; } +bool UITask::getGPSState() { + if (_sensors != NULL) { + int num = _sensors->getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(_sensors->getSettingName(i), "gps") == 0) { + return !strcmp(_sensors->getSettingValue(i), "1"); + } + } + } + return false; +} + void UITask::toggleGPS() { if (_sensors != NULL) { // toggle GPS on/off diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 5a087eeb..c24d33a4 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -71,6 +71,7 @@ public: bool isButtonPressed() const; void toggleBuzzer(); + bool getGPSState(); void toggleGPS(); diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 0e4bc27d..1ace6220 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include "sensors/LocationProvider.h" #define TELEM_PERM_BASE 0x01 // 'base' permission includes battery #define TELEM_PERM_LOCATION 0x02 @@ -21,4 +22,5 @@ public: virtual const char* getSettingName(int i) const { return NULL; } virtual const char* getSettingValue(int i) const { return NULL; } virtual bool setSettingValue(const char* name, const char* value) { return false; } + virtual LocationProvider* getLocationProvider() { return NULL; } }; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 3302d6f6..09c6cae4 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -39,6 +39,7 @@ protected: public: #if ENV_INCLUDE_GPS EnvironmentSensorManager(LocationProvider &location): _location(&location){}; + LocationProvider* getLocationProvider() { return _location; } #else EnvironmentSensorManager(){}; #endif diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index f51eea28..f93dec48 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -17,8 +17,8 @@ public: virtual bool isValid() = 0; virtual long getTimestamp() = 0; virtual void sendSentence(const char * sentence); - virtual void reset(); - virtual void begin(); - virtual void stop(); - virtual void loop(); + virtual void reset() = 0; + virtual void begin() = 0; + virtual void stop() = 0; + virtual void loop() = 0; }; diff --git a/variants/lilygo_techo/platformio.ini b/variants/lilygo_techo/platformio.ini index e814ea54..b5e94b2d 100644 --- a/variants/lilygo_techo/platformio.ini +++ b/variants/lilygo_techo/platformio.ini @@ -29,6 +29,7 @@ build_flags = ${nrf52_base.build_flags} -D ENV_INCLUDE_BME280=1 -D GPS_BAUD_RATE=9600 -D PIN_GPS_EN=GPS_EN + -D PIN_GPS_RESET_ACTIVE=LOW -D TELEM_BME280_ADDRESS=0x77 -D DISPLAY_CLASS=GxEPDDisplay -D BACKLIGHT_BTN=PIN_BUTTON2 @@ -92,6 +93,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D UI_RECENT_LIST_SIZE=9 -D UI_SENSORS_PAGE=1 + -D UI_GPS_PAGE=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -D AUTO_SHUTDOWN_MILLIVOLTS=3300 diff --git a/variants/t1000-e/target.h b/variants/t1000-e/target.h index 6ac0d3a6..27351b94 100644 --- a/variants/t1000-e/target.h +++ b/variants/t1000-e/target.h @@ -28,6 +28,7 @@ public: const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + LocationProvider* getLocationProvider() { return _nmea; } }; #ifdef DISPLAY_CLASS diff --git a/variants/wio-tracker-l1-eink/platformio.ini b/variants/wio-tracker-l1-eink/platformio.ini index a41e9e23..db22c01b 100644 --- a/variants/wio-tracker-l1-eink/platformio.ini +++ b/variants/wio-tracker-l1-eink/platformio.ini @@ -55,6 +55,7 @@ build_flags = ${WioTrackerL1Eink.build_flags} ; -D MESH_DEBUG=1 -D UI_RECENT_LIST_SIZE=6 -D UI_SENSORS_PAGE=1 + -D UI_GPS_PAGE=1 build_src_filter = ${WioTrackerL1Eink.build_src_filter} + + From 1d45c7ec66b5f52d9b4037b530f7c99ffd687d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Wed, 24 Sep 2025 16:30:00 +0100 Subject: [PATCH 005/115] Add bridge management CLI --- examples/simple_repeater/MyMesh.cpp | 11 ++- examples/simple_repeater/MyMesh.h | 37 ++++--- src/helpers/AbstractBridge.h | 16 ++- src/helpers/CommonCLI.cpp | 143 +++++++++++++++++---------- src/helpers/CommonCLI.h | 57 ++++++----- src/helpers/bridges/BridgeBase.cpp | 11 +++ src/helpers/bridges/BridgeBase.h | 10 ++ src/helpers/bridges/ESPNowBridge.cpp | 53 ++++++++-- src/helpers/bridges/ESPNowBridge.h | 30 ++++++ src/helpers/bridges/RS232Bridge.cpp | 103 +++++++++++-------- src/helpers/bridges/RS232Bridge.h | 6 ++ 11 files changed, 340 insertions(+), 137 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4265e1cd..4806c28a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -513,6 +513,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + _prefs.bridge_enabled = 1; // enabled + _prefs.bridge_channel = 0; // auto } void MyMesh::begin(FILESYSTEM *fs) { @@ -523,8 +525,13 @@ void MyMesh::begin(FILESYSTEM *fs) { acl.load(_fs); -#ifdef WITH_BRIDGE - bridge.begin(); +#if defined(WITH_ESPNOW_BRIDGE) + bridge.setChannel(_prefs.bridge_channel); +#endif +#if defined(WITH_BRIDGE) + if (_prefs.bridge_enabled) { + bridge.begin(); + } #endif radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 798d31f2..7ae10812 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -2,7 +2,8 @@ #include #include -#include +#include +#include #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include @@ -12,16 +13,6 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - #ifdef WITH_RS232_BRIDGE #include "helpers/bridges/RS232Bridge.h" #define WITH_BRIDGE @@ -32,6 +23,15 @@ #define WITH_BRIDGE #endif +#include +#include +#include +#include +#include +#include +#include +#include + #ifdef WITH_BRIDGE extern AbstractBridge* bridge; #endif @@ -165,6 +165,21 @@ public: void updateAdvertTimer() override; void updateFloodAdvertTimer() override; +#if defined(WITH_ESPNOW_BRIDGE) + void setBridgeState(bool enable) { + if (enable == bridge.getState()) return; + enable ? bridge.begin() : bridge.end(); + } + + void updateBridgeChannel(int ch) override { + bridge.setChannel(ch); + if (bridge.getState()) { + bridge.end(); + bridge.begin(); + } + } +#endif + void setLoggingOn(bool enable) override { _logging = enable; } void eraseLogFile() override { diff --git a/src/helpers/AbstractBridge.h b/src/helpers/AbstractBridge.h index a348e933..73a967d8 100644 --- a/src/helpers/AbstractBridge.h +++ b/src/helpers/AbstractBridge.h @@ -11,6 +11,18 @@ public: */ virtual void begin() = 0; + /** + * @brief Stops the bridge. + */ + virtual void end() = 0; + + /** + * @brief Gets the current state of the bridge. + * + * @return true if the bridge is initialized and running, false otherwise. + */ + virtual bool getState() const = 0; + /** * @brief A method to be called on every main loop iteration. * Used for tasks like checking for incoming data. @@ -20,14 +32,14 @@ public: /** * @brief A callback that is triggered when the mesh transmits a packet. * The bridge can use this to forward the packet. - * + * * @param packet The packet that was transmitted. */ virtual void onPacketTransmitted(mesh::Packet* packet) = 0; /** * @brief Processes a received packet from the bridge's medium. - * + * * @param packet The packet that was received. */ virtual void onPacketReceived(mesh::Packet* packet) = 0; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 7125e5b0..97c7eada 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -32,32 +32,34 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (file) { uint8_t pad[8]; - file.read((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0 - file.read((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4 - file.read(pad, 4); // 36 - file.read((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40 - file.read((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48 - file.read((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56 - file.read((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72 - file.read((uint8_t *) &_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 - file.read((uint8_t *) &_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 - file.read((uint8_t *) &_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 - file.read((uint8_t *) pad, 1); // 79 was 'unused' - file.read((uint8_t *) &_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 - file.read((uint8_t *) &_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 - file.read((uint8_t *) &_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 - file.read((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.read(pad, 4); // 108 - file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 - file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 - file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.read((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 - file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 - file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 - file.read(pad, 3); // 121 - file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 - file.read((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 - file.read((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 + file.read((uint8_t *)&_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0 + file.read((uint8_t *)&_prefs->node_name, sizeof(_prefs->node_name)); // 4 + file.read(pad, 4); // 36 + file.read((uint8_t *)&_prefs->node_lat, sizeof(_prefs->node_lat)); // 40 + file.read((uint8_t *)&_prefs->node_lon, sizeof(_prefs->node_lon)); // 48 + file.read((uint8_t *)&_prefs->password[0], sizeof(_prefs->password)); // 56 + file.read((uint8_t *)&_prefs->freq, sizeof(_prefs->freq)); // 72 + file.read((uint8_t *)&_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 + file.read((uint8_t *)&_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 + file.read((uint8_t *)&_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 + file.read((uint8_t *)pad, 1); // 79 was 'unused' + file.read((uint8_t *)&_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 + file.read((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 + file.read((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 + file.read((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 + file.read(pad, 4); // 108 + file.read((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 + file.read((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 + file.read((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 + file.read((uint8_t *)&_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 + file.read((uint8_t *)&_prefs->bw, sizeof(_prefs->bw)); // 116 + file.read((uint8_t *)&_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 + file.read(pad, 3); // 121 + file.read((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 + file.read((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 + file.read((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 + file.read((uint8_t *)&_prefs->bridge_enabled, sizeof(_prefs->bridge_enabled)); // 127 + file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 128 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -70,6 +72,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); + _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); + _prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14); file.close(); } @@ -88,32 +92,34 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { uint8_t pad[8]; memset(pad, 0, sizeof(pad)); - file.write((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0 - file.write((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4 - file.write(pad, 4); // 36 - file.write((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40 - file.write((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48 - file.write((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56 - file.write((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72 - file.write((uint8_t *) &_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 - file.write((uint8_t *) &_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 - file.write((uint8_t *) &_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 - file.write((uint8_t *) pad, 1); // 79 was 'unused' - file.write((uint8_t *) &_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 - file.write((uint8_t *) &_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 - file.write((uint8_t *) &_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 - file.write((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.write(pad, 4); // 108 - file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 - file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 - file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.write((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 - file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 - file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 - file.write(pad, 3); // 121 - file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 - file.write((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 - file.write((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 + file.write((uint8_t *)&_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0 + file.write((uint8_t *)&_prefs->node_name, sizeof(_prefs->node_name)); // 4 + file.write(pad, 4); // 36 + file.write((uint8_t *)&_prefs->node_lat, sizeof(_prefs->node_lat)); // 40 + file.write((uint8_t *)&_prefs->node_lon, sizeof(_prefs->node_lon)); // 48 + file.write((uint8_t *)&_prefs->password[0], sizeof(_prefs->password)); // 56 + file.write((uint8_t *)&_prefs->freq, sizeof(_prefs->freq)); // 72 + file.write((uint8_t *)&_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 + file.write((uint8_t *)&_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 + file.write((uint8_t *)&_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 + file.write((uint8_t *)pad, 1); // 79 was 'unused' + file.write((uint8_t *)&_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 + file.write((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 + file.write((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 + file.write((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 + file.write(pad, 4); // 108 + file.write((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 + file.write((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 + file.write((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 + file.write((uint8_t *)&_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 + file.write((uint8_t *)&_prefs->bw, sizeof(_prefs->bw)); // 116 + file.write((uint8_t *)&_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 + file.write(pad, 3); // 121 + file.write((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 + file.write((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 + file.write((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 + file.write((uint8_t *)&_prefs->bridge_enabled, sizeof(_prefs->bridge_enabled)); // 127 + file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 128 file.close(); } @@ -252,11 +258,41 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch mesh::Utils::toHex(&reply[2], _callbacks->getSelfId().pub_key, PUB_KEY_SIZE); } else if (memcmp(config, "role", 4) == 0) { sprintf(reply, "> %s", _callbacks->getRole()); +#ifdef WITH_BRIDGE + } else if (memcmp(config, "bridge.enabled", 14) == 0) { + sprintf(reply, "> %s", _prefs->bridge_enabled ? "on" : "off"); +#ifdef WITH_ESPNOW_BRIDGE + } else if (memcmp(config, "bridge.channel", 14) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->bridge_channel); +#endif +#endif } else { sprintf(reply, "??: %s", config); } } else if (memcmp(command, "set ", 4) == 0) { const char* config = &command[4]; +#ifdef WITH_BRIDGE + if (memcmp(config, "bridge.enabled ", 15) == 0) { + _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; + _callbacks->setBridgeState(_prefs->bridge_enabled); + savePrefs(); + strcpy(reply, "OK"); + } + else +#ifdef WITH_ESPNOW_BRIDGE + if (memcmp(config, "bridge.channel ", 15) == 0) { + int ch = atoi(&config[15]); + if (ch > 0 && ch < 15) { + _prefs->bridge_channel = (uint8_t)ch; + _callbacks->updateBridgeChannel(ch); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: channel must be 0 (AUTO) or 1-14"); + } + } else +#endif +#endif if (memcmp(config, "af ", 3) == 0) { _prefs->airtime_factor = atof(&config[3]); savePrefs(); @@ -301,7 +337,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password)); savePrefs(); strcpy(reply, "OK"); - } else if (sender_timestamp == 0 && memcmp(config, "prv.key ", 8) == 0) { // from serial command line only + } else if (sender_timestamp == 0 && + memcmp(config, "prv.key ", 8) == 0) { // from serial command line only uint8_t prv_key[PRV_KEY_SIZE]; bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]); if (success) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ff8ff50e..55751d45 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -3,29 +3,35 @@ #include "Mesh.h" #include -struct NodePrefs { // persisted to file - float airtime_factor; - char node_name[32]; - double node_lat, node_lon; - char password[16]; - float freq; - uint8_t tx_power_dbm; - uint8_t disable_fwd; - uint8_t advert_interval; // minutes / 2 - uint8_t flood_advert_interval; // hours - float rx_delay_base; - float tx_delay_factor; - char guest_password[16]; - float direct_tx_delay_factor; - uint32_t guard; - uint8_t sf; - uint8_t cr; - uint8_t allow_read_only; - uint8_t multi_acks; - float bw; - uint8_t flood_max; - uint8_t interference_threshold; - uint8_t agc_reset_interval; // secs / 4 +#if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE) +#define WITH_BRIDGE +#endif + +struct NodePrefs { // persisted to file + float airtime_factor; + char node_name[32]; + double node_lat, node_lon; + char password[16]; + float freq; + uint8_t tx_power_dbm; + uint8_t disable_fwd; + uint8_t advert_interval; // minutes / 2 + uint8_t flood_advert_interval; // hours + float rx_delay_base; + float tx_delay_factor; + char guest_password[16]; + float direct_tx_delay_factor; + uint32_t guard; + uint8_t sf; + uint8_t cr; + uint8_t allow_read_only; + uint8_t multi_acks; + float bw; + uint8_t flood_max; + uint8_t interference_threshold; + uint8_t agc_reset_interval; // secs / 4 + uint8_t bridge_enabled; // boolean + uint8_t bridge_channel; // 0 = AUTO, 1-14 valid }; class CommonCLICallbacks { @@ -50,6 +56,11 @@ public: virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; + +#ifdef WITH_ESPNOW_BRIDGE + virtual void setBridgeState(bool enable) = 0; + virtual void updateBridgeChannel(int ch) = 0; +#endif }; class CommonCLI { diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp index 03871418..9434b23b 100644 --- a/src/helpers/bridges/BridgeBase.cpp +++ b/src/helpers/bridges/BridgeBase.cpp @@ -2,6 +2,10 @@ #include +bool BridgeBase::getState() const { + return _initialized; +} + const char *BridgeBase::getLogDateTime() { static char tmp[32]; uint32_t now = _rtc->getCurrentTime(); @@ -28,6 +32,13 @@ bool BridgeBase::validateChecksum(const uint8_t *data, size_t len, uint16_t rece } void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { + // Guard against uninitialized state + if (_initialized == false) { + Serial.printf("%s: BRIDGE: RX packet received before initialization\n", getLogDateTime()); + _mgr->free(packet); + return; + } + if (!_seen_packets.hasSeen(packet)) { _mgr->queueInbound(packet, millis() + BRIDGE_DELAY); } else { diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index ab62619f..a3b1c85e 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -21,6 +21,13 @@ class BridgeBase : public AbstractBridge { public: virtual ~BridgeBase() = default; + /** + * @brief Gets the current state of the bridge. + * + * @return true if the bridge is initialized and running, false otherwise. + */ + bool getState() const override; + /** * @brief Common magic number used by all bridge implementations for packet identification * @@ -50,6 +57,9 @@ public: static constexpr uint16_t BRIDGE_DELAY = 500; // TODO: maybe too high ? protected: + /** Tracks bridge state */ + bool _initialized = false; + /** Packet manager for allocating and queuing mesh packets */ mesh::PacketManager *_mgr; diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index a02f804e..c508c5a0 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -27,8 +27,16 @@ ESPNowBridge::ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc) } void ESPNowBridge::begin() { + Serial.printf("%s: ESPNOW BRIDGE: Initializing...\n", getLogDateTime()); + // Initialize WiFi in station mode WiFi.mode(WIFI_STA); + + // Set wifi channel + if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Error setting WIFI channel to %d\n", getLogDateTime(), _channel); + return; + } // Initialize ESP-NOW if (esp_now_init() != ESP_OK) { @@ -44,13 +52,41 @@ void ESPNowBridge::begin() { esp_now_peer_info_t peerInfo = {}; memset(&peerInfo, 0, sizeof(peerInfo)); memset(peerInfo.peer_addr, 0xFF, ESP_NOW_ETH_ALEN); // Broadcast address - peerInfo.channel = 0; + peerInfo.channel = _channel; peerInfo.encrypt = false; if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.printf("%s: ESPNOW BRIDGE: Failed to add broadcast peer\n", getLogDateTime()); return; } + + // Update bridge state + _initialized = true; +} + +void ESPNowBridge::end() { + Serial.printf("%s: ESPNOW BRIDGE: Stopping...\n", getLogDateTime()); + + // Remove broadcast peer + uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (esp_now_del_peer(broadcastAddress) != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Error removing broadcast peer\n", getLogDateTime()); + } + + // Unregister callbacks + esp_now_register_recv_cb(nullptr); + esp_now_register_send_cb(nullptr); + + // Deinitialize ESP-NOW + if (esp_now_deinit() != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Error deinitializing ESP-NOW\n", getLogDateTime()); + } + + // Turn off WiFi + WiFi.mode(WIFI_OFF); + + // Update bridge state + _initialized = false; } void ESPNowBridge::loop() { @@ -130,11 +166,13 @@ void ESPNowBridge::onDataSent(const uint8_t *mac_addr, esp_now_send_status_t sta // Could add transmission error handling here if needed } -void ESPNowBridge::onPacketReceived(mesh::Packet *packet) { - handleReceivedPacket(packet); -} - void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { + // Guard against uninitialized state + if (_initialized == false) { + Serial.printf("%s: ESPNOW BRIDGE: TX packet attempted before initialization\n", getLogDateTime()); + return; + } + // First validate the packet pointer if (!packet) { #if MESH_PACKET_LOGGING @@ -144,7 +182,6 @@ void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { } if (!_seen_packets.hasSeen(packet)) { - // Create a temporary buffer just for size calculation and reuse for actual writing uint8_t sizingBuffer[MAX_PAYLOAD_SIZE]; uint16_t meshPacketLen = packet->writeTo(sizingBuffer); @@ -193,4 +230,8 @@ void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { } } +void ESPNowBridge::onPacketReceived(mesh::Packet *packet) { + handleReceivedPacket(packet); +} + #endif diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index b43f1744..401c9eee 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -80,6 +80,12 @@ private: */ const char *_secret = WITH_ESPNOW_BRIDGE_SECRET; + /** + * Channel for ESP-NOW communication + * Valid 2.4GHz channels: 1-14 + */ + int _channel = 0; + /** * Performs XOR encryption/decryption of data * @@ -130,6 +136,16 @@ public: */ void begin() override; + /** + * Stops the ESP-NOW bridge + * + * - Removes broadcast peer + * - Unregisters callbacks + * - Deinitializes ESP-NOW protocol + * - Turns off WiFi to release radio resources + */ + void end() override; + /** * Main loop handler * ESP-NOW is callback-based, so this is currently empty @@ -151,6 +167,20 @@ public: * @param packet The mesh packet to transmit */ void onPacketTransmitted(mesh::Packet *packet) override; + + /** + * Gets the current channel + * + * @return The current channel (0 = AUTO, 1-14 = valid channel) + */ + int getChannel() const { return _channel; } + + /** + * Sets the channel for ESP-NOW communication + * + * @param ch The channel to set (0 = AUTO, 1-14 = valid channel) + */ + void setChannel(int ch) { _channel = ch; } }; #endif diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index d182aea6..02e36397 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -8,6 +8,7 @@ RS232Bridge::RS232Bridge(Stream &serial, mesh::PacketManager *mgr, mesh::RTCCloc : BridgeBase(mgr, rtc), _serial(&serial) {} void RS232Bridge::begin() { + Serial.printf("%s: RS232 BRIDGE: Initializing...\n", getLogDateTime()); #if !defined(WITH_RS232_BRIDGE_RX) || !defined(WITH_RS232_BRIDGE_TX) #error "WITH_RS232_BRIDGE_RX and WITH_RS232_BRIDGE_TX must be defined" #endif @@ -26,52 +27,25 @@ void RS232Bridge::begin() { #error RS232Bridge was not tested on the current platform #endif ((HardwareSerial *)_serial)->begin(115200); + + // Update bridge state + _initialized = true; } -void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { - // First validate the packet pointer - if (!packet) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX invalid packet pointer\n", getLogDateTime()); -#endif - return; - } +void RS232Bridge::end() { + Serial.printf("%s: RS232 BRIDGE: Stopping...\n", getLogDateTime()); + ((HardwareSerial *)_serial)->end(); - if (!_seen_packets.hasSeen(packet)) { - - uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; - uint16_t len = packet->writeTo(buffer + 4); - - // Check if packet fits within our maximum payload size - if (len > (MAX_TRANS_UNIT + 1)) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), len, - MAX_TRANS_UNIT + 1); -#endif - return; - } - - // Build packet header - buffer[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF; // Magic high byte - buffer[1] = BRIDGE_PACKET_MAGIC & 0xFF; // Magic low byte - buffer[2] = (len >> 8) & 0xFF; // Length high byte - buffer[3] = len & 0xFF; // Length low byte - - // Calculate checksum over the payload - uint16_t checksum = fletcher16(buffer + 4, len); - buffer[4 + len] = (checksum >> 8) & 0xFF; // Checksum high byte - buffer[5 + len] = checksum & 0xFF; // Checksum low byte - - // Send complete packet - _serial->write(buffer, len + SERIAL_OVERHEAD); - -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); -#endif - } + // Update bridge state + _initialized = false; } void RS232Bridge::loop() { + // Guard against uninitialized state + if (_initialized == false) { + return; + } + while (_serial->available()) { uint8_t b = _serial->read(); @@ -140,6 +114,55 @@ void RS232Bridge::loop() { } } +void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { + // Guard against uninitialized state + if (_initialized == false) { + Serial.printf("%s: ESPNOW BRIDGE: TX packet attempted before initialization\n", getLogDateTime()); + return; + } + + // First validate the packet pointer + if (!packet) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: TX invalid packet pointer\n", getLogDateTime()); +#endif + return; + } + + if (!_seen_packets.hasSeen(packet)) { + + uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; + uint16_t len = packet->writeTo(buffer + 4); + + // Check if packet fits within our maximum payload size + if (len > (MAX_TRANS_UNIT + 1)) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), len, + MAX_TRANS_UNIT + 1); +#endif + return; + } + + // Build packet header + buffer[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF; // Magic high byte + buffer[1] = BRIDGE_PACKET_MAGIC & 0xFF; // Magic low byte + buffer[2] = (len >> 8) & 0xFF; // Length high byte + buffer[3] = len & 0xFF; // Length low byte + + // Calculate checksum over the payload + uint16_t checksum = fletcher16(buffer + 4, len); + buffer[4 + len] = (checksum >> 8) & 0xFF; // Checksum high byte + buffer[5 + len] = checksum & 0xFF; // Checksum low byte + + // Send complete packet + _serial->write(buffer, len + SERIAL_OVERHEAD); + +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); +#endif + } +} + void RS232Bridge::onPacketReceived(mesh::Packet *packet) { handleReceivedPacket(packet); } diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index 3b09de75..e9cc22d0 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -65,6 +65,12 @@ public: */ void begin() override; + /** + * Stops the RS232 bridge + * + */ + void end() override; + /** * @brief Main loop handler for processing incoming serial data * From 2297d2401356a9f388c24231fce3b72df9e0e759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Wed, 24 Sep 2025 16:46:03 +0100 Subject: [PATCH 006/115] Minor fixes --- examples/simple_repeater/MyMesh.h | 4 +++- src/helpers/CommonCLI.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7ae10812..c1d1f264 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -165,12 +165,13 @@ public: void updateAdvertTimer() override; void updateFloodAdvertTimer() override; -#if defined(WITH_ESPNOW_BRIDGE) +#if defined(WITH_BRIDGE) void setBridgeState(bool enable) { if (enable == bridge.getState()) return; enable ? bridge.begin() : bridge.end(); } +#if defined(WITH_ESPNOW_BRIDGE) void updateBridgeChannel(int ch) override { bridge.setChannel(ch); if (bridge.getState()) { @@ -178,6 +179,7 @@ public: bridge.begin(); } } +#endif #endif void setLoggingOn(bool enable) override { _logging = enable; } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 55751d45..4344ff1d 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -31,7 +31,7 @@ struct NodePrefs { // persisted to file uint8_t interference_threshold; uint8_t agc_reset_interval; // secs / 4 uint8_t bridge_enabled; // boolean - uint8_t bridge_channel; // 0 = AUTO, 1-14 valid + uint8_t bridge_channel; // 1-14 }; class CommonCLICallbacks { @@ -56,7 +56,7 @@ public: virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; - + #ifdef WITH_ESPNOW_BRIDGE virtual void setBridgeState(bool enable) = 0; virtual void updateBridgeChannel(int ch) = 0; From db7635102d77ffb8609fd4206c86b12458fc3450 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 28 Sep 2025 09:43:28 +0200 Subject: [PATCH 007/115] gps_page: enable if gps enabled --- examples/companion_radio/ui-new/UITask.cpp | 8 ++++---- variants/wio-tracker-l1-eink/platformio.ini | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index c48c6972..d124a494 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -75,7 +75,7 @@ class HomeScreen : public UIScreen { RADIO, BLUETOOTH, ADVERT, -#if UI_GPS_PAGE == 1 +#if ENV_INCLUDE_GPS == 1 GPS, #endif #if UI_SENSORS_PAGE == 1 @@ -173,7 +173,7 @@ public: // curr page indicator int y = 14; - int x = display.width() / 2 - 25; + int x = display.width() / 2 - 5 * (HomePage::Count-1); for (uint8_t i = 0; i < HomePage::Count; i++, x += 10) { if (i == _page) { display.fillRect(x-1, y-1, 3, 3); @@ -253,7 +253,7 @@ public: display.setColor(DisplayDriver::GREEN); display.drawXbm((display.width() - 32) / 2, 18, advert_icon, 32, 32); display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL); -#if UI_GPS_PAGE == 1 +#if ENV_INCLUDE_GPS == 1 } else if (_page == HomePage::GPS) { LocationProvider* nmea = sensors.getLocationProvider(); int y = 18; @@ -408,7 +408,7 @@ public: } return true; } -#if UI_GPS_PAGE == 1 +#if ENV_INCLUDE_GPS == 1 if (c == KEY_ENTER && _page == HomePage::GPS) { _task->toggleGPS(); return true; diff --git a/variants/wio-tracker-l1-eink/platformio.ini b/variants/wio-tracker-l1-eink/platformio.ini index db22c01b..a41e9e23 100644 --- a/variants/wio-tracker-l1-eink/platformio.ini +++ b/variants/wio-tracker-l1-eink/platformio.ini @@ -55,7 +55,6 @@ build_flags = ${WioTrackerL1Eink.build_flags} ; -D MESH_DEBUG=1 -D UI_RECENT_LIST_SIZE=6 -D UI_SENSORS_PAGE=1 - -D UI_GPS_PAGE=1 build_src_filter = ${WioTrackerL1Eink.build_src_filter} + + From 18bfc2d81a45589f810ab7afc20a25810b8fa7e3 Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Tue, 30 Sep 2025 09:21:12 +0200 Subject: [PATCH 008/115] DisplayDriver: introduce drawTextRightAlign and drawTextLeftAlign --- examples/companion_radio/ui-new/UITask.cpp | 31 +++++++--------------- src/helpers/ui/DisplayDriver.h | 9 +++++++ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index d124a494..c3da0643 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -257,40 +257,27 @@ public: } else if (_page == HomePage::GPS) { LocationProvider* nmea = sensors.getLocationProvider(); int y = 18; - display.setCursor(0, y); - display.print(_task->getGPSState() ? "gps on" : "gps off"); + display.drawTextLeftAlign(0, y, _task->getGPSState() ? "gps on" : "gps off"); if (nmea == NULL) { y = y + 12; - display.setCursor(0, y); - display.print("Can't access GPS"); + display.drawTextLeftAlign(0, y, "Can't access GPS"); } else { char buf[50]; strcpy(buf, nmea->isValid()?"fix":"no fix"); - display.setCursor( - display.width()-display.getTextWidth(buf)-1, y); - display.print(buf); + display.drawTextRightAlign(display.width()-1, y, buf); y = y + 12; - display.setCursor(0,y); - display.print("sat"); + display.drawTextLeftAlign(0, y, "sat"); sprintf(buf, "%d", nmea->satellitesCount()); - display.setCursor( - display.width()-display.getTextWidth(buf)-1, y); - display.print(buf); + display.drawTextRightAlign(display.width()-1, y, buf); y = y + 12; - display.setCursor(0,y); - display.print("pos"); + display.drawTextLeftAlign(0, y, "pos"); sprintf(buf, "%.4f %.4f", nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.); - display.setCursor( - display.width()-display.getTextWidth(buf)-1, y); - display.print(buf); + display.drawTextRightAlign(display.width()-1, y, buf); y = y + 12; - display.setCursor(0,y); - display.print("alt"); + display.drawTextLeftAlign(0, y, "alt"); sprintf(buf, "%.2f", nmea->getAltitude()/1000.); - display.setCursor( - display.width()-display.getTextWidth(buf)-1, y); - display.print(buf); + display.drawTextRightAlign(display.width()-1, y, buf); y = y + 12; } #endif diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index 32839edc..ec63c191 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -32,6 +32,15 @@ public: setCursor(mid_x - w/2, y); print(str); } + virtual void drawTextRightAlign(int x_anch, int y, const char* str) { + int w = getTextWidth(str); + setCursor(x_anch - w, y); + print(str); + } + virtual void drawTextLeftAlign(int x_anch, int y, const char* str) { + setCursor(x_anch, y); + print(str); + } // convert UTF-8 characters to displayable block characters for compatibility virtual void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) { From aa946bbe3671ea025196de249f4f6232e616fe92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Thu, 2 Oct 2025 09:47:00 +0100 Subject: [PATCH 009/115] WITH_BRIDGE was not implementing setBridgeState() --- src/helpers/CommonCLI.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 4344ff1d..364cf518 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -57,10 +57,12 @@ public: virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; -#ifdef WITH_ESPNOW_BRIDGE +#ifdef WITH_BRIDGE virtual void setBridgeState(bool enable) = 0; +#ifdef WITH_ESPNOW_BRIDGE virtual void updateBridgeChannel(int ch) = 0; #endif +#endif }; class CommonCLI { From 262e9864e75c346d625b6c99d5ee6033908a24f5 Mon Sep 17 00:00:00 2001 From: Florent Date: Thu, 2 Oct 2025 12:18:47 +0200 Subject: [PATCH 010/115] stm32: upd repeater targets --- variants/rak3x72/platformio.ini | 1 + variants/wio-e5-dev/platformio.ini | 16 ++++++++++++++++ variants/wio-e5-mini/platformio.ini | 1 + 3 files changed, 18 insertions(+) diff --git a/variants/rak3x72/platformio.ini b/variants/rak3x72/platformio.ini index 67db107a..a6260089 100644 --- a/variants/rak3x72/platformio.ini +++ b/variants/rak3x72/platformio.ini @@ -18,6 +18,7 @@ extends = rak3x72 build_flags = ${rak3x72.build_flags} -D ADVERT_NAME='"RAK3x72 Repeater"' -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 build_src_filter = ${rak3x72.build_src_filter} +<../examples/simple_repeater/*.cpp> diff --git a/variants/wio-e5-dev/platformio.ini b/variants/wio-e5-dev/platformio.ini index c50bc247..d7e63c83 100644 --- a/variants/wio-e5-dev/platformio.ini +++ b/variants/wio-e5-dev/platformio.ini @@ -19,9 +19,25 @@ build_flags = ${lora_e5.build_flags} -D LORA_TX_POWER=22 -D ADVERT_NAME='"WIO-E5 Repeater"' -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 build_src_filter = ${lora_e5.build_src_filter} +<../examples/simple_repeater/*.cpp> +[env:wio-e5-repeater_bridge_rs232] +extends = lora_e5 +build_flags = ${lora_e5.build_flags} + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"WIO-E5 Repeater"' + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D ENABLE_HWSERIAL2 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=PA3 + -D WITH_RS232_BRIDGE_TX=PA2 +build_src_filter = ${lora_e5.build_src_filter} + + + +<../examples/simple_repeater/*.cpp> + [env:wio-e5_companion_radio_usb] extends = lora_e5 build_flags = ${lora_e5.build_flags} diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 919ee90c..83784443 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -22,6 +22,7 @@ build_flags = ${lora_e5_mini.build_flags} -D LORA_TX_POWER=22 -D ADVERT_NAME='"wio-e5-mini Repeater"' -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 build_src_filter = ${lora_e5_mini.build_src_filter} +<../examples/simple_repeater/*.cpp> From 8edcb46a28271be84d1f5e422fa7e4ee5f6ec963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 3 Oct 2025 00:20:09 +0100 Subject: [PATCH 011/115] Bridge: enhance CLI configuration options --- examples/simple_repeater/MyMesh.cpp | 31 +++++--- examples/simple_repeater/MyMesh.h | 30 ++++---- src/helpers/AbstractBridge.h | 2 +- src/helpers/CommonCLI.cpp | 106 ++++++++++++++++++++------- src/helpers/CommonCLI.h | 22 ++++-- src/helpers/bridges/BridgeBase.cpp | 3 +- src/helpers/bridges/BridgeBase.h | 18 ++--- src/helpers/bridges/ESPNowBridge.cpp | 17 ++--- src/helpers/bridges/ESPNowBridge.h | 35 ++------- src/helpers/bridges/RS232Bridge.cpp | 11 ++- src/helpers/bridges/RS232Bridge.h | 5 +- 11 files changed, 162 insertions(+), 118 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4806c28a..3124354e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -231,6 +231,12 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { } void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { +#ifdef WITH_BRIDGE + if (_prefs.bridge_pkt_src == 1) { + bridge.sendPacket(pkt); + } +#endif + if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -252,8 +258,11 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { void MyMesh::logTx(mesh::Packet *pkt, int len) { #ifdef WITH_BRIDGE - bridge.onPacketTransmitted(pkt); + if (_prefs.bridge_pkt_src == 0) { + bridge.sendPacket(pkt); + } #endif + if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -481,9 +490,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) #if defined(WITH_RS232_BRIDGE) - , bridge(WITH_RS232_BRIDGE, _mgr, &rtc) -#elif defined(WITH_ESPNOW_BRIDGE) - , bridge(_mgr, &rtc) + , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) +#endif +#if defined(WITH_ESPNOW_BRIDGE) + , bridge(&_prefs, _mgr, &rtc) #endif { next_local_advert = next_flood_advert = 0; @@ -513,8 +523,14 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled - _prefs.bridge_enabled = 1; // enabled - _prefs.bridge_channel = 0; // auto + + // bridge defaults + _prefs.bridge_enabled = 1; // enabled + _prefs.bridge_delay = 500; // milliseconds + _prefs.bridge_pkt_src = 0; // logTx + _prefs.bridge_baud = 115200; // baud rate + _prefs.bridge_channel = 1; // channel 1 + StrHelper::strncpy(_prefs.bridge_secret, "LVSITANOS", sizeof(_prefs.bridge_secret)); } void MyMesh::begin(FILESYSTEM *fs) { @@ -525,9 +541,6 @@ void MyMesh::begin(FILESYSTEM *fs) { acl.load(_fs); -#if defined(WITH_ESPNOW_BRIDGE) - bridge.setChannel(_prefs.bridge_channel); -#endif #if defined(WITH_BRIDGE) if (_prefs.bridge_enabled) { bridge.begin(); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c1d1f264..2e84843a 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -165,23 +165,6 @@ public: void updateAdvertTimer() override; void updateFloodAdvertTimer() override; -#if defined(WITH_BRIDGE) - void setBridgeState(bool enable) { - if (enable == bridge.getState()) return; - enable ? bridge.begin() : bridge.end(); - } - -#if defined(WITH_ESPNOW_BRIDGE) - void updateBridgeChannel(int ch) override { - bridge.setChannel(ch); - if (bridge.getState()) { - bridge.end(); - bridge.begin(); - } - } -#endif -#endif - void setLoggingOn(bool enable) override { _logging = enable; } void eraseLogFile() override { @@ -199,4 +182,17 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); + +#if defined(WITH_BRIDGE) + void setBridgeState(bool enable) override { + if (enable == bridge.getState()) return; + enable ? bridge.begin() : bridge.end(); + } + + void restartBridge() override { + if (!bridge.getState()) return; + bridge.end(); + bridge.begin(); + } +#endif }; diff --git a/src/helpers/AbstractBridge.h b/src/helpers/AbstractBridge.h index 73a967d8..89e2dcdd 100644 --- a/src/helpers/AbstractBridge.h +++ b/src/helpers/AbstractBridge.h @@ -35,7 +35,7 @@ public: * * @param packet The packet that was transmitted. */ - virtual void onPacketTransmitted(mesh::Packet* packet) = 0; + virtual void sendPacket(mesh::Packet* packet) = 0; /** * @brief Processes a received packet from the bridge's medium. diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 97c7eada..d7eba363 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -59,7 +59,11 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.read((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 file.read((uint8_t *)&_prefs->bridge_enabled, sizeof(_prefs->bridge_enabled)); // 127 - file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 128 + file.read((uint8_t *)&_prefs->bridge_delay, sizeof(_prefs->bridge_delay)); // 128 + file.read((uint8_t *)&_prefs->bridge_pkt_src, sizeof(_prefs->bridge_pkt_src)); // 130 + file.read((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131 + file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 132 + file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 133 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -72,7 +76,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); + + // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); + _prefs->bridge_delay = constrain(_prefs->bridge_delay, 0, 10000); + _prefs->bridge_pkt_src = constrain(_prefs->bridge_pkt_src, 0, 1); + _prefs->bridge_baud = constrain(_prefs->bridge_baud, 9600, 115200); _prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14); file.close(); @@ -119,7 +128,11 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.write((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 file.write((uint8_t *)&_prefs->bridge_enabled, sizeof(_prefs->bridge_enabled)); // 127 - file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 128 + file.write((uint8_t *)&_prefs->bridge_delay, sizeof(_prefs->bridge_delay)); // 128 + file.write((uint8_t *)&_prefs->bridge_pkt_src, sizeof(_prefs->bridge_pkt_src)); // 130 + file.write((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131 + file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 132 + file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 133 file.close(); } @@ -205,6 +218,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else if (memcmp(command, "clear stats", 11) == 0) { _callbacks->clearStats(); strcpy(reply, "(OK - stats reset)"); + /* + * GET commands + */ } else if (memcmp(command, "get ", 4) == 0) { const char* config = &command[4]; if (memcmp(config, "af", 2) == 0) { @@ -261,38 +277,29 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch #ifdef WITH_BRIDGE } else if (memcmp(config, "bridge.enabled", 14) == 0) { sprintf(reply, "> %s", _prefs->bridge_enabled ? "on" : "off"); + } else if (memcmp(config, "bridge.delay", 12) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->bridge_delay); + } else if (memcmp(config, "bridge.source", 13) == 0) { + sprintf(reply, "> %s", _prefs->bridge_pkt_src ? "logRx" : "logTx"); +#endif +#ifdef WITH_RS232_BRIDGE + } else if (memcmp(config, "bridge.baud", 11) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->bridge_baud); +#endif #ifdef WITH_ESPNOW_BRIDGE } else if (memcmp(config, "bridge.channel", 14) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->bridge_channel); -#endif + } else if (memcmp(config, "bridge.secret", 13) == 0) { + sprintf(reply, "> %s", _prefs->bridge_secret); #endif } else { sprintf(reply, "??: %s", config); } + /* + * SET commands + */ } else if (memcmp(command, "set ", 4) == 0) { const char* config = &command[4]; -#ifdef WITH_BRIDGE - if (memcmp(config, "bridge.enabled ", 15) == 0) { - _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; - _callbacks->setBridgeState(_prefs->bridge_enabled); - savePrefs(); - strcpy(reply, "OK"); - } - else -#ifdef WITH_ESPNOW_BRIDGE - if (memcmp(config, "bridge.channel ", 15) == 0) { - int ch = atoi(&config[15]); - if (ch > 0 && ch < 15) { - _prefs->bridge_channel = (uint8_t)ch; - _callbacks->updateBridgeChannel(ch); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error: channel must be 0 (AUTO) or 1-14"); - } - } else -#endif -#endif if (memcmp(config, "af ", 3) == 0) { _prefs->airtime_factor = atof(&config[3]); savePrefs(); @@ -428,6 +435,55 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->freq = atof(&config[5]); savePrefs(); strcpy(reply, "OK - reboot to apply"); +#ifdef WITH_BRIDGE + } else if (memcmp(config, "bridge.enabled ", 15) == 0) { + _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; + _callbacks->setBridgeState(_prefs->bridge_enabled); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.delay ", 13) == 0) { + int delay = _atoi(&config[13]); + if (delay >= 0 && delay <= 10000) { + _prefs->bridge_delay = (uint16_t)delay; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: delay must be between 0-10000 ms"); + } + } else if (memcmp(config, "bridge.source ", 14) == 0) { + _prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); +#endif +#ifdef WITH_RS232_BRIDGE + } else if (memcmp(config, "bridge.baud ", 12) == 0) { + uint32_t baud = atoi(&config[12]); + if (baud >= 9600 && baud <= 115200) { + _prefs->bridge_baud = (uint32_t)baud; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: baud rate must be between 9600-115200"); + } +#endif +#ifdef WITH_ESPNOW_BRIDGE + } else if (memcmp(config, "bridge.channel ", 15) == 0) { + int ch = atoi(&config[15]); + if (ch > 0 && ch < 15) { + _prefs->bridge_channel = (uint8_t)ch; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: channel must be between 1-14"); + } + } else if (memcmp(config, "bridge.secret ", 14) == 0) { + StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret)); + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); +#endif } else { sprintf(reply, "unknown config: %s", config); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 364cf518..227a9efb 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -30,8 +30,13 @@ struct NodePrefs { // persisted to file uint8_t flood_max; uint8_t interference_threshold; uint8_t agc_reset_interval; // secs / 4 - uint8_t bridge_enabled; // boolean - uint8_t bridge_channel; // 1-14 + // Bridge settings + uint8_t bridge_enabled; // boolean + uint16_t bridge_delay; // milliseconds (default 500 ms) + uint8_t bridge_pkt_src; // 0 = logTx, 1 = logRx (default logTx) + uint32_t bridge_baud; // 9600, 19200, 38400, 57600, 115200 (default 115200) + uint8_t bridge_channel; // 1-14 (ESP-NOW only) + char bridge_secret[16]; // for XOR encryption of bridge packets (ESP-NOW only) }; class CommonCLICallbacks { @@ -57,12 +62,13 @@ public: virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; -#ifdef WITH_BRIDGE - virtual void setBridgeState(bool enable) = 0; -#ifdef WITH_ESPNOW_BRIDGE - virtual void updateBridgeChannel(int ch) = 0; -#endif -#endif + virtual void setBridgeState(bool enable) { + // no op by default + }; + + virtual void restartBridge() { + // no op by default + }; }; class CommonCLI { diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp index 9434b23b..527ec358 100644 --- a/src/helpers/bridges/BridgeBase.cpp +++ b/src/helpers/bridges/BridgeBase.cpp @@ -40,7 +40,8 @@ void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { } if (!_seen_packets.hasSeen(packet)) { - _mgr->queueInbound(packet, millis() + BRIDGE_DELAY); + // bridge_delay provides a buffer to prevent immediate processing conflicts in the mesh network. + _mgr->queueInbound(packet, millis() + _prefs->bridge_delay); } else { _mgr->free(packet); } diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index a3b1c85e..20b136e2 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -1,6 +1,7 @@ #pragma once #include "helpers/AbstractBridge.h" +#include "helpers/CommonCLI.h" #include "helpers/SimpleMeshTables.h" #include @@ -48,34 +49,31 @@ public: static constexpr uint16_t BRIDGE_LENGTH_SIZE = sizeof(uint16_t); static constexpr uint16_t BRIDGE_CHECKSUM_SIZE = sizeof(uint16_t); - /** - * @brief Default delay in milliseconds for scheduling inbound packet processing - * - * It provides a buffer to prevent immediate processing conflicts in the mesh network. - * Used in handleReceivedPacket() as: millis() + BRIDGE_DELAY - */ - static constexpr uint16_t BRIDGE_DELAY = 500; // TODO: maybe too high ? - protected: /** Tracks bridge state */ bool _initialized = false; - + /** Packet manager for allocating and queuing mesh packets */ mesh::PacketManager *_mgr; /** RTC clock for timestamping debug messages */ mesh::RTCClock *_rtc; + /** Node preferences for configuration settings */ + NodePrefs *_prefs; + /** Tracks seen packets to prevent loops in broadcast communications */ SimpleMeshTables _seen_packets; /** * @brief Constructs a BridgeBase instance * + * @param prefs Node preferences for configuration settings * @param mgr PacketManager for allocating and queuing packets * @param rtc RTCClock for timestamping debug messages */ - BridgeBase(mesh::PacketManager *mgr, mesh::RTCClock *rtc) : _mgr(mgr), _rtc(rtc) {} + BridgeBase(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCClock *rtc) + : _prefs(prefs), _mgr(mgr), _rtc(rtc) {} /** * @brief Gets formatted date/time string for logging diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index c508c5a0..a8a6fb53 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -21,8 +21,8 @@ void ESPNowBridge::send_cb(const uint8_t *mac, esp_now_send_status_t status) { } } -ESPNowBridge::ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc) - : BridgeBase(mgr, rtc), _rx_buffer_pos(0) { +ESPNowBridge::ESPNowBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCClock *rtc) + : BridgeBase(prefs, mgr, rtc), _rx_buffer_pos(0) { _instance = this; } @@ -33,8 +33,8 @@ void ESPNowBridge::begin() { WiFi.mode(WIFI_STA); // Set wifi channel - if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Error setting WIFI channel to %d\n", getLogDateTime(), _channel); + if (esp_wifi_set_channel(_prefs->bridge_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Error setting WIFI channel to %d\n", getLogDateTime(), _prefs->bridge_channel); return; } @@ -52,7 +52,7 @@ void ESPNowBridge::begin() { esp_now_peer_info_t peerInfo = {}; memset(&peerInfo, 0, sizeof(peerInfo)); memset(peerInfo.peer_addr, 0xFF, ESP_NOW_ETH_ALEN); // Broadcast address - peerInfo.channel = _channel; + peerInfo.channel = _prefs->bridge_channel; peerInfo.encrypt = false; if (esp_now_add_peer(&peerInfo) != ESP_OK) { @@ -94,9 +94,9 @@ void ESPNowBridge::loop() { } void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { - size_t keyLen = strlen(_secret); + size_t keyLen = strlen(_prefs->bridge_secret); for (size_t i = 0; i < len; i++) { - data[i] ^= _secret[i % keyLen]; + data[i] ^= _prefs->bridge_secret[i % keyLen]; } } @@ -166,10 +166,9 @@ void ESPNowBridge::onDataSent(const uint8_t *mac_addr, esp_now_send_status_t sta // Could add transmission error handling here if needed } -void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { +void ESPNowBridge::sendPacket(mesh::Packet *packet) { // Guard against uninitialized state if (_initialized == false) { - Serial.printf("%s: ESPNOW BRIDGE: TX packet attempted before initialization\n", getLogDateTime()); return; } diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index 401c9eee..e5450dc4 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -73,22 +73,10 @@ private: /** Current position in receive buffer */ size_t _rx_buffer_pos; - /** - * Network encryption key from build define - * Must be defined with WITH_ESPNOW_BRIDGE_SECRET - * Used for XOR encryption to isolate different mesh networks - */ - const char *_secret = WITH_ESPNOW_BRIDGE_SECRET; - - /** - * Channel for ESP-NOW communication - * Valid 2.4GHz channels: 1-14 - */ - int _channel = 0; - /** * Performs XOR encryption/decryption of data - * + * Used to isolate different mesh networks + * * Uses WITH_ESPNOW_BRIDGE_SECRET as the key in a simple XOR operation. * The same operation is used for both encryption and decryption. * While not cryptographically secure, it provides basic network isolation. @@ -121,10 +109,11 @@ public: /** * Constructs an ESPNowBridge instance * + * @param prefs Node preferences for configuration settings * @param mgr PacketManager for allocating and queuing packets * @param rtc RTCClock for timestamping debug messages */ - ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc); + ESPNowBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCClock *rtc); /** * Initializes the ESP-NOW bridge @@ -166,21 +155,7 @@ public: * * @param packet The mesh packet to transmit */ - void onPacketTransmitted(mesh::Packet *packet) override; - - /** - * Gets the current channel - * - * @return The current channel (0 = AUTO, 1-14 = valid channel) - */ - int getChannel() const { return _channel; } - - /** - * Sets the channel for ESP-NOW communication - * - * @param ch The channel to set (0 = AUTO, 1-14 = valid channel) - */ - void setChannel(int ch) { _channel = ch; } + void sendPacket(mesh::Packet *packet) override; }; #endif diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 02e36397..70f4b7d8 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -4,11 +4,11 @@ #ifdef WITH_RS232_BRIDGE -RS232Bridge::RS232Bridge(Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc) - : BridgeBase(mgr, rtc), _serial(&serial) {} +RS232Bridge::RS232Bridge(NodePrefs *prefs, Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc) + : BridgeBase(prefs, mgr, rtc), _serial(&serial) {} void RS232Bridge::begin() { - Serial.printf("%s: RS232 BRIDGE: Initializing...\n", getLogDateTime()); + Serial.printf("%s: RS232 BRIDGE: Initializing at %d baud...\n", getLogDateTime(), _prefs->bridge_baud); #if !defined(WITH_RS232_BRIDGE_RX) || !defined(WITH_RS232_BRIDGE_TX) #error "WITH_RS232_BRIDGE_RX and WITH_RS232_BRIDGE_TX must be defined" #endif @@ -26,7 +26,7 @@ void RS232Bridge::begin() { #else #error RS232Bridge was not tested on the current platform #endif - ((HardwareSerial *)_serial)->begin(115200); + ((HardwareSerial *)_serial)->begin(_prefs->bridge_baud); // Update bridge state _initialized = true; @@ -114,10 +114,9 @@ void RS232Bridge::loop() { } } -void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { +void RS232Bridge::sendPacket(mesh::Packet *packet) { // Guard against uninitialized state if (_initialized == false) { - Serial.printf("%s: ESPNOW BRIDGE: TX packet attempted before initialization\n", getLogDateTime()); return; } diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index e9cc22d0..839c0ba0 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -49,11 +49,12 @@ public: /** * @brief Constructs an RS232Bridge instance * + * @param prefs Node preferences for configuration settings * @param serial The hardware serial port to use * @param mgr PacketManager for allocating and queuing packets * @param rtc RTCClock for timestamping debug messages */ - RS232Bridge(Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc); + RS232Bridge(NodePrefs *prefs, Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc); /** * Initializes the RS232 bridge @@ -96,7 +97,7 @@ public: * * @param packet The mesh packet to transmit */ - void onPacketTransmitted(mesh::Packet *packet) override; + void sendPacket(mesh::Packet *packet) override; /** * @brief Called when a complete valid packet has been received from serial From e48f3a58ae71d74772886c418b0000865d96c504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 3 Oct 2025 00:23:09 +0100 Subject: [PATCH 012/115] Remove WITH_ESPNOW_BRIDGE_SECRET definition from platformio.ini files and update documentation to use _prefs->bridge_secret --- src/helpers/bridges/ESPNowBridge.h | 12 ++++-------- variants/generic-e22/platformio.ini | 2 -- variants/heltec_ct62/platformio.ini | 1 - variants/heltec_e213/platformio.ini | 1 - variants/heltec_e290/platformio.ini | 1 - variants/heltec_t190/platformio.ini | 1 - variants/heltec_tracker/platformio.ini | 1 - variants/heltec_v2/platformio.ini | 1 - variants/heltec_v3/platformio.ini | 2 -- variants/heltec_v4/platformio.ini | 1 - variants/heltec_wireless_paper/platformio.ini | 1 - variants/lilygo_t3s3/platformio.ini | 1 - variants/lilygo_t3s3_sx1276/platformio.ini | 1 - variants/lilygo_tbeam_SX1262/platformio.ini | 1 - variants/lilygo_tbeam_SX1276/platformio.ini | 1 - variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 1 - variants/lilygo_tlora_v2_1/platformio.ini | 1 - variants/meshadventurer/platformio.ini | 2 -- variants/station_g2/platformio.ini | 2 -- variants/tenstar_c3/platformio.ini | 2 -- variants/xiao_s3_wio/platformio.ini | 1 - 21 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index e5450dc4..431a036b 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -6,10 +6,6 @@ #ifdef WITH_ESPNOW_BRIDGE -#ifndef WITH_ESPNOW_BRIDGE_SECRET -#error WITH_ESPNOW_BRIDGE_SECRET must be defined to use ESPNowBridge -#endif - /** * @brief Bridge implementation using ESP-NOW protocol for packet transport * @@ -36,11 +32,11 @@ * * Configuration: * - Define WITH_ESPNOW_BRIDGE to enable this bridge - * - Define WITH_ESPNOW_BRIDGE_SECRET with a string to set the network encryption key + * - Define _prefs->bridge_secret with a string to set the network encryption key * * Network Isolation: * Multiple independent mesh networks can coexist by using different - * WITH_ESPNOW_BRIDGE_SECRET values. Packets encrypted with a different key will + * _prefs->bridge_secret values. Packets encrypted with a different key will * fail the checksum validation and be discarded. */ class ESPNowBridge : public BridgeBase { @@ -76,8 +72,8 @@ private: /** * Performs XOR encryption/decryption of data * Used to isolate different mesh networks - * - * Uses WITH_ESPNOW_BRIDGE_SECRET as the key in a simple XOR operation. + * + * Uses _prefs->bridge_secret as the key in a simple XOR operation. * The same operation is used for both encryption and decryption. * While not cryptographically secure, it provides basic network isolation. * diff --git a/variants/generic-e22/platformio.ini b/variants/generic-e22/platformio.ini index 2f61f412..9e4bcf72 100644 --- a/variants/generic-e22/platformio.ini +++ b/variants/generic-e22/platformio.ini @@ -87,7 +87,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -154,7 +153,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 1b83adbf..9dc67334 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -80,7 +80,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_ct62.build_src_filter} diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index a6fe2560..0d63b7e4 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -127,7 +127,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_E213_base.build_src_filter} diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 0223b30c..63703d7c 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -123,7 +123,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_E290_base.build_src_filter} diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index 52bb79e0..7ab4da55 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -125,7 +125,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_T190_base.build_src_filter} diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 5c0df007..d2f7f6b1 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -116,7 +116,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_tracker_base.build_src_filter} diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index d2afe4db..049b83bb 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -75,7 +75,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v2.build_src_filter} diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 417f6edb..26b7754e 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -83,7 +83,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -263,7 +262,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index d50c27a5..72fbfea9 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -77,7 +77,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v4.build_src_filter} diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index 43ac2a82..9f125d75 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -102,7 +102,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index ca221108..3686ba2b 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -86,7 +86,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index 1c0d5cf1..e7d22602 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -84,7 +84,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index f7d1a764..70aed341 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -105,7 +105,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index d7e119ef..512083ff 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -105,7 +105,6 @@ build_flags = -D MAX_NEIGHBOURS=8 -D PERSISTANT_GPS=1 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 328ebf07..7a2b8daa 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -83,7 +83,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index aa957fba..a9ad946b 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -197,7 +197,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini index be3b4943..fe3c7b77 100644 --- a/variants/meshadventurer/platformio.ini +++ b/variants/meshadventurer/platformio.ini @@ -96,7 +96,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -166,7 +165,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 908d6443..83813dc6 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -76,7 +76,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} @@ -139,7 +138,6 @@ build_flags = -D MESH_PACKET_LOGGING=1 -D SX126X_RX_BOOSTED_GAIN=1 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} + diff --git a/variants/tenstar_c3/platformio.ini b/variants/tenstar_c3/platformio.ini index 25bf6713..60014fc9 100644 --- a/variants/tenstar_c3/platformio.ini +++ b/variants/tenstar_c3/platformio.ini @@ -86,7 +86,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -153,7 +152,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index e6d2357d..f5713e42 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -78,7 +78,6 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 - -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = From 69e6d69798f5bdc7fd90deb765c48fcb94f601f7 Mon Sep 17 00:00:00 2001 From: WattleFoxxo Date: Fri, 3 Oct 2025 22:55:32 +1000 Subject: [PATCH 013/115] Fix font and icon scaling issues for TDeck --- src/helpers/ui/ST7789LCDDisplay.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/helpers/ui/ST7789LCDDisplay.cpp b/src/helpers/ui/ST7789LCDDisplay.cpp index 38c28893..87f9b8ad 100644 --- a/src/helpers/ui/ST7789LCDDisplay.cpp +++ b/src/helpers/ui/ST7789LCDDisplay.cpp @@ -39,7 +39,7 @@ bool ST7789LCDDisplay::begin() { display.fillScreen(ST77XX_BLACK); display.setTextColor(ST77XX_WHITE); - display.setTextSize(2); + display.setTextSize(2 * DISPLAY_SCALE_X); display.cp437(true); // Use full 256 char 'Code Page 437' font _isOn = true; @@ -70,12 +70,12 @@ void ST7789LCDDisplay::clear() { void ST7789LCDDisplay::startFrame(Color bkg) { display.fillScreen(ST77XX_BLACK); display.setTextColor(ST77XX_WHITE); - display.setTextSize(1); // This one affects size of Please wait... message + display.setTextSize(1 * DISPLAY_SCALE_X); // This one affects size of Please wait... message display.cp437(true); // Use full 256 char 'Code Page 437' font } void ST7789LCDDisplay::setTextSize(int sz) { - display.setTextSize(sz); + display.setTextSize(sz * DISPLAY_SCALE_X); } void ST7789LCDDisplay::setColor(Color c) { @@ -125,7 +125,22 @@ void ST7789LCDDisplay::drawRect(int x, int y, int w, int h) { } void ST7789LCDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { - display.drawBitmap(x * DISPLAY_SCALE_X, y * DISPLAY_SCALE_Y, bits, w, h, _color); + uint8_t byteWidth = (w + 7) / 8; + + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + uint8_t byte = bits[j * byteWidth + i / 8]; + bool pixelOn = byte & (0x80 >> (i & 7)); + + if (pixelOn) { + for (int dy = 0; dy < DISPLAY_SCALE_X; dy++) { + for (int dx = 0; dx < DISPLAY_SCALE_X; dx++) { + display.drawPixel(x * DISPLAY_SCALE_X + i * DISPLAY_SCALE_X + dx, y * DISPLAY_SCALE_Y + j * DISPLAY_SCALE_X + dy, _color); + } + } + } + } + } } uint16_t ST7789LCDDisplay::getTextWidth(const char* str) { @@ -138,4 +153,4 @@ uint16_t ST7789LCDDisplay::getTextWidth(const char* str) { void ST7789LCDDisplay::endFrame() { // display.display(); -} +} \ No newline at end of file From c568edc8d0e1da9999a1498e923fbd8e4eb09d8e Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sat, 4 Oct 2025 23:04:47 +0200 Subject: [PATCH 014/115] Add MAX_LORA_TX_POWER build flag for Heltec V4 configuration --- variants/heltec_v4/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 5fc9350e..c9ba9e61 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -26,6 +26,7 @@ build_flags = -D PIN_VEXT_EN=36 -D PIN_VEXT_EN_ACTIVE=HIGH -D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm. + -D MAX_LORA_TX_POWER=22 ; Max SX1262 output -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 From 9b4d93d112cc293ba9d6ec788fbce5d61afee5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Sun, 5 Oct 2025 11:48:05 +0100 Subject: [PATCH 015/115] Add bridge type command to CLI for reporting bridge configuration --- src/helpers/CommonCLI.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index d7eba363..cb425b6c 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -274,6 +274,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch mesh::Utils::toHex(&reply[2], _callbacks->getSelfId().pub_key, PUB_KEY_SIZE); } else if (memcmp(config, "role", 4) == 0) { sprintf(reply, "> %s", _callbacks->getRole()); + } else if (memcmp(config, "bridge.type", 11) == 0) { + sprintf(reply, "> %s", +#ifdef WITH_RS232_BRIDGE + "rs232" +#elif WITH_ESPNOW_BRIDGE + "espnow" +#else + "none" +#endif + ); #ifdef WITH_BRIDGE } else if (memcmp(config, "bridge.enabled", 14) == 0) { sprintf(reply, "> %s", _prefs->bridge_enabled ? "on" : "off"); From 45ab0e8cf7656ceff4e30041cece1a656a8f00d5 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 5 Oct 2025 13:58:25 +0200 Subject: [PATCH 016/115] sensecap_indicator: initial espnow support --- src/helpers/ui/LGFXDisplay.cpp | 125 +++++++++++++++++ src/helpers/ui/LGFXDisplay.h | 44 ++++++ .../SCIndicatorDisplay.h | 129 ++++++++++++++++++ .../sensecap_indicator-espnow/platformio.ini | 50 +++++++ variants/sensecap_indicator-espnow/target.cpp | 56 ++++++++ variants/sensecap_indicator-espnow/target.h | 29 ++++ 6 files changed, 433 insertions(+) create mode 100644 src/helpers/ui/LGFXDisplay.cpp create mode 100644 src/helpers/ui/LGFXDisplay.h create mode 100644 variants/sensecap_indicator-espnow/SCIndicatorDisplay.h create mode 100644 variants/sensecap_indicator-espnow/platformio.ini create mode 100644 variants/sensecap_indicator-espnow/target.cpp create mode 100644 variants/sensecap_indicator-espnow/target.h diff --git a/src/helpers/ui/LGFXDisplay.cpp b/src/helpers/ui/LGFXDisplay.cpp new file mode 100644 index 00000000..a53cbc62 --- /dev/null +++ b/src/helpers/ui/LGFXDisplay.cpp @@ -0,0 +1,125 @@ +#include "LGFXDisplay.h" + +bool LGFXDisplay::begin() { + turnOn(); + display->init(); + display->setRotation(1); + display->setBrightness(64); + display->setColorDepth(8); + display->setTextColor(TFT_WHITE); + + buffer.setColorDepth(8); + buffer.setPsram(true); + buffer.createSprite(width(), height()); + + return true; +} + +void LGFXDisplay::turnOn() { +// display->wakeup(); + if (!_isOn) { + display->wakeup(); + } + _isOn = true; +} + +void LGFXDisplay::turnOff() { + if (_isOn) { + display->sleep(); + } + _isOn = false; +} + +void LGFXDisplay::clear() { +// display->clearDisplay(); + buffer.clearDisplay(); +} + +void LGFXDisplay::startFrame(Color bkg) { +// display->startWrite(); +// display->getScanLine(); + buffer.clearDisplay(); + buffer.setTextColor(TFT_WHITE); +} + +void LGFXDisplay::setTextSize(int sz) { + buffer.setTextSize(sz); +} + +void LGFXDisplay::setColor(Color c) { + // _color = (c != 0) ? ILI9342_WHITE : ILI9342_BLACK; + switch (c) { + case DARK: + _color = TFT_BLACK; + break; + case LIGHT: + _color = TFT_WHITE; + break; + case RED: + _color = TFT_RED; + break; + case GREEN: + _color = TFT_GREEN; + break; + case BLUE: + _color = TFT_BLUE; + break; + case YELLOW: + _color = TFT_YELLOW; + break; + case ORANGE: + _color = TFT_ORANGE; + break; + default: + _color = TFT_WHITE; + } + buffer.setTextColor(_color); +} + +void LGFXDisplay::setCursor(int x, int y) { + buffer.setCursor(x, y); +} + +void LGFXDisplay::print(const char* str) { + buffer.println(str); +// Serial.println(str); +} + +void LGFXDisplay::fillRect(int x, int y, int w, int h) { + buffer.fillRect(x, y, w, h, _color); +} + +void LGFXDisplay::drawRect(int x, int y, int w, int h) { + buffer.drawRect(x, y, w, h, _color); +} + +void LGFXDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { + buffer.drawBitmap(x, y, bits, w, h, _color); +} + +uint16_t LGFXDisplay::getTextWidth(const char* str) { + return buffer.textWidth(str); +} + +void LGFXDisplay::endFrame() { + display->startWrite(); + if (UI_ZOOM != 1) { + buffer.pushRotateZoom(display, display->width()/2, display->height()/2 , 0, UI_ZOOM, UI_ZOOM); + } else { + buffer.pushSprite(display, 0, 0); + } + display->endWrite(); +} + +bool LGFXDisplay::getTouch(int *x, int *y) { + lgfx::v1::touch_point_t point; + display->getTouch(&point); + if (UI_ZOOM != 1) { + *x = point.x / UI_ZOOM; + *y = point.y / UI_ZOOM; + } else { + *x = point.x; + *y = point.y; + } + return (*x >= 0) && (*y >= 0); +} \ No newline at end of file diff --git a/src/helpers/ui/LGFXDisplay.h b/src/helpers/ui/LGFXDisplay.h new file mode 100644 index 00000000..81d0239f --- /dev/null +++ b/src/helpers/ui/LGFXDisplay.h @@ -0,0 +1,44 @@ + +/* + * Base class for LovyanGFX supported display (works on ESP32 mainly) + * You can extend this class to support your display, providing your own LGFX + */ + +#pragma once + +#include + +#define LGFX_USE_V1 +#include + +#ifndef UI_ZOOM + #define UI_ZOOM 1 +#endif + +class LGFXDisplay : public DisplayDriver { +protected: + LGFX_Device* display; + LGFX_Sprite buffer; + + bool _isOn; + int _color = TFT_WHITE; + +public: + LGFXDisplay(int w, int h):DisplayDriver(w/UI_ZOOM, h/UI_ZOOM) {_isOn = false;} + bool begin(); + bool isOn() override { return _isOn; } + void turnOn() override; + void turnOff() override; + void clear() override; + void startFrame(Color bkg = DARK) override; + void setTextSize(int sz) override; + void setColor(Color c) override; + void setCursor(int x, int y) override; + void print(const char* str) override; + void fillRect(int x, int y, int w, int h) override; + void drawRect(int x, int y, int w, int h) override; + void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; + uint16_t getTextWidth(const char* str) override; + void endFrame() override; + virtual bool getTouch(int *x, int *y); +}; diff --git a/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h b/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h new file mode 100644 index 00000000..6a7e3177 --- /dev/null +++ b/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h @@ -0,0 +1,129 @@ +#pragma once + +#include + +#define LGFX_USE_V1 +#include + +#include +#include + +class LGFX : public lgfx::LGFX_Device +{ + lgfx::Panel_ST7701 _panel_instance; + lgfx::Bus_RGB _bus_instance; + lgfx::Light_PWM _light_instance; + lgfx::Touch_FT5x06 _touch_instance; + +public: + const uint16_t screenWidth = 480; + const uint16_t screenHeight = 480; + + bool hasButton(void) { return true; } + + LGFX(void) + { + { + auto cfg = _panel_instance.config(); + cfg.memory_width = 480; + cfg.memory_height = 480; + cfg.panel_width = screenWidth; + cfg.panel_height = screenHeight; + cfg.offset_x = 0; + cfg.offset_y = 0; + cfg.offset_rotation = 1; + _panel_instance.config(cfg); + } + + { + auto cfg = _panel_instance.config_detail(); + cfg.pin_cs = 4 | IO_EXPANDER; + cfg.pin_sclk = 41; + cfg.pin_mosi = 48; + cfg.use_psram = 1; + _panel_instance.config_detail(cfg); + } + + { + auto cfg = _bus_instance.config(); + cfg.panel = &_panel_instance; + + cfg.freq_write = 8000000; + cfg.pin_henable = 18; + + cfg.pin_pclk = 21; + cfg.pclk_active_neg = 0; + cfg.pclk_idle_high = 0; + cfg.de_idle_high = 1; + + cfg.pin_hsync = 16; + cfg.hsync_polarity = 0; + cfg.hsync_front_porch = 10; + cfg.hsync_pulse_width = 8; + cfg.hsync_back_porch = 50; + + cfg.pin_vsync = 17; + cfg.vsync_polarity = 0; + cfg.vsync_front_porch = 10; + cfg.vsync_pulse_width = 8; + cfg.vsync_back_porch = 20; + + cfg.pin_d0 = 15; + cfg.pin_d1 = 14; + cfg.pin_d2 = 13; + cfg.pin_d3 = 12; + cfg.pin_d4 = 11; + cfg.pin_d5 = 10; + cfg.pin_d6 = 9; + cfg.pin_d7 = 8; + cfg.pin_d8 = 7; + cfg.pin_d9 = 6; + cfg.pin_d10 = 5; + cfg.pin_d11 = 4; + cfg.pin_d12 = 3; + cfg.pin_d13 = 2; + cfg.pin_d14 = 1; + cfg.pin_d15 = 0; + + _bus_instance.config(cfg); + } + _panel_instance.setBus(&_bus_instance); + + { + auto cfg = _light_instance.config(); + cfg.pin_bl = 45; + _light_instance.config(cfg); + } + _panel_instance.light(&_light_instance); + + { + auto cfg = _touch_instance.config(); + cfg.pin_cs = GPIO_NUM_NC; + cfg.x_min = 0; + cfg.x_max = 479; + cfg.y_min = 0; + cfg.y_max = 479; + cfg.pin_int = GPIO_NUM_NC; + cfg.pin_rst = GPIO_NUM_NC; + cfg.bus_shared = true; + cfg.offset_rotation = 0; + + cfg.i2c_port = 0; + cfg.i2c_addr = 0x48; + cfg.pin_sda = 39; + cfg.pin_scl = 40; + cfg.freq = 400000; + _touch_instance.config(cfg); + _panel_instance.setTouch(&_touch_instance); + } + + setPanel(&_panel_instance); + } +}; + +class SCIndicatorDisplay : public LGFXDisplay { + LGFX disp; +public: + SCIndicatorDisplay() : LGFXDisplay(480, 480) + { display=&disp; } +}; diff --git a/variants/sensecap_indicator-espnow/platformio.ini b/variants/sensecap_indicator-espnow/platformio.ini new file mode 100644 index 00000000..064f3ae8 --- /dev/null +++ b/variants/sensecap_indicator-espnow/platformio.ini @@ -0,0 +1,50 @@ +[SenseCapIndicator-ESPNow] +extends = esp32_base +board = esp32-s3-devkitc-1 +board_build.arduino.memory_type = qio_opi +board_build.flash_mode = qio +board_build.psram_type = opi +board_upload.flash_size = 8MB +board_upload.maximum_size = 8388608 +board_build.partitions = default.csv +build_flags = + ${esp32_base.build_flags} + -D PIN_BOARD_SDA=39 + -D PIN_BOARD_SCL=40 + -D DISPLAY_CLASS=SCIndicatorDisplay + -D DISPLAY_LINES=21 + -D LINE_LENGTH=53 + -D DISABLE_WIFI_OTA=1 + -D IO_EXPANDER=0x40 + -D IO_EXPANDER_IRQ=42 + -D UI_ZOOM=3.5 + -D UI_RECENT_LIST_SIZE=9 + -D UI_SENSORS_PAGE=1 + -D PIN_USER_BTN=38 + -D HAS_TOUCH + -I variants/sensecap_indicator-espnow +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/sensecap_indicator-espnow/*.cpp> + + + + + + +lib_deps=${esp32_base.lib_deps} + adafruit/Adafruit BusIO @ ^1.17.2 + lovyan03/LovyanGFX @ ^1.2.7 + +[env:SenseCapIndicator-ESPNow_comp_radio_usb] +extends =SenseCapIndicator-ESPNow +build_flags = + ${SenseCapIndicator-ESPNow.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1 +build_src_filter = ${SenseCapIndicator-ESPNow.build_src_filter} + +<../examples/companion_radio/ui-new/*.cpp> + +<../examples/companion_radio/*.cpp> +lib_deps = + ${SenseCapIndicator-ESPNow.lib_deps} + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/sensecap_indicator-espnow/target.cpp b/variants/sensecap_indicator-espnow/target.cpp new file mode 100644 index 00000000..efdaac61 --- /dev/null +++ b/variants/sensecap_indicator-espnow/target.cpp @@ -0,0 +1,56 @@ +#include +#include "target.h" +#include + +ESP32Board board; + +ESPNOWRadio radio_driver; + +ESP32RTCClock rtc_clock; +#if defined(ENV_INCLUDE_GPS) +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, (mesh::RTCClock*)&rtc_clock); +EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else +EnvironmentSensorManager sensors = EnvironmentSensorManager(); +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + #ifdef PIN_USER_BTN + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); + #endif +#endif + +bool radio_init() { + rtc_clock.begin(); + + radio_driver.init(); + + return true; // success +} + +uint32_t radio_get_rng_seed() { + return millis() + radio_driver.intID(); // TODO: where to get some entropy? +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + // no-op +} + +void radio_set_tx_power(uint8_t dbm) { + radio_driver.setTxPower(dbm); +} + +// NOTE: as we are using the WiFi radio, the ESP_IDF will have enabled hardware RNG: +// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html +class ESP_RNG : public mesh::RNG { +public: + void random(uint8_t* dest, size_t sz) override { + esp_fill_random(dest, sz); + } +}; + +mesh::LocalIdentity radio_new_identity() { + ESP_RNG rng; + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/sensecap_indicator-espnow/target.h b/variants/sensecap_indicator-espnow/target.h new file mode 100644 index 00000000..bb78e923 --- /dev/null +++ b/variants/sensecap_indicator-espnow/target.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#ifdef ENV_INCLUDE_GPS + #include +#endif +#ifdef DISPLAY_CLASS + #include "SCIndicatorDisplay.h" + #include +#endif + +extern ESP32Board board; +extern ESPNOWRadio radio_driver; +extern ESP32RTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); From 0502bc370dcdce6c106632511e32f3ca7713ae2f Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 5 Oct 2025 19:23:52 +0200 Subject: [PATCH 017/115] CommonCLI: gps management commands --- examples/simple_repeater/MyMesh.cpp | 38 +++++++++++++++++++ examples/simple_repeater/MyMesh.h | 6 +++ src/helpers/CommonCLI.cpp | 13 +++++++ src/helpers/CommonCLI.h | 4 ++ src/helpers/sensors/LocationProvider.h | 1 + .../sensors/MicroNMEALocationProvider.h | 10 +++++ 6 files changed, 72 insertions(+) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index df945d45..93175eb6 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -758,6 +758,44 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) { #endif } +void MyMesh::gpsGetStatus(char * reply) { + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + bool status = l->isActive(); + bool sync = l->isValid(); + int sats = l->satellitesCount(); + if (status) { + sprintf(reply, "on, %s, %d sats", sync?"fix":"no fix", sats); + } else { + strcpy(reply, "off"); + } + } else { + strcpy(reply, "Can't find GPS"); + } +} + +void MyMesh::gpsStart() { + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + l->begin(); + l->reset(); + } +} + +void MyMesh::gpsStop() { + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + l->stop(); + } +} + +void MyMesh::gpsSyncTime() { + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + l->syncTime(); + } +} + void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 05a8d13b..19bef70e 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -176,6 +176,12 @@ public: void formatNeighborsReply(char *reply) override; void removeNeighbor(const uint8_t* pubkey, int key_len) override; + // Gps mgmt cli callbacks + void gpsGetStatus(char * reply) override; + void gpsStart() override; + void gpsStop() override; + void gpsSyncTime() override; + mesh::LocalIdentity& getSelfId() override { return self_id; } void saveIdentity(const mesh::LocalIdentity& new_id) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 68acdf2b..e674cbc2 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -401,6 +401,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate()); } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); +#if ENV_INCLUDE_GPS == 1 + } else if (memcmp(command, "gps on", 6) == 0) { + _callbacks->gpsStart(); + strcpy(reply, "ok"); + } else if (memcmp(command, "gps off", 7) == 0) { + _callbacks->gpsStop(); + strcpy(reply, "ok"); + } else if (memcmp(command, "gps sync", 8) == 0) { + _callbacks->gpsSyncTime(); + strcpy(reply, "Waiting fix ..."); + } else if (memcmp(command, "gps", 3) == 0) { + _callbacks->gpsGetStatus(reply); +#endif } else if (memcmp(command, "log start", 9) == 0) { _callbacks->setLoggingOn(true); strcpy(reply, " logging on"); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ff8ff50e..08e5f988 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -50,6 +50,10 @@ public: virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; + virtual void gpsGetStatus(char * reply) {} + virtual void gpsStart() {} + virtual void gpsStop() {} + virtual void gpsSyncTime() {} }; class CommonCLI { diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index f93dec48..f1c934e5 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -21,4 +21,5 @@ public: virtual void begin() = 0; virtual void stop() = 0; virtual void loop() = 0; + virtual bool isActive() = 0; }; diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index ec82f25e..16344108 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -78,6 +78,16 @@ public : } } + bool isActive() override { + // directly read the enable pin if present as gps can be + // activated/deactivated outside of here ... + if (_pin_en != -1) { + return digitalRead(_pin_en) == PIN_GPS_EN_ACTIVE; + } else { + return true; // no enable so must be active + } + } + void syncTime() override { nmea.clear(); LocationProvider::syncTime(); } long getLatitude() override { return nmea.getLatitude(); } long getLongitude() override { return nmea.getLongitude(); } From e4f2d63b0abd850fe602e0ab045c6147ff2f4037 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 5 Oct 2025 20:31:25 +0200 Subject: [PATCH 018/115] cli_gps: use sensormanger to toggle gps on/off to keep state coherent --- examples/simple_repeater/MyMesh.cpp | 44 +++++++++++++------ examples/simple_repeater/MyMesh.h | 3 ++ src/helpers/sensors/LocationProvider.h | 2 +- .../sensors/MicroNMEALocationProvider.h | 2 +- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 93175eb6..e5a8b6b3 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -761,11 +761,15 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) { void MyMesh::gpsGetStatus(char * reply) { LocationProvider * l = sensors.getLocationProvider(); if (l != NULL) { - bool status = l->isActive(); - bool sync = l->isValid(); + bool enabled = l->isEnabled(); // is EN pin on ? + bool active = gpsGetState(); // is enabled at SensorManager level ? + bool fix = l->isValid(); // has fix ? int sats = l->satellitesCount(); - if (status) { - sprintf(reply, "on, %s, %d sats", sync?"fix":"no fix", sats); + if (enabled) { + sprintf(reply, "on, %s, %s, %d sats", + active?"active":"deactivated", + fix?"fix":"no fix", + sats); } else { strcpy(reply, "off"); } @@ -774,19 +778,33 @@ void MyMesh::gpsGetStatus(char * reply) { } } -void MyMesh::gpsStart() { - LocationProvider * l = sensors.getLocationProvider(); - if (l != NULL) { - l->begin(); - l->reset(); +bool MyMesh::gpsGetState() { + int num = sensors.getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(sensors.getSettingName(i), "gps") == 0) { + return !strcmp(sensors.getSettingValue(i), "1"); + } } + return false; +} + +void MyMesh::gpsSetState(bool value) { + // toggle GPS on/off + int num = sensors.getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(sensors.getSettingName(i), "gps") == 0) { + sensors.setSettingValue("gps", value?"1":"0"); + break; + } + } +} + +void MyMesh::gpsStart() { + gpsSetState(true); } void MyMesh::gpsStop() { - LocationProvider * l = sensors.getLocationProvider(); - if (l != NULL) { - l->stop(); - } + gpsSetState(false); } void MyMesh::gpsSyncTime() { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 19bef70e..880d1043 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -142,6 +142,9 @@ protected: void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; + bool gpsGetState(); + void gpsSetState(bool value); + public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index f1c934e5..81d08652 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -21,5 +21,5 @@ public: virtual void begin() = 0; virtual void stop() = 0; virtual void loop() = 0; - virtual bool isActive() = 0; + virtual bool isEnabled() = 0; }; diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 16344108..fb29fd79 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -78,7 +78,7 @@ public : } } - bool isActive() override { + bool isEnabled() override { // directly read the enable pin if present as gps can be // activated/deactivated outside of here ... if (_pin_en != -1) { From 7be65c148eac7d2f5b95b68b47c9db994a5ecd5a Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Mon, 6 Oct 2025 10:25:10 +0200 Subject: [PATCH 019/115] cli_gps: remove callbacks and add generic sensor set/get. --- examples/simple_repeater/MyMesh.cpp | 56 --------------------- examples/simple_repeater/MyMesh.h | 9 ---- src/helpers/CommonCLI.cpp | 77 ++++++++++++++++++++++++++--- src/helpers/CommonCLI.h | 8 +-- 4 files changed, 74 insertions(+), 76 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index e5a8b6b3..df945d45 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -758,62 +758,6 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) { #endif } -void MyMesh::gpsGetStatus(char * reply) { - LocationProvider * l = sensors.getLocationProvider(); - if (l != NULL) { - bool enabled = l->isEnabled(); // is EN pin on ? - bool active = gpsGetState(); // is enabled at SensorManager level ? - bool fix = l->isValid(); // has fix ? - int sats = l->satellitesCount(); - if (enabled) { - sprintf(reply, "on, %s, %s, %d sats", - active?"active":"deactivated", - fix?"fix":"no fix", - sats); - } else { - strcpy(reply, "off"); - } - } else { - strcpy(reply, "Can't find GPS"); - } -} - -bool MyMesh::gpsGetState() { - int num = sensors.getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(sensors.getSettingName(i), "gps") == 0) { - return !strcmp(sensors.getSettingValue(i), "1"); - } - } - return false; -} - -void MyMesh::gpsSetState(bool value) { - // toggle GPS on/off - int num = sensors.getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(sensors.getSettingName(i), "gps") == 0) { - sensors.setSettingValue("gps", value?"1":"0"); - break; - } - } -} - -void MyMesh::gpsStart() { - gpsSetState(true); -} - -void MyMesh::gpsStop() { - gpsSetState(false); -} - -void MyMesh::gpsSyncTime() { - LocationProvider * l = sensors.getLocationProvider(); - if (l != NULL) { - l->syncTime(); - } -} - void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 880d1043..05a8d13b 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -142,9 +142,6 @@ protected: void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; - bool gpsGetState(); - void gpsSetState(bool value); - public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); @@ -179,12 +176,6 @@ public: void formatNeighborsReply(char *reply) override; void removeNeighbor(const uint8_t* pubkey, int key_len) override; - // Gps mgmt cli callbacks - void gpsGetStatus(char * reply) override; - void gpsStart() override; - void gpsStop() override; - void gpsSyncTime() override; - mesh::LocalIdentity& getSelfId() override { return self_id; } void saveIdentity(const mesh::LocalIdentity& new_id) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index e674cbc2..ee029ec3 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -128,6 +128,27 @@ void CommonCLI::savePrefs() { _callbacks->savePrefs(); } +const char* CommonCLI::sensorGetCustomVar(const char* key) { + int num = sensors.getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(sensors.getSettingName(i), key) == 0) { + return sensors.getSettingValue(i); + } + } + return NULL; +} + +bool CommonCLI::sensorSetCustomVar(const char* key, const char* value) { + int num = sensors.getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(sensors.getSettingName(i), key) == 0) { + sensors.setSettingValue(key, value); + return true; + } + } + return false; +} + void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, char* reply) { if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return @@ -401,18 +422,60 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate()); } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); + } else if (memcmp(command, "sensor get ", 11) == 0) { + const char* key = command + 11; + const char* val = sensorGetCustomVar(key); + if (val != NULL) { + strcpy(reply, val); + } else { + strcpy(reply, "can't find custom var"); + } + } else if (memcmp(command, "sensor set ", 11) == 0) { + const char* args = &command[11]; + const char* value = strchr(args,' ') + 1; + char key [value-args+1]; + strncpy(key, args, value-args-1); + if (sensorSetCustomVar(key, value)) { + strcpy(reply, "ok"); + } else { + strcpy(reply, "can't find custom var"); + } #if ENV_INCLUDE_GPS == 1 } else if (memcmp(command, "gps on", 6) == 0) { - _callbacks->gpsStart(); - strcpy(reply, "ok"); + if (sensorSetCustomVar("gps", "1")) { + strcpy(reply, "ok"); + } else { + strcpy(reply, "gps toggle not found"); + } } else if (memcmp(command, "gps off", 7) == 0) { - _callbacks->gpsStop(); - strcpy(reply, "ok"); + if (sensorSetCustomVar("gps", "0")) { + strcpy(reply, "ok"); + } else { + strcpy(reply, "gps toggle not found"); + } } else if (memcmp(command, "gps sync", 8) == 0) { - _callbacks->gpsSyncTime(); - strcpy(reply, "Waiting fix ..."); + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + l->syncTime(); + } } else if (memcmp(command, "gps", 3) == 0) { - _callbacks->gpsGetStatus(reply); + LocationProvider * l = sensors.getLocationProvider(); + if (l != NULL) { + bool enabled = l->isEnabled(); // is EN pin on ? + bool fix = l->isValid(); // has fix ? + int sats = l->satellitesCount(); + bool active = !strcmp(sensorGetCustomVar("gps"), "1"); + if (enabled) { + sprintf(reply, "on, %s, %s, %d sats", + active?"active":"deactivated", + fix?"fix":"no fix", + sats); + } else { + strcpy(reply, "off"); + } + } else { + strcpy(reply, "Can't find GPS"); + } #endif } else if (memcmp(command, "log start", 9) == 0) { _callbacks->setLoggingOn(true); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 08e5f988..d3e3a19d 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -2,6 +2,7 @@ #include "Mesh.h" #include +#include struct NodePrefs { // persisted to file float airtime_factor; @@ -50,10 +51,6 @@ public: virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; - virtual void gpsGetStatus(char * reply) {} - virtual void gpsStart() {} - virtual void gpsStop() {} - virtual void gpsSyncTime() {} }; class CommonCLI { @@ -67,6 +64,9 @@ class CommonCLI { void savePrefs(); void loadPrefsInt(FILESYSTEM* _fs, const char* filename); + const char* sensorGetCustomVar(const char* key); + bool sensorSetCustomVar(const char* key, const char* value); + public: CommonCLI(mesh::MainBoard& board, mesh::RTCClock& rtc, NodePrefs* prefs, CommonCLICallbacks* callbacks) : _board(&board), _rtc(&rtc), _prefs(prefs), _callbacks(callbacks) { } From fb46e5cc8a228e8cea70013de6a0d692a463038f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 6 Oct 2025 12:57:04 +0100 Subject: [PATCH 020/115] Refactor debug logging across bridge implementations --- examples/simple_repeater/MyMesh.h | 13 +++++-- src/MeshCore.h | 6 ++++ src/helpers/AbstractBridge.h | 2 +- src/helpers/bridges/BridgeBase.cpp | 4 +-- src/helpers/bridges/BridgeBase.h | 2 +- src/helpers/bridges/ESPNowBridge.cpp | 51 ++++++++++------------------ src/helpers/bridges/RS232Bridge.cpp | 40 ++++++---------------- 7 files changed, 48 insertions(+), 70 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 2e84843a..1dc5a76a 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -185,12 +185,19 @@ public: #if defined(WITH_BRIDGE) void setBridgeState(bool enable) override { - if (enable == bridge.getState()) return; - enable ? bridge.begin() : bridge.end(); + if (enable == bridge.isRunning()) return; + if (enable) + { + bridge.begin(); + } + else + { + bridge.end(); + } } void restartBridge() override { - if (!bridge.getState()) return; + if (!bridge.isRunning()) return; bridge.end(); bridge.begin(); } diff --git a/src/MeshCore.h b/src/MeshCore.h index d8886136..5c7e1760 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -28,6 +28,12 @@ #define MESH_DEBUG_PRINTLN(...) {} #endif +#if BRIDGE_DEBUG && ARDUINO +#define BRIDGE_DEBUG_PRINTLN(F, ...) Serial.printf("%s BRIDGE: " F, getLogDateTime(), ##__VA_ARGS__) +#else +#define BRIDGE_DEBUG_PRINTLN(...) {} +#endif + namespace mesh { #define BD_STARTUP_NORMAL 0 // getStartupReason() codes diff --git a/src/helpers/AbstractBridge.h b/src/helpers/AbstractBridge.h index 89e2dcdd..62284bd5 100644 --- a/src/helpers/AbstractBridge.h +++ b/src/helpers/AbstractBridge.h @@ -21,7 +21,7 @@ public: * * @return true if the bridge is initialized and running, false otherwise. */ - virtual bool getState() const = 0; + virtual bool isRunning() const = 0; /** * @brief A method to be called on every main loop iteration. diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp index 527ec358..d2e2e5e0 100644 --- a/src/helpers/bridges/BridgeBase.cpp +++ b/src/helpers/bridges/BridgeBase.cpp @@ -2,7 +2,7 @@ #include -bool BridgeBase::getState() const { +bool BridgeBase::isRunning() const { return _initialized; } @@ -34,7 +34,7 @@ bool BridgeBase::validateChecksum(const uint8_t *data, size_t len, uint16_t rece void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { // Guard against uninitialized state if (_initialized == false) { - Serial.printf("%s: BRIDGE: RX packet received before initialization\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("RX packet received before initialization\n"); _mgr->free(packet); return; } diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index 20b136e2..04c1564b 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -27,7 +27,7 @@ public: * * @return true if the bridge is initialized and running, false otherwise. */ - bool getState() const override; + bool isRunning() const override; /** * @brief Common magic number used by all bridge implementations for packet identification diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index a8a6fb53..b9eb1c10 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -27,20 +27,20 @@ ESPNowBridge::ESPNowBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTC } void ESPNowBridge::begin() { - Serial.printf("%s: ESPNOW BRIDGE: Initializing...\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Initializing...\n"); // Initialize WiFi in station mode WiFi.mode(WIFI_STA); // Set wifi channel if (esp_wifi_set_channel(_prefs->bridge_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Error setting WIFI channel to %d\n", getLogDateTime(), _prefs->bridge_channel); + BRIDGE_DEBUG_PRINTLN("Error setting WIFI channel to %d\n", _prefs->bridge_channel); return; } // Initialize ESP-NOW if (esp_now_init() != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Error initializing ESP-NOW\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Error initializing ESP-NOW\n"); return; } @@ -56,7 +56,7 @@ void ESPNowBridge::begin() { peerInfo.encrypt = false; if (esp_now_add_peer(&peerInfo) != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Failed to add broadcast peer\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Failed to add broadcast peer\n"); return; } @@ -65,12 +65,12 @@ void ESPNowBridge::begin() { } void ESPNowBridge::end() { - Serial.printf("%s: ESPNOW BRIDGE: Stopping...\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Stopping...\n"); // Remove broadcast peer uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; if (esp_now_del_peer(broadcastAddress) != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Error removing broadcast peer\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Error removing broadcast peer\n"); } // Unregister callbacks @@ -79,7 +79,7 @@ void ESPNowBridge::end() { // Deinitialize ESP-NOW if (esp_now_deinit() != ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: Error deinitializing ESP-NOW\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Error deinitializing ESP-NOW\n"); } // Turn off WiFi @@ -103,26 +103,20 @@ void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len) { // Ignore packets that are too small to contain header + checksum if (len < (BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE)) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX packet too small, len=%d\n", getLogDateTime(), len); -#endif + BRIDGE_DEBUG_PRINTLN("RX packet too small, len=%d\n", len); return; } // Validate total packet size if (len > MAX_ESPNOW_PACKET_SIZE) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX packet too large, len=%d\n", getLogDateTime(), len); -#endif + BRIDGE_DEBUG_PRINTLN("RX packet too large, len=%d\n", len); return; } // Check packet header magic uint16_t received_magic = (data[0] << 8) | data[1]; if (received_magic != BRIDGE_PACKET_MAGIC) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX invalid magic 0x%04X\n", getLogDateTime(), received_magic); -#endif + BRIDGE_DEBUG_PRINTLN("RX invalid magic 0x%04X\n", received_magic); return; } @@ -140,16 +134,11 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t l if (!validateChecksum(decrypted + BRIDGE_CHECKSUM_SIZE, payloadLen, received_checksum)) { // Failed to decrypt - likely from a different network -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX checksum mismatch, rcv=0x%04X\n", getLogDateTime(), - received_checksum); -#endif + BRIDGE_DEBUG_PRINTLN("RX checksum mismatch, rcv=0x%04X\n", received_checksum); return; } -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX, payload_len=%d\n", getLogDateTime(), payloadLen); -#endif + BRIDGE_DEBUG_PRINTLN("RX, payload_len=%d\n", payloadLen); // Create mesh packet mesh::Packet *pkt = _instance->_mgr->allocNew(); @@ -174,9 +163,7 @@ void ESPNowBridge::sendPacket(mesh::Packet *packet) { // First validate the packet pointer if (!packet) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: TX invalid packet pointer\n", getLogDateTime()); -#endif + BRIDGE_DEBUG_PRINTLN("TX invalid packet pointer\n"); return; } @@ -187,10 +174,8 @@ void ESPNowBridge::sendPacket(mesh::Packet *packet) { // Check if packet fits within our maximum payload size if (meshPacketLen > MAX_PAYLOAD_SIZE) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), - meshPacketLen, MAX_PAYLOAD_SIZE); -#endif + BRIDGE_DEBUG_PRINTLN("TX packet too large (payload=%d, max=%d)\n", meshPacketLen, + MAX_PAYLOAD_SIZE); return; } @@ -219,13 +204,11 @@ void ESPNowBridge::sendPacket(mesh::Packet *packet) { uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; esp_err_t result = esp_now_send(broadcastAddress, buffer, totalPacketSize); -#if MESH_PACKET_LOGGING if (result == ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: TX, len=%d\n", getLogDateTime(), meshPacketLen); + BRIDGE_DEBUG_PRINTLN("TX, len=%d\n", meshPacketLen); } else { - Serial.printf("%s: ESPNOW BRIDGE: TX FAILED!\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("TX FAILED!\n"); } -#endif } } diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 70f4b7d8..554e8fcc 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -8,7 +8,7 @@ RS232Bridge::RS232Bridge(NodePrefs *prefs, Stream &serial, mesh::PacketManager * : BridgeBase(prefs, mgr, rtc), _serial(&serial) {} void RS232Bridge::begin() { - Serial.printf("%s: RS232 BRIDGE: Initializing at %d baud...\n", getLogDateTime(), _prefs->bridge_baud); + BRIDGE_DEBUG_PRINTLN("Initializing at %d baud...\n", _prefs->bridge_baud); #if !defined(WITH_RS232_BRIDGE_RX) || !defined(WITH_RS232_BRIDGE_TX) #error "WITH_RS232_BRIDGE_RX and WITH_RS232_BRIDGE_TX must be defined" #endif @@ -33,7 +33,7 @@ void RS232Bridge::begin() { } void RS232Bridge::end() { - Serial.printf("%s: RS232 BRIDGE: Stopping...\n", getLogDateTime()); + BRIDGE_DEBUG_PRINTLN("Stopping...\n"); ((HardwareSerial *)_serial)->end(); // Update bridge state @@ -71,9 +71,7 @@ void RS232Bridge::loop() { // Validate length field if (len > (MAX_TRANS_UNIT + 1)) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: RX invalid length %d, resetting\n", getLogDateTime(), len); -#endif + BRIDGE_DEBUG_PRINTLN("RX invalid length %d, resetting\n", len); _rx_buffer_pos = 0; // Invalid length, reset continue; } @@ -82,30 +80,20 @@ void RS232Bridge::loop() { uint16_t received_checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; if (validateChecksum(_rx_buffer + 4, len, received_checksum)) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, - received_checksum); -#endif + BRIDGE_DEBUG_PRINTLN("RX, len=%d crc=0x%04x\n", len, received_checksum); mesh::Packet *pkt = _mgr->allocNew(); if (pkt) { if (pkt->readFrom(_rx_buffer + 4, len)) { onPacketReceived(pkt); } else { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: RX failed to parse packet\n", getLogDateTime()); -#endif + BRIDGE_DEBUG_PRINTLN("RX failed to parse packet\n"); _mgr->free(pkt); } } else { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: RX failed to allocate packet\n", getLogDateTime()); -#endif + BRIDGE_DEBUG_PRINTLN("RX failed to allocate packet\n"); } } else { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: RX checksum mismatch, rcv=0x%04x\n", getLogDateTime(), - received_checksum); -#endif + BRIDGE_DEBUG_PRINTLN("RX checksum mismatch, rcv=0x%04x\n", received_checksum); } _rx_buffer_pos = 0; // Reset for next packet } @@ -122,9 +110,7 @@ void RS232Bridge::sendPacket(mesh::Packet *packet) { // First validate the packet pointer if (!packet) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX invalid packet pointer\n", getLogDateTime()); -#endif + BRIDGE_DEBUG_PRINTLN("TX invalid packet pointer\n"); return; } @@ -135,10 +121,8 @@ void RS232Bridge::sendPacket(mesh::Packet *packet) { // Check if packet fits within our maximum payload size if (len > (MAX_TRANS_UNIT + 1)) { -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), len, - MAX_TRANS_UNIT + 1); -#endif + BRIDGE_DEBUG_PRINTLN("TX packet too large (payload=%d, max=%d)\n", len, + MAX_TRANS_UNIT + 1); return; } @@ -156,9 +140,7 @@ void RS232Bridge::sendPacket(mesh::Packet *packet) { // Send complete packet _serial->write(buffer, len + SERIAL_OVERHEAD); -#if MESH_PACKET_LOGGING - Serial.printf("%s: RS232 BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); -#endif + BRIDGE_DEBUG_PRINTLN("TX, len=%d crc=0x%04x\n", len, checksum); } } From 13a0202062c403731dee000392b97556671f96a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 6 Oct 2025 12:57:32 +0100 Subject: [PATCH 021/115] Add BRIDGE_DEBUG flag --- variants/generic-e22/platformio.ini | 4 ++++ variants/heltec_ct62/platformio.ini | 2 ++ variants/heltec_e213/platformio.ini | 2 ++ variants/heltec_e290/platformio.ini | 2 ++ variants/heltec_t190/platformio.ini | 2 ++ variants/heltec_tracker/platformio.ini | 2 ++ variants/heltec_v2/platformio.ini | 2 ++ variants/heltec_v3/platformio.ini | 4 ++++ variants/heltec_v4/platformio.ini | 1 + variants/heltec_wireless_paper/platformio.ini | 2 ++ variants/lilygo_t3s3/platformio.ini | 2 ++ variants/lilygo_t3s3_sx1276/platformio.ini | 2 ++ variants/lilygo_tbeam_SX1262/platformio.ini | 2 ++ variants/lilygo_tbeam_SX1276/platformio.ini | 2 ++ variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 2 ++ variants/lilygo_tlora_v2_1/platformio.ini | 2 ++ variants/meshadventurer/platformio.ini | 4 ++++ variants/station_g2/platformio.ini | 4 ++++ variants/tenstar_c3/platformio.ini | 4 ++++ variants/waveshare_rp2040_lora/platformio.ini | 1 + variants/xiao_s3_wio/platformio.ini | 2 ++ 21 files changed, 50 insertions(+) diff --git a/variants/generic-e22/platformio.ini b/variants/generic-e22/platformio.ini index 9e4bcf72..5a544747 100644 --- a/variants/generic-e22/platformio.ini +++ b/variants/generic-e22/platformio.ini @@ -65,6 +65,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -87,6 +88,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -131,6 +133,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -153,6 +156,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 9dc67334..ff8d59db 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -61,6 +61,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_ct62.build_src_filter} @@ -80,6 +81,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_ct62.build_src_filter} diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index 0d63b7e4..ca7a601f 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -106,6 +106,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_E213_base.build_src_filter} @@ -127,6 +128,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_E213_base.build_src_filter} diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 63703d7c..e5046854 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -102,6 +102,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_E290_base.build_src_filter} @@ -123,6 +124,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_E290_base.build_src_filter} diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index 7ab4da55..b4fe2a8a 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -106,6 +106,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_T190_base.build_src_filter} @@ -125,6 +126,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_T190_base.build_src_filter} diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index d2f7f6b1..c3f52739 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -94,6 +94,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_tracker_base.build_src_filter} @@ -116,6 +117,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_tracker_base.build_src_filter} diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 049b83bb..025f9b4d 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -53,6 +53,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_lora32_v2.build_src_filter} @@ -75,6 +76,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v2.build_src_filter} diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 26b7754e..8ba0be70 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -62,6 +62,7 @@ build_flags = -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=5 -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -83,6 +84,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -242,6 +244,7 @@ build_flags = -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=5 -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -262,6 +265,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 72fbfea9..609ef5e6 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -77,6 +77,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v4.build_src_filter} diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index 9f125d75..507b3790 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -81,6 +81,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} @@ -102,6 +103,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index 3686ba2b..507c1dfe 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -65,6 +65,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} @@ -86,6 +87,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index e7d22602..54545f52 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -63,6 +63,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} @@ -84,6 +85,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 70aed341..2a61165b 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -86,6 +86,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} @@ -105,6 +106,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 512083ff..806ca8cb 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -85,6 +85,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} @@ -105,6 +106,7 @@ build_flags = -D MAX_NEIGHBOURS=8 -D PERSISTANT_GPS=1 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 7a2b8daa..21bd2520 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -64,6 +64,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} @@ -83,6 +84,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index a9ad946b..ddcb1b10 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -176,6 +176,7 @@ build_flags = -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=34 -D WITH_RS232_BRIDGE_TX=25 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 @@ -197,6 +198,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini index fe3c7b77..c051767f 100644 --- a/variants/meshadventurer/platformio.ini +++ b/variants/meshadventurer/platformio.ini @@ -73,6 +73,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -96,6 +97,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -142,6 +144,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -165,6 +168,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 83813dc6..6e2d0f2c 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -57,6 +57,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Station_G2.build_src_filter} @@ -76,6 +77,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} @@ -118,6 +120,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Station_G2.build_src_filter} ; + @@ -138,6 +141,7 @@ build_flags = -D MESH_PACKET_LOGGING=1 -D SX126X_RX_BOOSTED_GAIN=1 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} + diff --git a/variants/tenstar_c3/platformio.ini b/variants/tenstar_c3/platformio.ini index 60014fc9..f4c743b6 100644 --- a/variants/tenstar_c3/platformio.ini +++ b/variants/tenstar_c3/platformio.ini @@ -63,6 +63,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -86,6 +87,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -130,6 +132,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -152,6 +155,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index f2f180aa..76758c81 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -57,6 +57,7 @@ build_flags = ${waveshare_rp2040_lora.build_flags} -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=9 -D WITH_RS232_BRIDGE_TX=8 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index f5713e42..86fde33c 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -59,6 +59,7 @@ lib_deps = ; -D WITH_RS232_BRIDGE=Serial2 ; -D WITH_RS232_BRIDGE_RX=5 ; -D WITH_RS232_BRIDGE_TX=6 +; -D BRIDGE_DEBUG=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; lib_deps = @@ -78,6 +79,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = From 341b69e3c949cf5c52d5f51f4668fe52ccfaf7a6 Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Mon, 6 Oct 2025 14:08:16 +0200 Subject: [PATCH 022/115] sensor list command --- src/helpers/CommonCLI.cpp | 27 ++++++++++++++++++- .../sensors/EnvironmentSensorManager.cpp | 25 ++++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index ee029ec3..e20de609 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -439,7 +439,32 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "ok"); } else { strcpy(reply, "can't find custom var"); - } + } + } else if (memcmp(command, "sensor list", 11) == 0) { + char* dp = reply; + int start = 0; + int end = sensors.getNumSettings(); + if (strlen(command) > 11) { + start = _atoi(command+12); + } + if (start >= end) { + strcpy(reply, "no custom var"); + } else { + sprintf(dp, "%d vars\n", end); + dp = strchr(dp, 0); + int i; + for (i = start; i < end && (dp-reply < 134); i++) { + sprintf(dp, "%s=%s\n", + sensors.getSettingName(i), + sensors.getSettingValue(i)); + dp = strchr(dp, 0); + } + if (i < end) { + sprintf(dp, "... next:%d", i); + } else { + *(dp-1) = 0; // remove last CR + } + } #if ENV_INCLUDE_GPS == 1 } else if (memcmp(command, "gps on", 6) == 0) { if (sensorSetCustomVar("gps", "1")) { diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 99605ff3..aa51c85a 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -387,27 +387,34 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen int EnvironmentSensorManager::getNumSettings() const { + int settings = 0; #if ENV_INCLUDE_GPS - return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected - #else - return 0; + if (gps_detected) settings++; // only show GPS setting if GPS is detected #endif + return settings; } const char* EnvironmentSensorManager::getSettingName(int i) const { + int settings = 0; #if ENV_INCLUDE_GPS - return (gps_detected && i == 0) ? "gps" : NULL; - #else - return NULL; + if (gps_detected && i == settings++) { + return "gps"; + } #endif + // convenient way to add params (needed for some tests) +// if (i == settings++) return "param.2"; + return NULL; } const char* EnvironmentSensorManager::getSettingValue(int i) const { + int settings = 0; #if ENV_INCLUDE_GPS - if (gps_detected && i == 0) { - return gps_active ? "1" : "0"; - } + if (gps_detected && i == settings++) { + return gps_active ? "1" : "0"; + } #endif + // convenient way to add params ... +// if (i == settings++) return "2"; return NULL; } From 6ed8e9d5142715b61a22fed6dc8627fb3b3476cc Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Mon, 6 Oct 2025 15:12:03 +0200 Subject: [PATCH 023/115] gps_cli: gps state is now saved and restored upon reboot --- examples/simple_repeater/MyMesh.cpp | 15 ++++++++ examples/simple_repeater/MyMesh.h | 4 +++ src/helpers/CommonCLI.cpp | 53 +++++++++++++---------------- src/helpers/CommonCLI.h | 6 ++-- src/helpers/SensorManager.h | 21 ++++++++++++ 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index b2b47ac3..1c6c0d77 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -631,7 +631,12 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.bridge_pkt_src = 0; // logTx _prefs.bridge_baud = 115200; // baud rate _prefs.bridge_channel = 1; // channel 1 + StrHelper::strncpy(_prefs.bridge_secret, "LVSITANOS", sizeof(_prefs.bridge_secret)); + + // GPS defaults + _prefs.gps_enabled = 0; + _prefs.gps_interval = 0; } void MyMesh::begin(FILESYSTEM *fs) { @@ -653,8 +658,18 @@ void MyMesh::begin(FILESYSTEM *fs) { updateAdvertTimer(); updateFloodAdvertTimer(); + +#if ENV_INCLUDE_GPS == 1 + applyGpsPrefs(); +#endif } +#if ENV_INCLUDE_GPS == 1 +void MyMesh::applyGpsPrefs() { + sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); +} +#endif + void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params pending_freq = freq; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index d77b74d8..ca85d1a4 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -159,6 +159,10 @@ public: _cli.savePrefs(_fs); } +#if ENV_INCLUDE_GPS == 1 + void applyGpsPrefs(); +#endif + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; bool formatFileSystem() override; void sendSelfAdvertisement(int delay_millis) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index e6f8a3a6..2efdd00a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -62,8 +62,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->bridge_delay, sizeof(_prefs->bridge_delay)); // 128 file.read((uint8_t *)&_prefs->bridge_pkt_src, sizeof(_prefs->bridge_pkt_src)); // 130 file.read((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131 - file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 132 - file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 133 + file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135 + file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136 + file.read(pad, 4); // 152 + file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 + file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 + // 161 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -84,6 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->bridge_baud = constrain(_prefs->bridge_baud, 9600, 115200); _prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14); + _prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1); + file.close(); } } @@ -131,8 +137,12 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->bridge_delay, sizeof(_prefs->bridge_delay)); // 128 file.write((uint8_t *)&_prefs->bridge_pkt_src, sizeof(_prefs->bridge_pkt_src)); // 130 file.write((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131 - file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 132 - file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 133 + file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135 + file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136 + file.write(pad, 4); // 152 + file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 + file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 + // 161 file.close(); } @@ -147,27 +157,6 @@ void CommonCLI::savePrefs() { _callbacks->savePrefs(); } -const char* CommonCLI::sensorGetCustomVar(const char* key) { - int num = sensors.getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(sensors.getSettingName(i), key) == 0) { - return sensors.getSettingValue(i); - } - } - return NULL; -} - -bool CommonCLI::sensorSetCustomVar(const char* key, const char* value) { - int num = sensors.getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(sensors.getSettingName(i), key) == 0) { - sensors.setSettingValue(key, value); - return true; - } - } - return false; -} - void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, char* reply) { if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return @@ -527,7 +516,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "%s", _board->getManufacturerName()); } else if (memcmp(command, "sensor get ", 11) == 0) { const char* key = command + 11; - const char* val = sensorGetCustomVar(key); + const char* val = sensors.getSettingByKey(key); if (val != NULL) { strcpy(reply, val); } else { @@ -538,7 +527,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch const char* value = strchr(args,' ') + 1; char key [value-args+1]; strncpy(key, args, value-args-1); - if (sensorSetCustomVar(key, value)) { + if (sensors.setSettingByKey(key, value)) { strcpy(reply, "ok"); } else { strcpy(reply, "can't find custom var"); @@ -570,13 +559,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } #if ENV_INCLUDE_GPS == 1 } else if (memcmp(command, "gps on", 6) == 0) { - if (sensorSetCustomVar("gps", "1")) { + if (sensors.setSettingByKey("gps", "1")) { + _prefs->gps_enabled = 1; + savePrefs(); strcpy(reply, "ok"); } else { strcpy(reply, "gps toggle not found"); } } else if (memcmp(command, "gps off", 7) == 0) { - if (sensorSetCustomVar("gps", "0")) { + if (sensors.setSettingByKey("gps", "0")) { + _prefs->gps_enabled = 0; + savePrefs(); strcpy(reply, "ok"); } else { strcpy(reply, "gps toggle not found"); @@ -592,7 +585,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch bool enabled = l->isEnabled(); // is EN pin on ? bool fix = l->isValid(); // has fix ? int sats = l->satellitesCount(); - bool active = !strcmp(sensorGetCustomVar("gps"), "1"); + bool active = !strcmp(sensors.getSettingByKey("gps"), "1"); if (enabled) { sprintf(reply, "on, %s, %s, %d sats", active?"active":"deactivated", diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 14e249de..07523643 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -38,6 +38,9 @@ struct NodePrefs { // persisted to file uint32_t bridge_baud; // 9600, 19200, 38400, 57600, 115200 (default 115200) uint8_t bridge_channel; // 1-14 (ESP-NOW only) char bridge_secret[16]; // for XOR encryption of bridge packets (ESP-NOW only) + // Gps settings + uint8_t gps_enabled; + uint32_t gps_interval; // in seconds }; class CommonCLICallbacks { @@ -83,9 +86,6 @@ class CommonCLI { void savePrefs(); void loadPrefsInt(FILESYSTEM* _fs, const char* filename); - const char* sensorGetCustomVar(const char* key); - bool sensorSetCustomVar(const char* key, const char* value); - public: CommonCLI(mesh::MainBoard& board, mesh::RTCClock& rtc, NodePrefs* prefs, CommonCLICallbacks* callbacks) : _board(&board), _rtc(&rtc), _prefs(prefs), _callbacks(callbacks) { } diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 1ace6220..38c1d806 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -23,4 +23,25 @@ public: virtual const char* getSettingValue(int i) const { return NULL; } virtual bool setSettingValue(const char* name, const char* value) { return false; } virtual LocationProvider* getLocationProvider() { return NULL; } + + // Helper functions to manage setting by keys (useful in many places ...) + const char* getSettingByKey(const char* key) { + int num = getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(getSettingName(i), key) == 0) { + return getSettingValue(i); + } + } + return NULL; + } + + bool setSettingByKey(const char* key, const char* value) { + int num = getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(getSettingName(i), key) == 0) { + return setSettingValue(key, value); + } + } + return false; + } }; From 9e3c2fc9d9f75cdcda0106529d5cae51eecfab5c Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Mon, 6 Oct 2025 15:30:18 +0200 Subject: [PATCH 024/115] gps_cli: gps also restored on sensors and rooms --- examples/simple_repeater/MyMesh.cpp | 6 ------ examples/simple_repeater/MyMesh.h | 10 ++++++---- examples/simple_room_server/MyMesh.cpp | 4 ++++ examples/simple_room_server/MyMesh.h | 6 ++++++ examples/simple_sensor/SensorMesh.cpp | 4 ++++ examples/simple_sensor/SensorMesh.h | 5 +++++ 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 1c6c0d77..f7153da3 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -664,12 +664,6 @@ void MyMesh::begin(FILESYSTEM *fs) { #endif } -#if ENV_INCLUDE_GPS == 1 -void MyMesh::applyGpsPrefs() { - sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); -} -#endif - void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params pending_freq = freq; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index ca85d1a4..c45c141d 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -135,6 +135,12 @@ protected: return _prefs.multi_acks; } +#if ENV_INCLUDE_GPS == 1 + void applyGpsPrefs() { + sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + } +#endif + void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override; @@ -159,10 +165,6 @@ public: _cli.savePrefs(_fs); } -#if ENV_INCLUDE_GPS == 1 - void applyGpsPrefs(); -#endif - void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; bool formatFileSystem() override; void sendSelfAdvertisement(int delay_millis) override; diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 89f2afb3..d9a36397 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -632,6 +632,10 @@ void MyMesh::begin(FILESYSTEM *fs) { updateAdvertTimer(); updateFloodAdvertTimer(); + +#if ENV_INCLUDE_GPS == 1 + applyGpsPrefs(); +#endif } void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index b2df60c3..60ef1e73 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -149,6 +149,12 @@ protected: bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override; +#if ENV_INCLUDE_GPS == 1 + void applyGpsPrefs() { + sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + } +#endif + public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index ba41ca45..00da006a 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -697,6 +697,10 @@ void SensorMesh::begin(FILESYSTEM* fs) { updateAdvertTimer(); updateFloodAdvertTimer(); + +#if ENV_INCLUDE_GPS == 1 + applyGpsPrefs(); +#endif } bool SensorMesh::formatFileSystem() { diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index d26bcb14..cdc3940c 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -149,4 +149,9 @@ private: void sendAlert(const ClientInfo* c, Trigger* t); + #if ENV_INCLUDE_GPS == 1 + void applyGpsPrefs() { + sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + } +#endif }; From 601479e572eba359a985f8941fe84bd864b154c2 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Tue, 7 Oct 2025 11:12:37 +0200 Subject: [PATCH 025/115] Introduce Heltec_WSL3_companion_radio_wifi target --- variants/heltec_v3/platformio.ini | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index aa79e20e..b43c0870 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -325,6 +325,24 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_WSL3_companion_radio_wifi] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_WSL3_sensor] extends = Heltec_lora32_v3 build_flags = From da7b8ad669395a073ec8da289fac6f77b7ef78b2 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Thu, 9 Oct 2025 20:30:25 +0200 Subject: [PATCH 026/115] Add powerOff support for MeshPocket --- variants/mesh_pocket/MeshPocket.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/mesh_pocket/MeshPocket.h b/variants/mesh_pocket/MeshPocket.h index 82f66dd5..8f5b09c9 100644 --- a/variants/mesh_pocket/MeshPocket.h +++ b/variants/mesh_pocket/MeshPocket.h @@ -41,5 +41,9 @@ public: NVIC_SystemReset(); } + void powerOff() override { + sd_power_system_off(); + } + bool startOTAUpdate(const char* id, char reply[]) override; }; From b588e3f1e3b8a75b1519c4f640cbe40f5587e9df Mon Sep 17 00:00:00 2001 From: Bill Plein <260078+bplein@users.noreply.github.com> Date: Thu, 9 Oct 2025 17:31:32 -0500 Subject: [PATCH 027/115] Ikoka Nano Variant Created as a fork of the ikoka stick variant. - Updated for Ikoka Nano legacy pinout - Removed display support - Removed user button support - Retains I2C sensor support Tested with the ebytes E22 30W module, companion-ble and repeater firmware versions, with an I2C INA3221 power sensor. --- variants/ikoka_nano_nrf/IkokaNanoNRFBoard.cpp | 94 ++++++ variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h | 60 ++++ variants/ikoka_nano_nrf/platformio.ini | 312 ++++++++++++++++++ variants/ikoka_nano_nrf/target.cpp | 44 +++ variants/ikoka_nano_nrf/target.h | 28 ++ variants/ikoka_nano_nrf/variant.cpp | 86 +++++ variants/ikoka_nano_nrf/variant.h | 149 +++++++++ 7 files changed, 773 insertions(+) create mode 100644 variants/ikoka_nano_nrf/IkokaNanoNRFBoard.cpp create mode 100644 variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h create mode 100644 variants/ikoka_nano_nrf/platformio.ini create mode 100644 variants/ikoka_nano_nrf/target.cpp create mode 100644 variants/ikoka_nano_nrf/target.h create mode 100644 variants/ikoka_nano_nrf/variant.cpp create mode 100644 variants/ikoka_nano_nrf/variant.h diff --git a/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.cpp b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.cpp new file mode 100644 index 00000000..ee799692 --- /dev/null +++ b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.cpp @@ -0,0 +1,94 @@ +#ifdef XIAO_NRF52 + +#include +#include "IkokaNanoNRFBoard.h" + +#include +#include + +static BLEDfu bledfu; + +static void connect_callback(uint16_t conn_handle) { + (void)conn_handle; + MESH_DEBUG_PRINTLN("BLE client connected"); +} + +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { + (void)conn_handle; + (void)reason; + + MESH_DEBUG_PRINTLN("BLE client disconnected"); +} + +void IkokaNanoNRFBoard::begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + + pinMode(PIN_VBAT, INPUT); + pinMode(VBAT_ENABLE, OUTPUT); + digitalWrite(VBAT_ENABLE, HIGH); + +#ifdef PIN_USER_BTN + pinMode(PIN_USER_BTN, INPUT_PULLUP); +#endif + +#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) + Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL); +#endif + + Wire.begin(); + +#ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, HIGH); +#endif + +// pinMode(SX126X_POWER_EN, OUTPUT); +// digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} + +bool IkokaNanoNRFBoard::startOTAUpdate(const char *id, char reply[]) { + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4 + Bluefruit.setTxPower(4); + // Set the BLE device name + Bluefruit.setName("XIAO_NRF52_OTA"); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + + // Set up and start advertising + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + + /* Start Advertising + - Enable auto advertising if disconnected + - Interval: fast mode = 20 ms, slow mode = 152.5 ms + - Timeout for fast mode is 30 seconds + - Start(timeout) with timeout = 0 will advertise forever (until connected) + + For recommended advertising interval + https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + + strcpy(reply, "OK - started"); + return true; +} + +#endif diff --git a/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h new file mode 100644 index 00000000..8484085b --- /dev/null +++ b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#ifdef XIAO_NRF52 + +class IkokaNanoNRFBoard : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin(); + uint8_t getStartupReason() const override { return startup_reason; } + +#if defined(P_LORA_TX_LED) + void onBeforeTransmit() override { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + #if defined(LED_BLUE) + // turn off that annoying blue LED before transmitting + digitalWrite(LED_BLUE, HIGH); + #endif + } + void onAfterTransmit() override { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + #if defined(LED_BLUE) + // do it after transmitting too, just in case + digitalWrite(LED_BLUE, HIGH); + #endif + } +#endif + + uint16_t getBattMilliVolts() override { + // Please read befor going further ;) + // https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging + + // We can't drive VBAT_ENABLE to HIGH as long + // as we don't know wether we are charging or not ... + // this is a 3mA loss (4/1500) + digitalWrite(VBAT_ENABLE, LOW); + int adcvalue = 0; + analogReadResolution(12); + analogReference(AR_INTERNAL_3_0); + delay(10); + adcvalue = analogRead(PIN_VBAT); + return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096; + } + + const char *getManufacturerName() const override { + return MANUFACTURER_STRING; + } + + void reboot() override { + NVIC_SystemReset(); + } + + bool startOTAUpdate(const char *id, char reply[]) override; +}; + +#endif diff --git a/variants/ikoka_nano_nrf/platformio.ini b/variants/ikoka_nano_nrf/platformio.ini new file mode 100644 index 00000000..abfbcf67 --- /dev/null +++ b/variants/ikoka_nano_nrf/platformio.ini @@ -0,0 +1,312 @@ +[nrf52840_xiao] +extends = nrf52_base +platform_packages = + toolchain-gccarmnoneeabi@~1.100301.0 + framework-arduinoadafruitnrf52 +board = seeed-xiao-afruitnrf52-nrf52840 +board_build.ldscript = boards/nrf52840_s140_v7.ld +build_flags = ${nrf52_base.build_flags} + -D NRF52_PLATFORM -D XIAO_NRF52 + -I lib/nrf52/s140_nrf52_7.3.0_API/include + -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 +lib_ignore = + BluetoothOTA + lvgl + lib5b4 +lib_deps = + ${nrf52_base.lib_deps} + rweather/Crypto @ ^0.4.0 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[ikoka_nano_nrf_baseboard] +extends = nrf52840_xiao +;board_build.ldscript = boards/nrf52840_s140_v7.ld +build_flags = ${nrf52840_xiao.build_flags} + -D P_LORA_TX_LED=11 + -I variants/ikoka_nano_nrf + -I src/helpers/nrf52 + -D DISPLAY_CLASS=NullDisplayDriver + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D P_LORA_DIO_1=D1 +; -D P_LORA_BUSY=D3 + -D P_LORA_BUSY=D2 ; specific to ikoka nano variant. +; -D P_LORA_RESET=D2 + -D P_LORA_RESET=D3 ; specific to ikoka nano variant. +; -D P_LORA_NSS=D4 + -D P_LORA_NSS=D0 ; specific to ikoka nano variant. +; -D SX126X_RXEN=D5 + -D SX126X_RXEN=D7 + -D SX126X_TXEN=RADIOLIB_NC + -D SX126X_DIO2_AS_RF_SWITCH=1 + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_WIRE_SCL=5 ; specific to ikoka nano variant. + -D PIN_WIRE_SDA=4 ; specific to ikoka nano variant. + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_INA3221=1 + -D ENV_INCLUDE_INA219=1 +debug_tool = jlink +upload_protocol = nrfutil + + +;;; abstracted hardware variants + +[ikoka_nano_nrf_e22_22dbm] +extends = ikoka_nano_nrf_baseboard +; No PA in this model, full 22dBm +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Nano-E22-22dBm (Xiao_nrf52)"' + -D LORA_TX_POWER=22 +build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + + + +<../variants/ikoka_nano_nrf> + +[ikoka_nano_nrf_e22_30dbm] +extends = ikoka_nano_nrf_baseboard +; limit txpower to 20dBm on E22-900M30S. Anything higher will +; cause distortion in the PA output. 20dBm in -> 30dBm out +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Nano-E22-30dBm (Xiao_nrf52)"' + -D LORA_TX_POWER=20 +build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + + + +<../variants/ikoka_nano_nrf> + +[ikoka_nano_nrf_e22_33dbm] +extends = ikoka_nano_nrf_baseboard +; limit txpower to 9dBm on E22-900M33S to avoid hardware damage +; to the rf amplifier frontend. 9dBm in -> 33dBm out +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D MANUFACTURER_STRING='"Ikoka Nano-E22-33dBm (Xiao_nrf52)"' + -D LORA_TX_POWER=9 +build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + + + +<../variants/ikoka_nano_nrf> + +;;; abstracted firmware roles + +[ikoka_nano_nrf_companion_radio_ble] +extends = ikoka_nano_nrf_baseboard +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld +board_upload.maximum_size = 708608 +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -I examples/companion_radio/ui-new +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${ikoka_nano_nrf_baseboard.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[ikoka_nano_nrf_companion_radio_usb] +extends = ikoka_nano_nrf_baseboard +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld +board_upload.maximum_size = 708608 +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -I examples/companion_radio/ui-new +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${ikoka_nano_nrf_baseboard.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[ikoka_nano_nrf_repeater] +extends = ikoka_nano_nrf_baseboard +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D ADVERT_NAME='"Ikoka Nano Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} + +<../examples/simple_repeater/*.cpp> + +[ikoka_nano_nrf_room_server] +extends = ikoka_nano_nrf_baseboard +build_flags = + ${ikoka_nano_nrf_baseboard.build_flags} + -D ADVERT_NAME='"Ikoka Nano Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} + +<../examples/simple_room_server/*.cpp> + +;;; hardware + firmware variants + +;;; 22dBm EBYTE E22-900M22 variants + +[env:ikoka_nano_nrf_22dbm_companion_radio_usb] +extends = + ikoka_nano_nrf_e22_22dbm + ikoka_nano_nrf_companion_radio_usb +build_flags = + ${ikoka_nano_nrf_companion_radio_usb.build_flags} + ${ikoka_nano_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_usb.build_src_filter} + ${ikoka_nano_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_nano_nrf_22dbm_companion_radio_ble] +extends = + ikoka_nano_nrf_e22_22dbm + ikoka_nano_nrf_companion_radio_ble +build_flags = + ${ikoka_nano_nrf_companion_radio_ble.build_flags} + ${ikoka_nano_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_ble.build_src_filter} + ${ikoka_nano_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_nano_nrf_22dbm_repeater] +extends = + ikoka_nano_nrf_e22_22dbm + ikoka_nano_nrf_repeater +build_flags = + ${ikoka_nano_nrf_repeater.build_flags} + ${ikoka_nano_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_repeater.build_src_filter} + ${ikoka_nano_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_nano_nrf_22dbm_room_server] +extends = + ikoka_nano_nrf_e22_22dbm + ikoka_nano_nrf_room_server +build_flags = + ${ikoka_nano_nrf_room_server.build_flags} + ${ikoka_nano_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_room_server.build_src_filter} + ${ikoka_nano_nrf_e22_22dbm.build_src_filter} + + +;;; 30dBm EBYTE E22-900M30 variants + +[env:ikoka_nano_nrf_30dbm_companion_radio_usb] +extends = + ikoka_nano_nrf_e22_30dbm + ikoka_nano_nrf_companion_radio_usb +build_flags = + ${ikoka_nano_nrf_companion_radio_usb.build_flags} + ${ikoka_nano_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_usb.build_src_filter} + ${ikoka_nano_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_nano_nrf_30dbm_companion_radio_ble] +extends = + ikoka_nano_nrf_e22_30dbm + ikoka_nano_nrf_companion_radio_ble +build_flags = + ${ikoka_nano_nrf_companion_radio_ble.build_flags} + ${ikoka_nano_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_ble.build_src_filter} + ${ikoka_nano_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_nano_nrf_30dbm_repeater] +extends = + ikoka_nano_nrf_e22_30dbm + ikoka_nano_nrf_repeater +build_flags = + ${ikoka_nano_nrf_repeater.build_flags} + ${ikoka_nano_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_repeater.build_src_filter} + ${ikoka_nano_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_nano_nrf_30dbm_room_server] +extends = + ikoka_nano_nrf_e22_30dbm + ikoka_nano_nrf_room_server +build_flags = + ${ikoka_nano_nrf_room_server.build_flags} + ${ikoka_nano_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_room_server.build_src_filter} + ${ikoka_nano_nrf_e22_30dbm.build_src_filter} + + +;;; 33dBm EBYTE E22-900M33 variants + +[env:ikoka_nano_nrf_33dbm_companion_radio_usb] +extends = + ikoka_nano_nrf_e22_33dbm + ikoka_nano_nrf_companion_radio_usb +build_flags = + ${ikoka_nano_nrf_companion_radio_usb.build_flags} + ${ikoka_nano_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_usb.build_src_filter} + ${ikoka_nano_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_nano_nrf_33dbm_companion_radio_ble] +extends = + ikoka_nano_nrf_e22_33dbm + ikoka_nano_nrf_companion_radio_ble +build_flags = + ${ikoka_nano_nrf_companion_radio_ble.build_flags} + ${ikoka_nano_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_companion_radio_ble.build_src_filter} + ${ikoka_nano_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_nano_nrf_33dbm_repeater] +extends = + ikoka_nano_nrf_e22_33dbm + ikoka_nano_nrf_repeater +build_flags = + ${ikoka_nano_nrf_repeater.build_flags} + ${ikoka_nano_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_repeater.build_src_filter} + ${ikoka_nano_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_nano_nrf_33dbm_room_server] +extends = + ikoka_nano_nrf_e22_33dbm + ikoka_nano_nrf_room_server +build_flags = + ${ikoka_nano_nrf_room_server.build_flags} + ${ikoka_nano_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_nano_nrf_room_server.build_src_filter} + ${ikoka_nano_nrf_e22_33dbm.build_src_filter} diff --git a/variants/ikoka_nano_nrf/target.cpp b/variants/ikoka_nano_nrf/target.cpp new file mode 100644 index 00000000..aed59182 --- /dev/null +++ b/variants/ikoka_nano_nrf/target.cpp @@ -0,0 +1,44 @@ +#include +#include "target.h" +#include + +IkokaNanoNRFBoard board; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + // MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +EnvironmentSensorManager sensors; + +bool radio_init() { + rtc_clock.begin(Wire); + + return radio.std_init(&SPI); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/ikoka_nano_nrf/target.h b/variants/ikoka_nano_nrf/target.h new file mode 100644 index 00000000..9b4e908e --- /dev/null +++ b/variants/ikoka_nano_nrf/target.h @@ -0,0 +1,28 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + #include + extern DISPLAY_CLASS display; + // extern MomentaryButton user_btn; +#endif + +extern IkokaNanoNRFBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/ikoka_nano_nrf/variant.cpp b/variants/ikoka_nano_nrf/variant.cpp new file mode 100644 index 00000000..16542e27 --- /dev/null +++ b/variants/ikoka_nano_nrf/variant.cpp @@ -0,0 +1,86 @@ +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // D0 .. D10 + 2, // D0 is P0.02 (A0) + 3, // D1 is P0.03 (A1) + 28, // D2 is P0.28 (A2) + 29, // D3 is P0.29 (A3) + 4, // D4 is P0.04 (A4,SDA) + 5, // D5 is P0.05 (A5,SCL) + 43, // D6 is P1.11 (TX) + 44, // D7 is P1.12 (RX) + 45, // D8 is P1.13 (SCK) + 46, // D9 is P1.14 (MISO) + 47, // D10 is P1.15 (MOSI) + + // LEDs + 26, // D11 is P0.26 (LED RED) + 6, // D12 is P0.06 (LED BLUE) + 30, // D13 is P0.30 (LED GREEN) + 14, // D14 is P0.14 (READ_BAT) + + // LSM6DS3TR + 40, // D15 is P1.08 (6D_PWR) + 27, // D16 is P0.27 (6D_I2C_SCL) + 7, // D17 is P0.07 (6D_I2C_SDA) + 11, // D18 is P0.11 (6D_INT1) + + // MIC + 42, // D19 is P1.10 (MIC_PWR) + 32, // D20 is P1.00 (PDM_CLK) + 16, // D21 is P0.16 (PDM_DATA) + + // BQ25100 + 13, // D22 is P0.13 (HICHG) + 17, // D23 is P0.17 (~CHG) + + // + 21, // D24 is P0.21 (QSPI_SCK) + 25, // D25 is P0.25 (QSPI_CSN) + 20, // D26 is P0.20 (QSPI_SIO_0 DI) + 24, // D27 is P0.24 (QSPI_SIO_1 DO) + 22, // D28 is P0.22 (QSPI_SIO_2 WP) + 23, // D29 is P0.23 (QSPI_SIO_3 HOLD) + + // NFC + 9, // D30 is P0.09 (NFC1) + 10, // D31 is P0.10 (NFC2) + + // VBAT + 31, // D32 is P0.31 (VBAT) +}; + +void initVariant() +{ + // Disable reading of the BAT voltage. + // https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging + pinMode(VBAT_ENABLE, OUTPUT); + //digitalWrite(VBAT_ENABLE, HIGH); + // This was taken from Seeed github butis not coherent with the doc, + // VBAT_ENABLE should be kept to LOW to protect P0.14, (1500/500)*(4.2-3.3)+3.3 = 3.9V > 3.6V + // This induces a 3mA current in the resistors :( but it's better than burning the nrf + digitalWrite(VBAT_ENABLE, LOW); + + // Low charging current (50mA) + // https://wiki.seeedstudio.com/XIAO_BLE#battery-charging-current + //pinMode(PIN_CHARGING_CURRENT, INPUT); + + // High charging current (100mA) + pinMode(PIN_CHARGING_CURRENT, OUTPUT); + digitalWrite(PIN_CHARGING_CURRENT, LOW); + + pinMode(PIN_QSPI_CS, OUTPUT); + digitalWrite(PIN_QSPI_CS, HIGH); + + pinMode(LED_RED, OUTPUT); + digitalWrite(LED_RED, HIGH); + pinMode(LED_GREEN, OUTPUT); + digitalWrite(LED_GREEN, HIGH); + pinMode(LED_BLUE, OUTPUT); + digitalWrite(LED_BLUE, HIGH); +} diff --git a/variants/ikoka_nano_nrf/variant.h b/variants/ikoka_nano_nrf/variant.h new file mode 100644 index 00000000..1496aad0 --- /dev/null +++ b/variants/ikoka_nano_nrf/variant.h @@ -0,0 +1,149 @@ +#ifndef _IKOKA_NANO_NRF_H_ +#define _IKOKA_NANO_NRF_H_ + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +//#define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define PINS_COUNT (33) +#define NUM_DIGITAL_PINS (33) +#define NUM_ANALOG_INPUTS (8) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED (LED_RED) +#define LED_PWR (PINS_COUNT) +#define PIN_NEOPIXEL (PINS_COUNT) +#define NEOPIXEL_NUM (0) + +#define LED_BUILTIN (PIN_LED) + +#define LED_RED (11) +#define LED_GREEN (13) +#define LED_BLUE (12) + +#define LED_STATE_ON (0) // State when LED is litted + +// Buttons +// #define PIN_BUTTON1 (PINS_COUNT) + +// Digital PINs +static const uint8_t D0 = 0 ; +static const uint8_t D1 = 1 ; +static const uint8_t D2 = 2 ; +static const uint8_t D3 = 3 ; +static const uint8_t D4 = 4 ; +static const uint8_t D5 = 5 ; +static const uint8_t D6 = 6 ; +static const uint8_t D7 = 7 ; +static const uint8_t D8 = 8 ; +static const uint8_t D9 = 9 ; +static const uint8_t D10 = 10; + +#define VBAT_ENABLE (14) // Output LOW to enable reading of the BAT voltage. + // https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging + +#define PIN_CHARGING_CURRENT (22) // Battery Charging current + // https://wiki.seeedstudio.com/XIAO_BLE#battery-charging-current + +// Analog pins +#define PIN_A0 (0) +#define PIN_A1 (1) +#define PIN_A2 (2) +#define PIN_A3 (3) +#define PIN_A4 (4) +#define PIN_A5 (5) +#define PIN_VBAT (32) // Read the BAT voltage. + // https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging + +#define BAT_NOT_CHARGING (23) // LOW when charging + +#define AREF_VOLTAGE (3.0) +#define ADC_MULTIPLIER (3.0F) // 1M, 512k divider bridge + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; + +#define ADC_RESOLUTION (12) + +// Other pins +#define PIN_NFC1 (30) +#define PIN_NFC2 (31) + +// Serial interfaces +#define PIN_SERIAL1_RX (7) +#define PIN_SERIAL1_TX (6) + +// SPI Interfaces +#define SPI_INTERFACES_COUNT (2) + +#define PIN_SPI_MISO (9) +#define PIN_SPI_MOSI (10) +#define PIN_SPI_SCK (8) + +#define PIN_SPI1_MISO (25) +#define PIN_SPI1_MOSI (26) +#define PIN_SPI1_SCK (29) + +// Lora SPI is on SPI0 +#define P_LORA_SCLK PIN_SPI_SCK +#define P_LORA_MISO PIN_SPI_MISO +#define P_LORA_MOSI PIN_SPI_MOSI + +// Wire Interfaces +#define WIRE_INTERFACES_COUNT (1) + +// #define PIN_WIRE_SDA (17) // 4 and 5 are used for the sx1262 ! +// #define PIN_WIRE_SCL (16) // use WIRE1_SDA + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +//#define PIN_WIRE1_SDA (17) +//#define PIN_WIRE1_SCL (16) +#define PIN_LSM6DS3TR_C_POWER (15) +#define PIN_LSM6DS3TR_C_INT1 (18) + +// PDM Interfaces +#define PIN_PDM_PWR (19) +#define PIN_PDM_CLK (20) +#define PIN_PDM_DIN (21) + +// QSPI Pins +#define PIN_QSPI_SCK (24) +#define PIN_QSPI_CS (25) +#define PIN_QSPI_IO0 (26) +#define PIN_QSPI_IO1 (27) +#define PIN_QSPI_IO2 (28) +#define PIN_QSPI_IO3 (29) + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES (P25Q16H) +#define EXTERNAL_FLASH_USE_QSPI + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif From 8a2e4721d1dcb67663a0219e8bfd9cfd902b1fb0 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Fri, 10 Oct 2025 16:01:48 +0200 Subject: [PATCH 028/115] heltec wireless tracker: use `-D ARDUINO_USB_CDC_ON_BOOT=1` with all envs repeater and room server envs did not have arduino cdc flag enabled which resulted in broken serial. --- variants/heltec_tracker/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 33a7d9b8..19ab49a8 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -5,6 +5,7 @@ build_flags = ${esp32_base.build_flags} -I variants/heltec_tracker -D HELTEC_LORA_V3 + -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 @@ -40,7 +41,6 @@ build_flags = ${Heltec_tracker_base.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display -D MAX_CONTACTS=300 From 70ac82059437007ee4c44ae246868af8940d2947 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 11 Oct 2025 18:01:26 +0800 Subject: [PATCH 029/115] add heltec tracker v2 board. --- boards/heltec_tracker_v2.json | 41 ++++ .../sensors/MicroNMEALocationProvider.h | 10 +- src/helpers/ui/ST7735Display.cpp | 9 +- .../HeltecTrackerV2Board.cpp | 88 +++++++ .../heltec_tracker_v2/HeltecTrackerV2Board.h | 23 ++ variants/heltec_tracker_v2/pins_arduino.h | 60 +++++ variants/heltec_tracker_v2/platformio.ini | 218 ++++++++++++++++++ variants/heltec_tracker_v2/target.cpp | 60 +++++ variants/heltec_tracker_v2/target.h | 30 +++ 9 files changed, 535 insertions(+), 4 deletions(-) create mode 100644 boards/heltec_tracker_v2.json create mode 100644 variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp create mode 100644 variants/heltec_tracker_v2/HeltecTrackerV2Board.h create mode 100644 variants/heltec_tracker_v2/pins_arduino.h create mode 100644 variants/heltec_tracker_v2/platformio.ini create mode 100644 variants/heltec_tracker_v2/target.cpp create mode 100644 variants/heltec_tracker_v2/target.h diff --git a/boards/heltec_tracker_v2.json b/boards/heltec_tracker_v2.json new file mode 100644 index 00000000..277dcabd --- /dev/null +++ b/boards/heltec_tracker_v2.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "heltec_tracker_v2" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "heltec_tracker v2", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://heltec.org/", + "vendor": "heltec" +} \ No newline at end of file diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index ec82f25e..1cf97358 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -3,6 +3,7 @@ #include "LocationProvider.h" #include #include +#include #ifndef GPS_EN #ifdef PIN_GPS_EN @@ -37,14 +38,15 @@ class MicroNMEALocationProvider : public LocationProvider { MicroNMEA nmea; mesh::RTCClock* _clock; Stream* _gps_serial; + RefCountedDigitalPin* _peripher_power; int _pin_reset; int _pin_en; long next_check = 0; long time_valid = 0; public : - MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = NULL, int pin_reset = GPS_RESET, int pin_en = GPS_EN) : - _gps_serial(&ser), nmea(_nmeaBuffer, sizeof(_nmeaBuffer)), _pin_reset(pin_reset), _pin_en(pin_en), _clock(clock) { + MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = NULL, int pin_reset = GPS_RESET, int pin_en = GPS_EN,RefCountedDigitalPin* peripher_power=NULL) : + _gps_serial(&ser), nmea(_nmeaBuffer, sizeof(_nmeaBuffer)), _pin_reset(pin_reset), _pin_en(pin_en), _clock(clock), _peripher_power(peripher_power) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); digitalWrite(_pin_reset, GPS_RESET_FORCE); @@ -56,6 +58,7 @@ public : } void begin() override { + if (_peripher_power) _peripher_power->claim(); if (_pin_en != -1) { digitalWrite(_pin_en, PIN_GPS_EN_ACTIVE); } @@ -75,7 +78,8 @@ public : void stop() override { if (_pin_en != -1) { digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE); - } + } + if (_peripher_power) _peripher_power->release(); } void syncTime() override { nmea.clear(); LocationProvider::syncTime(); } diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index e9eea69b..0a28077c 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -24,14 +24,21 @@ bool ST7735Display::begin() { digitalWrite(PIN_TFT_LEDA_CTL, HIGH); digitalWrite(PIN_TFT_RST, HIGH); +#if defined(HELTEC_TRACKER_V2) + display.initR(INITR_MINI160x80); + display.setRotation(DISPLAY_ROTATION); + uint8_t madctl = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV |ST7735_MADCTL_BGR;//Adjust color to BGR + display.sendCommand(ST77XX_MADCTL, &madctl, 1); +#else display.initR(INITR_MINI160x80_PLUGIN); display.setRotation(DISPLAY_ROTATION); +#endif display.setSPISpeed(40000000); display.fillScreen(ST77XX_BLACK); display.setTextColor(ST77XX_WHITE); display.setTextSize(2); display.cp437(true); // Use full 256 char 'Code Page 437' font - + _isOn = true; } return true; diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp new file mode 100644 index 00000000..4975d5cd --- /dev/null +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -0,0 +1,88 @@ +#include "HeltecTrackerV2Board.h" + +void HeltecTrackerV2Board::begin() { + ESP32Board::begin(); + + pinMode(PIN_ADC_CTRL, OUTPUT); + digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive + + pinMode(P_LORA_PA_POWER, OUTPUT); + digitalWrite(P_LORA_PA_POWER,HIGH); + + rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN); + pinMode(P_LORA_PA_EN, OUTPUT); + digitalWrite(P_LORA_PA_EN,HIGH); + pinMode(P_LORA_PA_TX_EN, OUTPUT); + digitalWrite(P_LORA_PA_TX_EN,LOW); + + periph_power.begin(); + + esp_reset_reason_t reason = esp_reset_reason(); + if (reason == ESP_RST_DEEPSLEEP) { + long wakeup_source = esp_sleep_get_ext1_wakeup_status(); + if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) + startup_reason = BD_STARTUP_RX_PACKET; + } + + rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); + rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); + } + } + + void HeltecTrackerV2Board::onBeforeTransmit(void) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + digitalWrite(P_LORA_PA_TX_EN,HIGH); + } + + void HeltecTrackerV2Board::onAfterTransmit(void) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + digitalWrite(P_LORA_PA_TX_EN,LOW); + } + + void HeltecTrackerV2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void HeltecTrackerV2Board::powerOff() { + enterDeepSleep(0); + } + + uint16_t HeltecTrackerV2Board::getBattMilliVolts() { + analogReadResolution(10); + digitalWrite(PIN_ADC_CTRL, HIGH); + delay(10); + uint32_t raw = 0; + for (int i = 0; i < 8; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / 8; + + digitalWrite(PIN_ADC_CTRL, LOW); + + return (5.42 * (3.3 / 1024.0) * raw) * 1000; + } + + const char* HeltecTrackerV2Board::getManufacturerName() const { + return "Heltec Tracker V2"; + } diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h new file mode 100644 index 00000000..d93c86cd --- /dev/null +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +class HeltecTrackerV2Board : public ESP32Board { + +public: + RefCountedDigitalPin periph_power; + + HeltecTrackerV2Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } + + void begin(); + void onBeforeTransmit(void) override; + void onAfterTransmit(void) override; + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override ; + +}; diff --git a/variants/heltec_tracker_v2/pins_arduino.h b/variants/heltec_tracker_v2/pins_arduino.h new file mode 100644 index 00000000..982cb5e5 --- /dev/null +++ b/variants/heltec_tracker_v2/pins_arduino.h @@ -0,0 +1,60 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +static const uint8_t LED_BUILTIN = 18; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 5; +static const uint8_t SCL = 6; + +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 = 3; +static const uint8_t LED = 18; + +#endif /* Pins_Arduino_h */ \ No newline at end of file diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini new file mode 100644 index 00000000..c4a79d9e --- /dev/null +++ b/variants/heltec_tracker_v2/platformio.ini @@ -0,0 +1,218 @@ +[Heltec_tracker_v2] +extends = esp32_base +board = heltec_tracker_v2 +build_flags = + ${esp32_base.build_flags} + ${sensor_base.build_flags} + -I variants/heltec_tracker_v2 + -D HELTEC_TRACKER_V2 + -D ESP32_CPU_FREQ=160 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D P_LORA_TX_LED=18 + -D P_LORA_DIO_1=14 + -D P_LORA_NSS=8 + -D P_LORA_RESET=12 + -D P_LORA_BUSY=13 + -D P_LORA_SCLK=9 + -D P_LORA_MISO=11 + -D P_LORA_MOSI=10 + -D P_LORA_PA_POWER=7 ;power en + -D P_LORA_PA_EN=4 + -D P_LORA_PA_TX_EN=46 ;enable tx + -D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm. + -D MAX_LORA_TX_POWER=22 ;Max SX1262 output + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_BOARD_SDA=5 + -D PIN_BOARD_SCL=6 + -D PIN_USER_BTN=0 + -D PIN_TFT_SDA=42 ; SDIN + -D PIN_TFT_SCL=41 ; SCLK + -D PIN_TFT_DC=40 ; RS (register select) + -D PIN_TFT_RST=39 ; RES + -D PIN_TFT_CS=38 + -D USE_PIN_TFT=1 + -D PIN_VEXT_EN=3 ; Vext is connected to VDD which is also connected to OLED & GPS + -D PIN_VEXT_EN_ACTIVE=HIGH + -D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground) + -D DISPLAY_ROTATION=1 + -D PIN_GPS_RX=34 + -D PIN_GPS_TX=33 + -D PIN_GPS_RESET=35 + -D PIN_GPS_RESET_ACTIVE=LOW + -D GPS_BAUD_RATE=115200 + -D ENV_INCLUDE_GPS=1 + -D PIN_ADC_CTRL=2 + -D PIN_VBAT_READ=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/heltec_tracker_v2> + + +lib_deps = + ${esp32_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0 + +[env:heltec_tracker_v2_repeater] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -D DISPLAY_CLASS=ST7735Display + -D ADVERT_NAME='"Heltec Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:heltec_tracker_v2_repeater_bridge_espnow] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -D DISPLAY_CLASS=ST7735Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + ${esp32_ota.lib_deps} + +[env:heltec_tracker_v2_room_server] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -D DISPLAY_CLASS=ST7735Display + -D ADVERT_NAME='"Heltec Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + +<../examples/simple_room_server> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + ${esp32_ota.lib_deps} + +[env:heltec_tracker_v2_terminal_chat] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_tracker_v2_companion_radio_usb] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D DISPLAY_CLASS=ST7735Display +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_tracker_v2_companion_radio_ble] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D DISPLAY_CLASS=ST7735Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_tracker_v2_companion_radio_wifi] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D DISPLAY_CLASS=ST7735Display + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_tracker_v2_sensor] +extends = Heltec_tracker_v2 +build_flags = + ${Heltec_tracker_v2.build_flags} + -D ADVERT_NAME='"Heltec Tracker V2 Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ENV_PIN_SDA=3 + -D ENV_PIN_SCL=4 + -D DISPLAY_CLASS=ST7735Display +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + +<../examples/simple_sensor> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + ${esp32_ota.lib_deps} diff --git a/variants/heltec_tracker_v2/target.cpp b/variants/heltec_tracker_v2/target.cpp new file mode 100644 index 00000000..da397fb7 --- /dev/null +++ b/variants/heltec_tracker_v2/target.cpp @@ -0,0 +1,60 @@ +#include +#include "target.h" + +HeltecTrackerV2Board board; + +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, NULL, GPS_RESET, GPS_EN, &board.periph_power); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display(&board.periph_power); // peripheral power pin is shared + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#if defined(P_LORA_SCLK) + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/heltec_tracker_v2/target.h b/variants/heltec_tracker_v2/target.h new file mode 100644 index 00000000..190404ef --- /dev/null +++ b/variants/heltec_tracker_v2/target.h @@ -0,0 +1,30 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif + +extern HeltecTrackerV2Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); From ad2894a0399d3337070ef5d0c8fa451c1322ddcd Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 11 Oct 2025 18:03:15 +0800 Subject: [PATCH 030/115] delete PSRAM. --- boards/heltec_tracker_v2.json | 1 - 1 file changed, 1 deletion(-) diff --git a/boards/heltec_tracker_v2.json b/boards/heltec_tracker_v2.json index 277dcabd..62b569e0 100644 --- a/boards/heltec_tracker_v2.json +++ b/boards/heltec_tracker_v2.json @@ -6,7 +6,6 @@ }, "core": "esp32", "extra_flags": [ - "-DBOARD_HAS_PSRAM", "-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_MODE=0", "-DARDUINO_RUNNING_CORE=1", From 76dcfbb23a50cd026a78a9719f54bd0bba81dc39 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 11 Oct 2025 15:29:17 +0200 Subject: [PATCH 031/115] gpsCli: use parseTextParts --- src/helpers/CommonCLI.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 2efdd00a..6616a056 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -518,15 +518,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch const char* key = command + 11; const char* val = sensors.getSettingByKey(key); if (val != NULL) { - strcpy(reply, val); + sprintf(reply, "> %s", val); } else { - strcpy(reply, "can't find custom var"); + strcpy(reply, "null"); } } else if (memcmp(command, "sensor set ", 11) == 0) { - const char* args = &command[11]; - const char* value = strchr(args,' ') + 1; - char key [value-args+1]; - strncpy(key, args, value-args-1); + strcpy(tmp, &command[11]); + const char *parts[2]; + int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); + const char *key = (num > 0) ? parts[0] : ""; + const char *value = (num > 1) ? parts[1] : "null"; if (sensors.setSettingByKey(key, value)) { strcpy(reply, "ok"); } else { From f6064b41e9cdd5faac3a080d028640062b1aaa43 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 11 Oct 2025 18:00:57 +0200 Subject: [PATCH 032/115] gps_cli: set node location based on gps --- src/helpers/CommonCLI.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 6616a056..cc63cfeb 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -580,6 +580,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch if (l != NULL) { l->syncTime(); } + } else if (memcmp(command, "gps setloc", 10) == 0) { + _prefs->node_lat = sensors.node_lat; + _prefs->node_lon = sensors.node_lon; + savePrefs(); + strcpy(reply, "ok"); } else if (memcmp(command, "gps", 3) == 0) { LocationProvider * l = sensors.getLocationProvider(); if (l != NULL) { From 4dc3dda2d8d13f2d76260589fed96e026b63eb5b Mon Sep 17 00:00:00 2001 From: recrof Date: Sat, 11 Oct 2025 18:32:02 +0200 Subject: [PATCH 033/115] xiao c3: migrated to esm, added missing roles, cleanup --- .../xiao_c3}/XiaoC3Board.h | 5 -- variants/xiao_c3/platformio.ini | 63 +++++++++++++++---- variants/xiao_c3/target.cpp | 9 ++- variants/xiao_c3/target.h | 7 +-- 4 files changed, 61 insertions(+), 23 deletions(-) rename {src/helpers => variants/xiao_c3}/XiaoC3Board.h (95%) diff --git a/src/helpers/XiaoC3Board.h b/variants/xiao_c3/XiaoC3Board.h similarity index 95% rename from src/helpers/XiaoC3Board.h rename to variants/xiao_c3/XiaoC3Board.h index c97f22b7..6ea1c15f 100644 --- a/src/helpers/XiaoC3Board.h +++ b/variants/xiao_c3/XiaoC3Board.h @@ -3,11 +3,6 @@ #include #include -// LoRa radio module pins for custom Seeduino XiaoC3 build -// #define P_LORA_SCLK D8 -// #define P_LORA_MISO D9 -// #define P_LORA_MOSI D10 - #include #include diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 1f27dfc8..617f610e 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -3,6 +3,8 @@ extends = esp32_base board = seeed_xiao_esp32c3 build_flags = ${esp32_base.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS -I variants/xiao_c3 -D ESP32_CPU_FREQ=80 -D PIN_VBAT_READ=D0 @@ -12,23 +14,27 @@ build_flags = -D P_LORA_BUSY=D3 -D PIN_BOARD_SDA=D6 -D PIN_BOARD_SCL=D7 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D SX126X_RX_BOOSTED_GAIN=1 + -D LORA_TX_POWER=22 -D SX126X_RXEN=D5 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 build_src_filter = ${esp32_base.build_src_filter} +<../variants/xiao_c3> + + +lib_deps = + ${esp32_base.lib_deps} + ${sensor_base.lib_deps} -[env:Xiao_C3_sx1262_repeater] +[env:Xiao_C3_repeater] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} +<../examples/simple_repeater/*.cpp> build_flags = ${Xiao_esp32_C3.build_flags} - -D RADIO_CLASS=CustomSX1262 - -D WRAPPER_CLASS=CustomSX1262Wrapper - -D SX126X_RX_BOOSTED_GAIN=1 - -D LORA_TX_POWER=22 -D ADVERT_NAME='"Xiao C3 Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -41,6 +47,24 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 +[env:Xiao_C3_room_server] +extends = Xiao_esp32_C3 +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/simple_room_server/*.cpp> +build_flags = + ${Xiao_esp32_C3.build_flags} + -D ADVERT_NAME='"Xiao C3 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:Xiao_C3_companion_radio_ble] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} @@ -48,10 +72,6 @@ build_src_filter = ${Xiao_esp32_C3.build_src_filter} + build_flags = ${Xiao_esp32_C3.build_flags} - -D RADIO_CLASS=CustomSX1262 - -D WRAPPER_CLASS=CustomSX1262Wrapper - -D SX126X_RX_BOOSTED_GAIN=1 - -D LORA_TX_POWER=22 -D MAX_CONTACTS=300 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -71,10 +91,6 @@ build_src_filter = ${Xiao_esp32_C3.build_src_filter} + build_flags = ${Xiao_esp32_C3.build_flags} - -D RADIO_CLASS=CustomSX1262 - -D WRAPPER_CLASS=CustomSX1262Wrapper - -D SX126X_RX_BOOSTED_GAIN=1 - -D LORA_TX_POWER=22 -D MAX_CONTACTS=300 -D MAX_GROUP_CHANNELS=8 -D OFFLINE_QUEUE_SIZE=256 @@ -85,3 +101,24 @@ lib_deps = ${Xiao_esp32_C3.lib_deps} ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 + +[env:Xiao_C3_companion_radio_wifi] +extends = Xiao_esp32_C3 +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Xiao_esp32_C3.build_flags} + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D OFFLINE_QUEUE_SIZE=256 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_c3/target.cpp b/variants/xiao_c3/target.cpp index b3701ca7..fe3f7196 100644 --- a/variants/xiao_c3/target.cpp +++ b/variants/xiao_c3/target.cpp @@ -14,7 +14,14 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif bool radio_init() { fallback_clock.begin(); diff --git a/variants/xiao_c3/target.h b/variants/xiao_c3/target.h index fa29e52b..a7ef4421 100644 --- a/variants/xiao_c3/target.h +++ b/variants/xiao_c3/target.h @@ -3,16 +3,15 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include -#include #include -#include +#include extern XiaoC3Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern EnvironmentSensorManager sensors; bool radio_init(); uint32_t radio_get_rng_seed(); From bf1da43d7dd6395badfe81f57e29f0403e5bcb27 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 11 Oct 2025 19:00:02 +0200 Subject: [PATCH 034/115] gps_cli: gps advert to control advert location policy --- examples/simple_repeater/MyMesh.cpp | 12 ++++++-- examples/simple_room_server/MyMesh.cpp | 12 ++++++-- examples/simple_sensor/SensorMesh.cpp | 12 ++++++-- src/helpers/CommonCLI.cpp | 39 ++++++++++++++++++++++++-- src/helpers/CommonCLI.h | 5 ++++ 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f7153da3..de8072f1 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -289,8 +289,16 @@ mesh::Packet *MyMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); + if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); + app_data_len = builder.encodeTo(app_data); + } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); + app_data_len = builder.encodeTo(app_data); + } else { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); + app_data_len = builder.encodeTo(app_data); + } } return createAdvert(self_id, app_data, app_data_len); diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index d9a36397..00736820 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -116,8 +116,16 @@ mesh::Packet *MyMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; { - AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); + if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); + app_data_len = builder.encodeTo(app_data); + } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); + app_data_len = builder.encodeTo(app_data); + } else { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); + app_data_len = builder.encodeTo(app_data); + } } return createAdvert(self_id, app_data, app_data_len); diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 00da006a..97d95d5f 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -241,8 +241,16 @@ mesh::Packet* SensorMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; { - AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); + if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); + app_data_len = builder.encodeTo(app_data); + } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); + app_data_len = builder.encodeTo(app_data); + } else { + AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); + app_data_len = builder.encodeTo(app_data); + } } return createAdvert(self_id, app_data, app_data_len); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index cc63cfeb..757180f8 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -67,7 +67,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read(pad, 4); // 152 file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 - // 161 + if (file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)) == -1) { + _prefs->advert_loc_policy = ADVERT_LOC_PREFS; // default value + } // 161 + // 162 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -89,6 +92,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14); _prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1); + _prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2); file.close(); } @@ -142,7 +146,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write(pad, 4); // 152 file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 - // 161 + file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161 + // 162 file.close(); } @@ -585,6 +590,36 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->node_lon = sensors.node_lon; savePrefs(); strcpy(reply, "ok"); + } else if (memcmp(command, "gps advert", 10) == 0) { + if (strlen(command) == 10) { + switch (_prefs->advert_loc_policy) { + case ADVERT_LOC_NONE: + strcpy(reply, "> none"); + break; + case ADVERT_LOC_PREFS: + strcpy(reply, "> prefs"); + break; + case ADVERT_LOC_SHARE: + strcpy(reply, "> share"); + break; + default: + strcpy(reply, "error"); + } + } else if (memcmp(command+11, "none", 4) == 0) { + _prefs->advert_loc_policy = ADVERT_LOC_NONE; + savePrefs(); + strcpy(reply, "ok"); + } else if (memcmp(command+11, "share", 5) == 0) { + _prefs->advert_loc_policy = ADVERT_LOC_SHARE; + savePrefs(); + strcpy(reply, "ok"); + } else if (memcmp(command+11, "prefs", 4) == 0) { + _prefs->advert_loc_policy = ADVERT_LOC_PREFS; + savePrefs(); + strcpy(reply, "ok"); + } else { + strcpy(reply, "error"); + } } else if (memcmp(command, "gps", 3) == 0) { LocationProvider * l = sensors.getLocationProvider(); if (l != NULL) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 07523643..68489913 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -8,6 +8,10 @@ #define WITH_BRIDGE #endif +#define ADVERT_LOC_NONE 0 +#define ADVERT_LOC_SHARE 1 +#define ADVERT_LOC_PREFS 2 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -41,6 +45,7 @@ struct NodePrefs { // persisted to file // Gps settings uint8_t gps_enabled; uint32_t gps_interval; // in seconds + uint8_t advert_loc_policy; }; class CommonCLICallbacks { From c4a2b139308ecd3a03028fb2083a159311624b2e Mon Sep 17 00:00:00 2001 From: recrof Date: Sat, 11 Oct 2025 21:52:48 +0200 Subject: [PATCH 035/115] moved HeltecV3Board.h to variant folder --- .../heltec_v3}/HeltecV3Board.h | 18 +++--------------- variants/heltec_v3/platformio.ini | 7 +++++++ variants/heltec_v3/target.h | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) rename {src/helpers => variants/heltec_v3}/HeltecV3Board.h (88%) diff --git a/src/helpers/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h similarity index 88% rename from src/helpers/HeltecV3Board.h rename to variants/heltec_v3/HeltecV3Board.h index c63ed2d8..afdaf639 100644 --- a/src/helpers/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -2,16 +2,7 @@ #include #include - -// LoRa radio module pins for Heltec V3 -// Also for Heltec Wireless Tracker/Paper -#define P_LORA_DIO_1 14 -#define P_LORA_NSS 8 -#define P_LORA_RESET RADIOLIB_NC -#define P_LORA_BUSY 13 -#define P_LORA_SCLK 9 -#define P_LORA_MISO 11 -#define P_LORA_MOSI 10 +#include // built-ins #ifndef PIN_VBAT_READ // set in platformio.ini for boards like Heltec Wireless Paper (20) @@ -22,9 +13,6 @@ #endif #define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_INACTIVE HIGH -//#define PIN_LED_BUILTIN 35 - -#include "ESP32Board.h" #include @@ -43,7 +31,7 @@ public: // Auto-detect correct ADC_CTRL pin polarity (different for boards >3.2) pinMode(PIN_ADC_CTRL, INPUT); adc_active_state = !digitalRead(PIN_ADC_CTRL); - + pinMode(PIN_ADC_CTRL, OUTPUT); digitalWrite(PIN_ADC_CTRL, !adc_active_state); // Initially inactive @@ -64,7 +52,7 @@ public: void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index b43c0870..dcf566b3 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -7,6 +7,13 @@ build_flags = -I variants/heltec_v3 -D HELTEC_LORA_V3 -D ESP32_CPU_FREQ=80 + -D P_LORA_DIO_1=14 + -D P_LORA_NSS=8 + -D P_LORA_RESET=RADIOLIB_NC + -D P_LORA_BUSY=13 + -D P_LORA_SCLK=9 + -D P_LORA_MISO=11 + -D P_LORA_MOSI=10 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index b2125664..739aecfe 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include From 1979517381ecee7916e1d724a900e3c2c13c246b Mon Sep 17 00:00:00 2001 From: recrof Date: Sat, 11 Oct 2025 23:35:10 +0200 Subject: [PATCH 036/115] heltec v2 cleanup --- .../heltec_v2}/HeltecV2Board.h | 14 +------ variants/heltec_v2/platformio.ini | 38 +++++++++++++++++-- variants/heltec_v2/target.h | 2 +- 3 files changed, 37 insertions(+), 17 deletions(-) rename {src/helpers => variants/heltec_v2}/HeltecV2Board.h (85%) diff --git a/src/helpers/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h similarity index 85% rename from src/helpers/HeltecV2Board.h rename to variants/heltec_v2/HeltecV2Board.h index 5bf78778..a6221036 100644 --- a/src/helpers/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -1,22 +1,12 @@ #pragma once #include - -// LoRa radio module pins for Heltec V2 -#define P_LORA_DIO_1 26 // DIO0 -#define P_LORA_NSS 18 -#define P_LORA_RESET RADIOLIB_NC // 14 -#define P_LORA_BUSY RADIOLIB_NC -#define P_LORA_SCLK 5 -#define P_LORA_MISO 19 -#define P_LORA_MOSI 27 +#include // built-ins #define PIN_VBAT_READ 37 #define PIN_LED_BUILTIN 25 -#include "ESP32Board.h" - #include class HeltecV2Board : public ESP32Board { @@ -39,7 +29,7 @@ public: void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 100053ed..539cc52e 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -7,13 +7,20 @@ build_flags = -D HELTEC_LORA_V2 -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper + -D P_LORA_DIO_1=26 + -D P_LORA_NSS=18 + -D P_LORA_RESET=RADIOLIB_NC + -D P_LORA_BUSY=RADIOLIB_NC + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 + -D P_LORA_TX_LED=25 -D SX127X_CURRENT_LIMIT=120 -D LORA_TX_POWER=20 -D PIN_BOARD_SDA=4 -D PIN_BOARD_SCL=15 -D PIN_USER_BTN=0 -D PIN_OLED_RESET=16 - -D P_LORA_TX_LED=25 build_src_filter = ${esp32_base.build_src_filter} +<../variants/heltec_v2> lib_deps = @@ -112,7 +119,7 @@ lib_deps = extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} - -D MAX_CONTACTS=170 + -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -128,7 +135,7 @@ build_flags = ${Heltec_lora32_v2.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=170 + -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=8 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 @@ -148,7 +155,7 @@ build_flags = ${Heltec_lora32_v2.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=170 + -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 @@ -164,3 +171,26 @@ build_src_filter = ${Heltec_lora32_v2.build_src_filter} lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 + +[env:Heltec_v2_companion_radio_wifi] +extends = Heltec_lora32_v2 +build_flags = + ${Heltec_lora32_v2.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=160 + -D MAX_GROUP_CHANNELS=8 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/heltec_v2/target.h b/variants/heltec_v2/target.h index 2e5b17de..48d750be 100644 --- a/variants/heltec_v2/target.h +++ b/variants/heltec_v2/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include From 7cb2e0863a8171a300c062547d95697e1c69f009 Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 12 Oct 2025 00:13:34 +0200 Subject: [PATCH 037/115] move StationG2Board.h to variants, enable ESM, add companion wifi, cleanup --- .../station_g2}/StationG2Board.h | 17 +-------- variants/station_g2/platformio.ini | 36 +++++++++++++++++-- variants/station_g2/target.cpp | 9 ++++- variants/station_g2/target.h | 6 ++-- 4 files changed, 45 insertions(+), 23 deletions(-) rename {src/helpers => variants/station_g2}/StationG2Board.h (83%) diff --git a/src/helpers/StationG2Board.h b/variants/station_g2/StationG2Board.h similarity index 83% rename from src/helpers/StationG2Board.h rename to variants/station_g2/StationG2Board.h index 2e31571f..a905682c 100644 --- a/src/helpers/StationG2Board.h +++ b/variants/station_g2/StationG2Board.h @@ -1,22 +1,7 @@ #pragma once #include - -// LoRa radio module pins for Station G2 -#define P_LORA_DIO_1 48 -#define P_LORA_NSS 11 -#define P_LORA_RESET 21 -#define P_LORA_BUSY 47 -#define P_LORA_SCLK 12 -#define P_LORA_MISO 14 -#define P_LORA_MOSI 13 - -// built-ins -//#define PIN_LED_BUILTIN 35 -//#define PIN_VEXT_EN 36 - -#include "ESP32Board.h" - +#include #include class StationG2Board : public ESP32Board { diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 016a46a9..bda8b7ae 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -3,28 +3,40 @@ extends = esp32_base board = station-g2 build_flags = ${esp32_base.build_flags} + ${sensor_base.build_flags} -I variants/station_g2 + -I src/helpers/ui -D STATION_G2 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper + -D P_LORA_DIO_1=48 + -D P_LORA_NSS=11 + -D P_LORA_RESET=21 + -D P_LORA_BUSY=47 + -D P_LORA_SCLK=12 + -D P_LORA_MISO=14 + -D P_LORA_MOSI=13 -D LORA_TX_POWER=19 ; -D P_LORA_TX_LED=35 -D PIN_BOARD_SDA=5 -D PIN_BOARD_SCL=6 -D PIN_USER_BTN=38 + -D PIN_GPS_RX=7 + -D PIN_GPS_TX=15 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 ; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS! ; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance - -I src/helpers/ui -D DISPLAY_CLASS=SH1106Display build_src_filter = ${esp32_base.build_src_filter} +<../variants/station_g2> + + + + lib_deps = ${esp32_base.lib_deps} + ${sensor_base.lib_deps} adafruit/Adafruit SH110X @ ~2.1.13 adafruit/Adafruit GFX Library @ ^1.12.1 @@ -172,7 +184,6 @@ extends = Station_G2 build_flags = ${Station_G2.build_flags} -I examples/companion_radio/ui-new - -D DISPLAY_CLASS=SH1106Display -D MAX_CONTACTS=300 -D MAX_GROUP_CHANNELS=8 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 @@ -190,7 +201,6 @@ extends = Station_G2 build_flags = ${Station_G2.build_flags} -I examples/companion_radio/ui-new - -D DISPLAY_CLASS=SH1106Display -D MAX_CONTACTS=300 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -205,3 +215,23 @@ build_src_filter = ${Station_G2.build_src_filter} lib_deps = ${Station_G2.lib_deps} densaugeo/base64 @ ~1.4.0 + +[env:Station_G2_companion_radio_wifi] +extends = Station_G2 +build_flags = + ${Station_G2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Station_G2.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Station_G2.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/station_g2/target.cpp b/variants/station_g2/target.cpp index 5423af68..c2ee97e2 100644 --- a/variants/station_g2/target.cpp +++ b/variants/station_g2/target.cpp @@ -14,7 +14,14 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif #ifdef DISPLAY_CLASS DISPLAY_CLASS display; diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index 3f67af3a..2bf7016d 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -3,10 +3,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include -#include +#include #ifdef DISPLAY_CLASS #include @@ -16,7 +16,7 @@ extern StationG2Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; From 69cddbca4e66e74fe19e71994360872a6a6a96eb Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 12 Oct 2025 00:32:26 +0200 Subject: [PATCH 038/115] move LilyGoTLoraBoard.h to variants, use template in platformio.ini, cleanup --- .../lilygo_tlora_v2_1}/LilyGoTLoraBoard.h | 4 ++-- variants/lilygo_tlora_v2_1/platformio.ini | 22 ++++--------------- variants/lilygo_tlora_v2_1/target.h | 3 +-- 3 files changed, 7 insertions(+), 22 deletions(-) rename {src/helpers => variants/lilygo_tlora_v2_1}/LilyGoTLoraBoard.h (92%) diff --git a/src/helpers/LilyGoTLoraBoard.h b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h similarity index 92% rename from src/helpers/LilyGoTLoraBoard.h rename to variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h index c595740f..545219b2 100644 --- a/src/helpers/LilyGoTLoraBoard.h +++ b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h @@ -1,7 +1,7 @@ #pragma once #include -#include "ESP32Board.h" +#include // LILYGO T-LoRa V2.1-1.6 board with SX1276 class LilyGoTLoraBoard : public ESP32Board { @@ -9,7 +9,7 @@ public: const char* getManufacturerName() const override { return "LILYGO T-LoRa V2.1-1.6"; } - + uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index e580959c..9ef9734e 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -6,6 +6,8 @@ build_type = release ; Set build type to release board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32_base.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS -I variants/lilygo_tlora_v2_1 -Os -ffunction-sections -fdata-sections ; Optimize for size -D LILYGO_TLORA ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276 @@ -27,28 +29,18 @@ build_flags = -D WRAPPER_CLASS=CustomSX1276Wrapper -D SX127X_CURRENT_LIMIT=120 -D LORA_TX_POWER=20 - -D ENV_INCLUDE_AHTX0=1 - -D ENV_INCLUDE_BME280=1 - -D ENV_INCLUDE_BMP280=1 - -D ENV_INCLUDE_INA3221=1 - -D ENV_INCLUDE_INA219=1 build_src_filter = ${esp32_base.build_src_filter} + + +<../variants/lilygo_tlora_v2_1> + lib_deps = ${esp32_base.lib_deps} - adafruit/Adafruit SSD1306 @ ^2.5.13 - adafruit/Adafruit INA3221 Library @ ^1.0.1 - adafruit/Adafruit INA219 @ ^1.2.3 - adafruit/Adafruit AHTX0 @ ^2.0.5 - adafruit/Adafruit BME280 Library @ ^2.3.0 - adafruit/Adafruit BMP280 Library @ ^2.6.8 + ${sensor_base.lib_deps} ; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments === [env:LilyGo_TLora_V2_1_1_6_repeater] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + +<../examples/simple_repeater> build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} @@ -73,7 +65,6 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + +<../examples/simple_repeater> lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} @@ -89,7 +80,6 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> @@ -111,7 +101,6 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + - + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> @@ -149,7 +138,6 @@ build_flags = -D WIFI_DEBUG_LOGGING=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + - + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> @@ -163,7 +151,6 @@ lib_deps = [env:LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + + +<../examples/simple_repeater> build_flags = @@ -187,7 +174,6 @@ lib_deps = [env:LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + + +<../examples/simple_repeater> build_flags = diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index 380d733b..326a0dee 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -3,10 +3,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include -#include #include #ifdef DISPLAY_CLASS #include From 837e7dcbdbbb08f0ba0514fdac6a5ed056dd2b67 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 12 Oct 2025 12:33:20 +1100 Subject: [PATCH 039/115] * Advert type fix * GPS pref defaults tidy --- examples/simple_repeater/MyMesh.cpp | 1 + examples/simple_room_server/MyMesh.cpp | 11 ++++++++--- examples/simple_sensor/SensorMesh.cpp | 11 ++++++++--- src/helpers/CommonCLI.cpp | 4 +--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index de8072f1..eaf0fb2b 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -645,6 +645,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // GPS defaults _prefs.gps_enabled = 0; _prefs.gps_interval = 0; + _prefs.advert_loc_policy = ADVERT_LOC_PREFS; } void MyMesh::begin(FILESYSTEM *fs) { diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 00736820..b241788e 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -117,13 +117,13 @@ mesh::Packet *MyMesh::createSelfAdvert() { uint8_t app_data_len; { if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); + AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name); app_data_len = builder.encodeTo(app_data); } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); + AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, sensors.node_lat, sensors.node_lon); app_data_len = builder.encodeTo(app_data); } else { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); + AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); app_data_len = builder.encodeTo(app_data); } } @@ -620,6 +620,11 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif + // GPS defaults + _prefs.gps_enabled = 0; + _prefs.gps_interval = 0; + _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + next_post_idx = 0; next_client_idx = 0; next_push = 0; diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 97d95d5f..d365f7fa 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -242,13 +242,13 @@ mesh::Packet* SensorMesh::createSelfAdvert() { uint8_t app_data_len; { if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); + AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name); app_data_len = builder.encodeTo(app_data); } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); + AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name, sensors.node_lat, sensors.node_lon); app_data_len = builder.encodeTo(app_data); } else { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); + AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); app_data_len = builder.encodeTo(app_data); } } @@ -690,6 +690,11 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.disable_fwd = true; _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + + // GPS defaults + _prefs.gps_enabled = 0; + _prefs.gps_interval = 0; + _prefs.advert_loc_policy = ADVERT_LOC_PREFS; } void SensorMesh::begin(FILESYSTEM* fs) { diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 757180f8..b0229f70 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -67,9 +67,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read(pad, 4); // 152 file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 - if (file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)) == -1) { - _prefs->advert_loc_policy = ADVERT_LOC_PREFS; // default value - } // 161 + file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161 // 162 // sanitise bad pref values From 93c01807400db0a04fbef2aa2762fa6a717061c6 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 12 Oct 2025 12:49:26 +1100 Subject: [PATCH 040/115] * Refactor: advert_loc_policy now applied in new method CommonCLI::buildAdvertData() --- examples/simple_repeater/MyMesh.cpp | 14 +------------- examples/simple_room_server/MyMesh.cpp | 14 +------------- examples/simple_sensor/SensorMesh.cpp | 14 +------------- src/helpers/CommonCLI.cpp | 14 ++++++++++++++ src/helpers/CommonCLI.h | 1 + 5 files changed, 18 insertions(+), 39 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index eaf0fb2b..6c85b7e0 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -287,19 +287,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t mesh::Packet *MyMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; - uint8_t app_data_len; - { - if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name); - app_data_len = builder.encodeTo(app_data); - } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, sensors.node_lat, sensors.node_lon); - app_data_len = builder.encodeTo(app_data); - } else { - AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); - } - } + uint8_t app_data_len = _cli.buildAdvertData(ADV_TYPE_REPEATER, app_data); return createAdvert(self_id, app_data, app_data_len); } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index b241788e..e4b57f98 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -114,19 +114,7 @@ bool MyMesh::processAck(const uint8_t *data) { mesh::Packet *MyMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; - uint8_t app_data_len; - { - if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { - AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name); - app_data_len = builder.encodeTo(app_data); - } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, sensors.node_lat, sensors.node_lon); - app_data_len = builder.encodeTo(app_data); - } else { - AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); - } - } + uint8_t app_data_len = _cli.buildAdvertData(ADV_TYPE_ROOM, app_data); return createAdvert(self_id, app_data, app_data_len); } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index d365f7fa..92ea1889 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -239,19 +239,7 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint mesh::Packet* SensorMesh::createSelfAdvert() { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; - uint8_t app_data_len; - { - if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { - AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name); - app_data_len = builder.encodeTo(app_data); - } else if (_prefs.advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name, sensors.node_lat, sensors.node_lon); - app_data_len = builder.encodeTo(app_data); - } else { - AdvertDataBuilder builder(ADV_TYPE_SENSOR, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); - app_data_len = builder.encodeTo(app_data); - } - } + uint8_t app_data_len = _cli.buildAdvertData(ADV_TYPE_SENSOR, app_data); return createAdvert(self_id, app_data, app_data_len); } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b0229f70..87f20f5a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -1,6 +1,7 @@ #include #include "CommonCLI.h" #include "TxtDataHelpers.h" +#include "AdvertDataHelpers.h" #include // Believe it or not, this std C function is busted on some platforms! @@ -160,6 +161,19 @@ void CommonCLI::savePrefs() { _callbacks->savePrefs(); } +uint8_t CommonCLI::buildAdvertData(uint8_t node_type, uint8_t* app_data) { + if (_prefs->advert_loc_policy == ADVERT_LOC_NONE) { + AdvertDataBuilder builder(node_type, _prefs->node_name); + return builder.encodeTo(app_data); + } else if (_prefs->advert_loc_policy == ADVERT_LOC_SHARE) { + AdvertDataBuilder builder(node_type, _prefs->node_name, sensors.node_lat, sensors.node_lon); + return builder.encodeTo(app_data); + } else { + AdvertDataBuilder builder(node_type, _prefs->node_name, _prefs->node_lat, _prefs->node_lon); + return builder.encodeTo(app_data); + } +} + void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, char* reply) { if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 68489913..ce567016 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -98,4 +98,5 @@ public: void loadPrefs(FILESYSTEM* _fs); void savePrefs(FILESYSTEM* _fs); void handleCommand(uint32_t sender_timestamp, const char* command, char* reply); + uint8_t buildAdvertData(uint8_t node_type, uint8_t* app_data); }; From 8426fddcb7af7243f97c665067e6ed4d03910f97 Mon Sep 17 00:00:00 2001 From: Woodie-07 Date: Sun, 12 Oct 2025 16:09:57 +0100 Subject: [PATCH 041/115] workaround for LR1110 shift issue it seems that if the LR1110 radio hears a packet corrupted in a specific way, it'll report a packet of 0 length and with the header error IRQ set. every packet received afterwards will then be shifted to the right by 4 bytes on top of the radio's reported offset. this can occur multiple times with the shift increasing by 4 bytes each time. thus, this patch will read from an additional offset after hearing the trigger packet. transmitting seems to reset the shift - unsure exactly what operation resets it but standby() is called after tx so patch assumes shift is 0 after standby(). more investigation may be needed here. --- platformio.ini | 2 +- src/helpers/radiolib/CustomLR1110.h | 68 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 4fe17af9..e72b7b3a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ monitor_speed = 115200 lib_deps = SPI Wire - jgromes/RadioLib @ ^7.1.2 + jgromes/RadioLib @ ^7.3.0 rweather/Crypto @ ^0.4.0 adafruit/RTClib @ ^2.1.3 melopero/Melopero RV3028 @ ^1.1.0 diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index e82f48f5..0d1fbccc 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -9,6 +9,74 @@ class CustomLR1110 : public LR1110 { public: CustomLR1110(Module *mod) : LR1110(mod) { } + uint8_t shiftCount = 0; + + int16_t standby() override { + // tx resets the shift, standby is called on tx completion + // this might not actually be what resets it, but it seems to work + // more investigation needed + this->shiftCount = 0; + return LR1110::standby(); + } + + size_t getPacketLength(bool update) override { + size_t len = LR1110::getPacketLength(update); + if (len == 0) { + uint32_t irq = getIrqStatus(); + if (irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) { + Serial.println(F("got possible bug packet")); + this->shiftCount += 4; // uint8 will loop around to 0 at 256, perfect as rx buffer is 256 bytes + } else { + Serial.println(F("got zero-length packet without header err irq")); + } + } + return len; + } + + int16_t readData(uint8_t *data, size_t len) override { + // check active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check integrity CRC + uint32_t irq = getIrqStatus(); + int16_t crcState = RADIOLIB_ERR_NONE; + // Report CRC mismatch when there's a payload CRC error, or a header error and no valid header (to avoid false alarm from previous packet) + if((irq & RADIOLIB_LR11X0_IRQ_CRC_ERR) || ((irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID))) { + crcState = RADIOLIB_ERR_CRC_MISMATCH; + } + + // get packet length + // the offset is needed since LR11x0 seems to move the buffer base by 4 bytes on every packet + uint8_t offset = 0; + size_t length = LR1110::getPacketLength(true, &offset); + if((len != 0) && (len < length)) { + // user requested less data than we got, only return what was requested + length = len; + } + + // read packet data + state = readBuffer8(data, length, (uint8_t)(offset + this->shiftCount)); // add shiftCount to offset - only change from radiolib + RADIOLIB_ASSERT(state); + + // clear the Rx buffer + state = clearRxBuffer(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); + + // check if CRC failed - this is done after reading data to give user the option to keep them + RADIOLIB_ASSERT(crcState); + + return(state); + } + RadioLibTime_t getTimeOnAir(size_t len) override { // calculate number of symbols float N_symbol = 0; From c6e5d5021eb9592ebaf47a4f45cae031bea92d97 Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 12 Oct 2025 17:16:45 +0200 Subject: [PATCH 042/115] fix: remove VL53L0X because it causes bootloops on esp32c3 --- variants/xiao_c3/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 617f610e..683199d9 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -5,6 +5,7 @@ build_flags = ${esp32_base.build_flags} ${sensor_base.build_flags} -UENV_INCLUDE_GPS + -UENV_INCLUDE_VL53L0X -I variants/xiao_c3 -D ESP32_CPU_FREQ=80 -D PIN_VBAT_READ=D0 From c6b4a584498ea375e6227d405b65fe4c67735017 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 14 Oct 2025 12:31:43 +1100 Subject: [PATCH 043/115] * repeater and room server: enable downgrading permissions on guest login --- examples/simple_repeater/MyMesh.cpp | 1 + examples/simple_room_server/MyMesh.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6c85b7e0..4bb9a2bb 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -114,6 +114,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr MESH_DEBUG_PRINTLN("Login success!"); client->last_timestamp = sender_timestamp; client->last_activity = getRTCClock()->getCurrentTime(); + client->permissions &= ~0x03; client->permissions |= perms; memcpy(client->shared_secret, secret, PUB_KEY_SIZE); diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index e4b57f98..c1a39a11 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -286,7 +286,7 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m data[len] = 0; // ensure null terminator ClientInfo* client = NULL; - if (data[8] == 0 && !_prefs.allow_read_only) { // blank password, just check if sender is in ACL + if (data[8] == 0) { // blank password, just check if sender is in ACL client = acl.getClient(sender.pub_key, PUB_KEY_SIZE); if (client == NULL) { #if MESH_DEBUG @@ -322,6 +322,7 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m client->extra.room.push_failures = 0; client->last_activity = getRTCClock()->getCurrentTime(); + client->permissions &= ~0x03; client->permissions |= perm; memcpy(client->shared_secret, secret, PUB_KEY_SIZE); From fa8c31be883be52066eba131b339676e72f8ccb1 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 15 Oct 2025 22:47:55 +1100 Subject: [PATCH 044/115] * fix for RAK12500 GPS (I2C) --- .../sensors/EnvironmentSensorManager.cpp | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index aa51c85a..e7a9f5e5 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -98,6 +98,41 @@ static bool serialGPSFlag = false; static SFE_UBLOX_GNSS ublox_GNSS; #endif +class RAK12500LocationProvider : public LocationProvider { + long _lat = 0; + long _lng = 0; + long _alt = 0; + int _sats = 0; + long _epoch = 0; + bool _fix = false; +public: + long getLatitude() override { return _lat; } + long getLongitude() override { return _lng; } + long getAltitude() override { return _alt; } + long satellitesCount() override { return _sats; } + bool isValid() override { return _fix; } + long getTimestamp() override { return _epoch; } + void sendSentence(const char * sentence) override { } + void reset() override { } + void begin() override { } + void stop() override { } + void loop() override { + if (ublox_GNSS.getGnssFixOk(8)) { + _fix = true; + _lat = ublox_GNSS.getLatitude(2) / 10; + _lng = ublox_GNSS.getLongitude(2) / 10; + _alt = ublox_GNSS.getAltitude(2); + _sats = ublox_GNSS.getSIV(2); + } else { + _fix = false; + } + _epoch = ublox_GNSS.getUnixEpoch(2); + } + bool isEnabled() override { return true; } +}; + +static RAK12500LocationProvider RAK12500_provider; + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS #ifdef RAK_WISBLOCK_GPS @@ -521,12 +556,22 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ //Try to init RAK12500 on I2C if (ublox_GNSS.begin(Wire) == true){ MESH_DEBUG_PRINTLN("RAK12500 GPS init correctly with pin %i",ioPin); - ublox_GNSS.setI2COutput(COM_TYPE_NMEA); + ublox_GNSS.setI2COutput(COM_TYPE_UBX); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_GPS); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_GALILEO); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_GLONASS); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_SBAS); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_BEIDOU); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_IMES); + ublox_GNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_QZSS); + ublox_GNSS.setMeasurementRate(1000); ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); gpsResetPin = ioPin; i2cGPSFlag = true; gps_active = true; gps_detected = true; + + _location = &RAK12500_provider; return true; } else if(Serial1){ @@ -583,14 +628,7 @@ void EnvironmentSensorManager::loop() { if (millis() > next_gps_update) { if(gps_active){ #ifdef RAK_WISBLOCK_GPS - if(i2cGPSFlag){ - node_lat = ((double)ublox_GNSS.getLatitude())/10000000.; - node_lon = ((double)ublox_GNSS.getLongitude())/10000000.; - MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); - node_altitude = ((double)ublox_GNSS.getAltitude()) / 1000.0; - MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); - } - else if (serialGPSFlag && _location->isValid()) { + if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; node_lon = ((double)_location->getLongitude())/1000000.; MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); From d3be6afccb83a4a7f15a64aaded6c6e75c2d9cbe Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 15 Oct 2025 22:51:05 +1100 Subject: [PATCH 045/115] * fix for non-RAK targets --- src/helpers/sensors/EnvironmentSensorManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index e7a9f5e5..29545894 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -96,7 +96,6 @@ static bool serialGPSFlag = false; #define TELEM_RAK12500_ADDRESS 0x42 //RAK12500 Ublox GPS via i2c #include static SFE_UBLOX_GNSS ublox_GNSS; -#endif class RAK12500LocationProvider : public LocationProvider { long _lat = 0; @@ -132,6 +131,7 @@ public: }; static RAK12500LocationProvider RAK12500_provider; +#endif bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS From cd920693ec4c21f184c1512162c578f5fc119d16 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 16 Oct 2025 17:33:22 +1100 Subject: [PATCH 046/115] * UITask: new UI_HAS_JOYSTICK * MomentaryButton: new constructor 'multiclick' param * WIoTrackerL1: now just use joystick, joystick press for KEY_ENTER, no multi-click for snappier UI --- examples/companion_radio/ui-new/UITask.cpp | 31 ++++++++++++++-------- src/helpers/ui/MomentaryButton.cpp | 4 +-- src/helpers/ui/MomentaryButton.h | 2 +- variants/wio-tracker-l1/platformio.ini | 2 ++ variants/wio-tracker-l1/target.cpp | 7 ++--- variants/wio-tracker-l1/target.h | 1 + variants/wio-tracker-l1/variant.h | 3 ++- 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index c3da0643..b6484a00 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -20,7 +20,11 @@ #define UI_RECENT_LIST_SIZE 4 #endif -#define PRESS_LABEL "long press" +#if UI_HAS_JOYSTICK + #define PRESS_LABEL "press Enter" +#else + #define PRESS_LABEL "long press" +#endif #include "icons.h" @@ -360,7 +364,7 @@ public: display.drawTextCentered(display.width() / 2, 34, "hibernating..."); } else { display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32); - display.drawTextCentered(display.width() / 2, 64 - 11, "hibernate: " PRESS_LABEL); + display.drawTextCentered(display.width() / 2, 64 - 11, "hibernate:" PRESS_LABEL); } } return 5000; // next render after 5000 ms @@ -660,19 +664,13 @@ bool UITask::isButtonPressed() const { void UITask::loop() { char c = 0; -#if defined(PIN_USER_BTN) +#if UI_HAS_JOYSTICK int ev = user_btn.check(); if (ev == BUTTON_EVENT_CLICK) { - c = checkDisplayOn(KEY_NEXT); + c = checkDisplayOn(KEY_ENTER); } else if (ev == BUTTON_EVENT_LONG_PRESS) { - c = handleLongPress(KEY_ENTER); - } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { - c = handleDoubleClick(KEY_PREV); - } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { - c = handleTripleClick(KEY_SELECT); + c = handleLongPress(KEY_ENTER); // REVISIT: could be mapped to different key code } -#endif -#if defined(WIO_TRACKER_L1) ev = joystick_left.check(); if (ev == BUTTON_EVENT_CLICK) { c = checkDisplayOn(KEY_LEFT); @@ -685,6 +683,17 @@ void UITask::loop() { } else if (ev == BUTTON_EVENT_LONG_PRESS) { c = handleLongPress(KEY_RIGHT); } +#elif defined(PIN_USER_BTN) + int ev = user_btn.check(); + if (ev == BUTTON_EVENT_CLICK) { + c = checkDisplayOn(KEY_NEXT); + } else if (ev == BUTTON_EVENT_LONG_PRESS) { + c = handleLongPress(KEY_ENTER); + } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { + c = handleDoubleClick(KEY_PREV); + } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { + c = handleTripleClick(KEY_SELECT); + } #endif #if defined(PIN_USER_BTN_ANA) ev = analog_btn.check(); diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 0ea4b027..9d01e5b0 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -2,7 +2,7 @@ #define MULTI_CLICK_WINDOW_MS 280 -MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup) { +MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup, bool multiclick) { _pin = pin; _reverse = reverse; _pull = pulldownup; @@ -13,7 +13,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse _threshold = 0; _click_count = 0; _last_click_time = 0; - _multi_click_window = MULTI_CLICK_WINDOW_MS; + _multi_click_window = multiclick ? MULTI_CLICK_WINDOW_MS : 0; _pending_click = false; } diff --git a/src/helpers/ui/MomentaryButton.h b/src/helpers/ui/MomentaryButton.h index 1122e56a..358a343b 100644 --- a/src/helpers/ui/MomentaryButton.h +++ b/src/helpers/ui/MomentaryButton.h @@ -23,7 +23,7 @@ class MomentaryButton { bool isPressed(int level) const; public: - MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false); + MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false, bool multiclick=true); MomentaryButton(int8_t pin, int long_press_mills, int analog_threshold); void begin(); int check(bool repeat_click=false); // returns one of BUTTON_EVENT_* diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 971a32cb..31c6bcb0 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -63,6 +63,7 @@ build_flags = ${WioTrackerL1.build_flags} -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SH1106Display + -D UI_HAS_JOYSTICK=1 -D OFFLINE_QUEUE_SIZE=256 -D PIN_BUZZER=12 -D QSPIFLASH=1 @@ -91,6 +92,7 @@ build_flags = ${WioTrackerL1.build_flags} -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display + -D UI_HAS_JOYSTICK=1 -D PIN_BUZZER=12 -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index 374e53af..e7b73040 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -21,9 +21,10 @@ EnvironmentSensorManager sensors = EnvironmentSensorManager(); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; - MomentaryButton user_btn(PIN_USER_BTN, 1000, true); - MomentaryButton joystick_left(JOYSTICK_LEFT, 1000, true); - MomentaryButton joystick_right(JOYSTICK_RIGHT, 1000, true); + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, false, false); + MomentaryButton joystick_left(JOYSTICK_LEFT, 1000, true, false, false); + MomentaryButton joystick_right(JOYSTICK_RIGHT, 1000, true, false, false); + MomentaryButton back_btn(PIN_BACK_BTN, 1000, true, false, false); #endif bool radio_init() { diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index d3021d6d..97e575d8 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -27,6 +27,7 @@ extern EnvironmentSensorManager sensors; extern MomentaryButton user_btn; extern MomentaryButton joystick_left; extern MomentaryButton joystick_right; + extern MomentaryButton back_btn; #endif bool radio_init(); diff --git a/variants/wio-tracker-l1/variant.h b/variants/wio-tracker-l1/variant.h index 7e1b1b86..86ad62ba 100644 --- a/variants/wio-tracker-l1/variant.h +++ b/variants/wio-tracker-l1/variant.h @@ -31,12 +31,13 @@ #define PIN_BUTTON4 (27) // Joystick Left #define PIN_BUTTON5 (28) // Joystick Right #define PIN_BUTTON6 (29) // Joystick Press -#define PIN_USER_BTN PIN_BUTTON1 +#define PIN_BACK_BTN PIN_BUTTON1 #define JOYSTICK_UP PIN_BUTTON2 #define JOYSTICK_DOWN PIN_BUTTON3 #define JOYSTICK_LEFT PIN_BUTTON4 #define JOYSTICK_RIGHT PIN_BUTTON5 #define JOYSTICK_PRESS PIN_BUTTON6 +#define PIN_USER_BTN PIN_BUTTON6 // Buzzer // #define PIN_BUZZER (12) // Buzzer pin (defined per firmware type) From 0e7486552dec3868ca2093917d65039bf43f8f72 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:17:23 +0200 Subject: [PATCH 047/115] Add simple BME680 support to RAK with adafruit library --- platformio.ini | 2 ++ .../sensors/EnvironmentSensorManager.cpp | 33 ++++++++++++++++++- .../sensors/EnvironmentSensorManager.h | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 4fe17af9..7af33747 100644 --- a/platformio.ini +++ b/platformio.ini @@ -124,6 +124,7 @@ build_flags = -D ENV_INCLUDE_INA260=1 -D ENV_INCLUDE_MLX90614=1 -D ENV_INCLUDE_VL53L0X=1 + -D ENV_INCLUDE_BME680=1 lib_deps = adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 @@ -138,3 +139,4 @@ lib_deps = adafruit/Adafruit MLX90614 Library @ ^2.1.5 adafruit/Adafruit_VL53L0X @ ^1.2.4 stevemarple/MicroNMEA @ ^2.0.6 + adafruit/Adafruit BME680 Library @ ^2.0.4 diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 29545894..e244ef20 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -6,6 +6,14 @@ #define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors #endif +#ifdef ENV_INCLUDE_BME680 +#ifndef TELEM_BME680_ADDRESS +#define TELEM_BME680_ADDRESS 0x76 // BME680 environmental sensor I2C address +#endif +#include +static Adafruit_BME680 BME680; +#endif + #if ENV_INCLUDE_AHTX0 #define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address #include @@ -286,6 +294,16 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_BME680 + if (BME680.begin(TELEM_BME680_ADDRESS, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); + BME680_initialized = true; + } else { + BME680_initialized = false; + MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESS); + } + #endif + return true; } @@ -415,6 +433,18 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_BME680 + if (BME680_initialized) { + if (BME680.performReading()) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BME680.temperature); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME680.humidity); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME680.pressure / 100); + telemetry.addAnalogInput(next_available_channel, BME680.gas_resistance); + next_available_channel++; + } + } + #endif + } return true; @@ -623,6 +653,7 @@ void EnvironmentSensorManager::stop_gps() { void EnvironmentSensorManager::loop() { static long next_gps_update = 0; + #if ENV_INCLUDE_GPS _location->loop(); if (millis() > next_gps_update) { @@ -647,5 +678,5 @@ void EnvironmentSensorManager::loop() { } next_gps_update = millis() + 1000; } + #endif } -#endif diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 09c6cae4..133d2650 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -20,6 +20,7 @@ protected: bool MLX90614_initialized = false; bool VL53L0X_initialized = false; bool SHT4X_initialized = false; + bool BME680_initialized = false; bool gps_detected = false; bool gps_active = false; From 3c48f016019d611d763493e0de246397d2a7ea98 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:29:22 +0200 Subject: [PATCH 048/115] BME680 library doesn't have altitude calculation, we can add it here to match other sensors' --- src/helpers/sensors/EnvironmentSensorManager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index e244ef20..bb70c0b5 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -8,8 +8,9 @@ #ifdef ENV_INCLUDE_BME680 #ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 // BME680 environmental sensor I2C address +#define TELEM_BME680_ADDRESS 0x76 #endif +#define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include static Adafruit_BME680 BME680; #endif @@ -439,6 +440,7 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen telemetry.addTemperature(TELEM_CHANNEL_SELF, BME680.temperature); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME680.humidity); telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME680.pressure / 100); + telemetry.addAltitude(TELEM_CHANNEL_SELF, 44330.0 * (1.0 - pow((BME680.pressure / 100) / TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903))); telemetry.addAnalogInput(next_available_channel, BME680.gas_resistance); next_available_channel++; } @@ -680,3 +682,4 @@ void EnvironmentSensorManager::loop() { } #endif } +#endif From 02351abc2d5cffbd68d6c940ee8ac974ce0605b2 Mon Sep 17 00:00:00 2001 From: Woodie-07 Date: Thu, 16 Oct 2025 16:25:18 +0100 Subject: [PATCH 049/115] change println to debug macro in lr1110 patch --- src/helpers/radiolib/CustomLR1110.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index 0d1fbccc..f723bb7f 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -1,6 +1,7 @@ #pragma once #include +#include "MeshCore.h" #define LR1110_IRQ_HAS_PREAMBLE 0b0000000100 // 4 4 valid LoRa header received #define LR1110_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received @@ -24,10 +25,10 @@ class CustomLR1110 : public LR1110 { if (len == 0) { uint32_t irq = getIrqStatus(); if (irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) { - Serial.println(F("got possible bug packet")); + MESH_DEBUG_PRINTLN("LR1110: got header err, assuming shift"); this->shiftCount += 4; // uint8 will loop around to 0 at 256, perfect as rx buffer is 256 bytes } else { - Serial.println(F("got zero-length packet without header err irq")); + MESH_DEBUG_PRINTLN("LR1110: got zero-length packet without header err irq"); } } return len; From 24ed5b377f35cea6e44e6512e4ffa2442dfb03f6 Mon Sep 17 00:00:00 2001 From: recrof Date: Fri, 17 Oct 2025 16:25:58 +0200 Subject: [PATCH 050/115] added custom pio task "Create UF2 file" --- create-uf2.py | 31 +++++++++++++++++++++++++++++++ platformio.ini | 1 + 2 files changed, 32 insertions(+) create mode 100644 create-uf2.py diff --git a/create-uf2.py b/create-uf2.py new file mode 100644 index 00000000..10ec0ed6 --- /dev/null +++ b/create-uf2.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 + +# Adds PlatformIO post-processing to convert hex files to uf2 files + +import os + +Import("env") + +firmware_hex = "${BUILD_DIR}/${PROGNAME}.hex" +uf2_file = os.environ.get("UF2_FILE_PATH", "${BUILD_DIR}/${PROGNAME}.uf2") + +def create_uf2_action(source, target, env): + uf2_cmd = " ".join( + [ + '"$PYTHONEXE"', + '"$PROJECT_DIR/bin/uf2conv/uf2conv.py"', + '-f', '0xADA52840', + '-c', firmware_hex, + '-o', uf2_file, + ] + ) + env.Execute(uf2_cmd) + +env.AddCustomTarget( + name="create_uf2", + dependencies=firmware_hex, + actions=create_uf2_action, + title="Create UF2 file", + description="Use uf2conv to convert hex binary into uf2", + always_build=True, +) \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 4fe17af9..f107867c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -76,6 +76,7 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ [nrf52_base] extends = arduino_base platform = nordicnrf52 +extra_scripts = create-uf2.py build_flags = ${arduino_base.build_flags} -D NRF52_PLATFORM -D LFS_NO_ASSERT=1 From 006af527768f2aebffe3f12cb6dc4fb60cfd6fc7 Mon Sep 17 00:00:00 2001 From: haxwithaxe Date: Fri, 17 Oct 2025 14:20:55 -0400 Subject: [PATCH 051/115] Added more polished build.sh usage --- build.sh | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/build.sh b/build.sh index a871b9ab..732dac43 100755 --- a/build.sh +++ b/build.sh @@ -1,12 +1,45 @@ #!/usr/bin/env bash -# usage -# sh build.sh build-firmware RAK_4631_Repeater -# sh build.sh build-firmwares -# sh build.sh build-matching-firmwares RAK_4631 -# sh build.sh build-companion-firmwares -# sh build.sh build-repeater-firmwares -# sh build.sh build-room-server-firmwares +global_usage() { + cat - < [target] + +Commands: + help|usage|-h|--help: Shows this message. + build-firmware : Build the firmware for the given build target. + build-firmwares: Build all firmwares for all targets. + build-matching-firmwares : Build all firmwares for build targets containing the string given for . + build-companion-firmwares: Build all companion firmwares for all build targets. + build-repeater-firmwares: Build all repeater firmwares for all build targets. + build-room-server-firmwares: Build all chat room server firmwares for all build targets. + +Examples: +Build firmware for the "RAK_4631_Repeater" device target +$ sh build.sh build-firmware RAK_4631_Repeater + +Build all firmwares for device targets containing the string "RAK_4631" +$ sh build.sh build-matching-firmwares + +Build all companion firmwares +$ sh build.sh build-companion-firmwares + +Build all repeater firmwares +$ sh build.sh build-repeater-firmwares + +Build all chat room server firmwares +$ sh build.sh build-room-server-firmwares +EOF +} + +# Catch cries for help before doing anything else. +case $1 in + help|usage|-h|--help) + global_usage + exit 1 + ;; +esac + # get a list of pio env names that start with "env:" get_pio_envs() { From 3210475f352c54e795310e908bd0c07a9b2ffa97 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 18 Oct 2025 12:33:43 +0200 Subject: [PATCH 052/115] CommonCli: Remove dependency on target.h --- examples/simple_repeater/MyMesh.cpp | 2 +- examples/simple_room_server/MyMesh.cpp | 2 +- examples/simple_sensor/SensorMesh.cpp | 2 +- src/helpers/CommonCLI.cpp | 26 +++++++++++++------------- src/helpers/CommonCLI.h | 7 ++++--- variants/t1000-e/platformio.ini | 1 + 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4bb9a2bb..374384bd 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -586,7 +586,7 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) #if defined(WITH_RS232_BRIDGE) , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) #endif diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index c1a39a11..a9ba7998 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -580,7 +580,7 @@ void MyMesh::onAckRecv(mesh::Packet *packet, uint32_t ack_crc) { MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; _logging = false; diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 92ea1889..8f8a11fe 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -651,7 +651,7 @@ void SensorMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) { SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 87f20f5a..b8bb698a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -166,7 +166,7 @@ uint8_t CommonCLI::buildAdvertData(uint8_t node_type, uint8_t* app_data) { AdvertDataBuilder builder(node_type, _prefs->node_name); return builder.encodeTo(app_data); } else if (_prefs->advert_loc_policy == ADVERT_LOC_SHARE) { - AdvertDataBuilder builder(node_type, _prefs->node_name, sensors.node_lat, sensors.node_lon); + AdvertDataBuilder builder(node_type, _prefs->node_name, _sensors->node_lat, _sensors->node_lon); return builder.encodeTo(app_data); } else { AdvertDataBuilder builder(node_type, _prefs->node_name, _prefs->node_lat, _prefs->node_lon); @@ -533,7 +533,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "%s", _board->getManufacturerName()); } else if (memcmp(command, "sensor get ", 11) == 0) { const char* key = command + 11; - const char* val = sensors.getSettingByKey(key); + const char* val = _sensors->getSettingByKey(key); if (val != NULL) { sprintf(reply, "> %s", val); } else { @@ -545,7 +545,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); const char *key = (num > 0) ? parts[0] : ""; const char *value = (num > 1) ? parts[1] : "null"; - if (sensors.setSettingByKey(key, value)) { + if (_sensors->setSettingByKey(key, value)) { strcpy(reply, "ok"); } else { strcpy(reply, "can't find custom var"); @@ -553,7 +553,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else if (memcmp(command, "sensor list", 11) == 0) { char* dp = reply; int start = 0; - int end = sensors.getNumSettings(); + int end = _sensors->getNumSettings(); if (strlen(command) > 11) { start = _atoi(command+12); } @@ -565,8 +565,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch int i; for (i = start; i < end && (dp-reply < 134); i++) { sprintf(dp, "%s=%s\n", - sensors.getSettingName(i), - sensors.getSettingValue(i)); + _sensors->getSettingName(i), + _sensors->getSettingValue(i)); dp = strchr(dp, 0); } if (i < end) { @@ -577,7 +577,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } #if ENV_INCLUDE_GPS == 1 } else if (memcmp(command, "gps on", 6) == 0) { - if (sensors.setSettingByKey("gps", "1")) { + if (_sensors->setSettingByKey("gps", "1")) { _prefs->gps_enabled = 1; savePrefs(); strcpy(reply, "ok"); @@ -585,7 +585,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "gps toggle not found"); } } else if (memcmp(command, "gps off", 7) == 0) { - if (sensors.setSettingByKey("gps", "0")) { + if (_sensors->setSettingByKey("gps", "0")) { _prefs->gps_enabled = 0; savePrefs(); strcpy(reply, "ok"); @@ -593,13 +593,13 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "gps toggle not found"); } } else if (memcmp(command, "gps sync", 8) == 0) { - LocationProvider * l = sensors.getLocationProvider(); + LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { l->syncTime(); } } else if (memcmp(command, "gps setloc", 10) == 0) { - _prefs->node_lat = sensors.node_lat; - _prefs->node_lon = sensors.node_lon; + _prefs->node_lat = _sensors->node_lat; + _prefs->node_lon = _sensors->node_lon; savePrefs(); strcpy(reply, "ok"); } else if (memcmp(command, "gps advert", 10) == 0) { @@ -633,12 +633,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "error"); } } else if (memcmp(command, "gps", 3) == 0) { - LocationProvider * l = sensors.getLocationProvider(); + LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { bool enabled = l->isEnabled(); // is EN pin on ? bool fix = l->isValid(); // has fix ? int sats = l->satellitesCount(); - bool active = !strcmp(sensors.getSettingByKey("gps"), "1"); + bool active = !strcmp(_sensors->getSettingByKey("gps"), "1"); if (enabled) { sprintf(reply, "on, %s, %s, %d sats", active?"active":"deactivated", diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ce567016..ea59aa92 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -2,7 +2,7 @@ #include "Mesh.h" #include -#include +#include #if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE) #define WITH_BRIDGE @@ -85,6 +85,7 @@ class CommonCLI { NodePrefs* _prefs; CommonCLICallbacks* _callbacks; mesh::MainBoard* _board; + SensorManager* _sensors; char tmp[PRV_KEY_SIZE*2 + 4]; mesh::RTCClock* getRTCClock() { return _rtc; } @@ -92,8 +93,8 @@ class CommonCLI { void loadPrefsInt(FILESYSTEM* _fs, const char* filename); public: - CommonCLI(mesh::MainBoard& board, mesh::RTCClock& rtc, NodePrefs* prefs, CommonCLICallbacks* callbacks) - : _board(&board), _rtc(&rtc), _prefs(prefs), _callbacks(callbacks) { } + CommonCLI(mesh::MainBoard& board, mesh::RTCClock& rtc, SensorManager& sensors, NodePrefs* prefs, CommonCLICallbacks* callbacks) + : _board(&board), _rtc(&rtc), _sensors(&sensors), _prefs(prefs), _callbacks(callbacks) { } void loadPrefs(FILESYSTEM* _fs); void savePrefs(FILESYSTEM* _fs); diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 6bb3bdb5..555b182f 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -26,6 +26,7 @@ build_flags = ${nrf52_base.build_flags} -D P_LORA_RESET=42 ; P1.10 -D LR11X0_DIO_AS_RF_SWITCH=true -D LR11X0_DIO3_TCXO_VOLTAGE=1.6 + -D ENV_INCLUDE_GPS=1 build_src_filter = ${nrf52_base.build_src_filter} + + From f085a9d6c560d598c6692b9e123cb9df50e4b123 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 18 Oct 2025 13:10:17 +0200 Subject: [PATCH 053/115] tracker_l1_eink: set UI_HAS_JOYSTICK --- variants/wio-tracker-l1-eink/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/wio-tracker-l1-eink/platformio.ini b/variants/wio-tracker-l1-eink/platformio.ini index a41e9e23..deb85f5e 100644 --- a/variants/wio-tracker-l1-eink/platformio.ini +++ b/variants/wio-tracker-l1-eink/platformio.ini @@ -49,6 +49,7 @@ build_flags = ${WioTrackerL1Eink.build_flags} -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=GxEPDDisplay + -D UI_HAS_JOYSTICK=1 -D PIN_BUZZER=12 -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 From 7d62a2783652c096c9038edd985ff92431091149 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 18 Oct 2025 13:37:18 +0200 Subject: [PATCH 054/115] uitask: bring back buzzer toggle on tracker l1 --- examples/companion_radio/ui-new/UITask.cpp | 4 ++++ variants/wio-tracker-l1/target.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index b6484a00..7c75a089 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -683,6 +683,10 @@ void UITask::loop() { } else if (ev == BUTTON_EVENT_LONG_PRESS) { c = handleLongPress(KEY_RIGHT); } + ev = back_btn.check(); + if (ev == BUTTON_EVENT_TRIPLE_CLICK) { + c = handleTripleClick(KEY_SELECT); + } #elif defined(PIN_USER_BTN) int ev = user_btn.check(); if (ev == BUTTON_EVENT_CLICK) { diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index e7b73040..64866de0 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -24,7 +24,7 @@ EnvironmentSensorManager sensors = EnvironmentSensorManager(); MomentaryButton user_btn(PIN_USER_BTN, 1000, true, false, false); MomentaryButton joystick_left(JOYSTICK_LEFT, 1000, true, false, false); MomentaryButton joystick_right(JOYSTICK_RIGHT, 1000, true, false, false); - MomentaryButton back_btn(PIN_BACK_BTN, 1000, true, false, false); + MomentaryButton back_btn(PIN_BACK_BTN, 1000, true, false, true); #endif bool radio_init() { From ce707923099b8739ac3cb88365797b262605794a Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 18 Oct 2025 14:03:27 +0200 Subject: [PATCH 055/115] lgfx_display: better handle display class construction --- src/helpers/ui/LGFXDisplay.h | 11 +++-------- .../sensecap_indicator-espnow/SCIndicatorDisplay.h | 3 +-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/helpers/ui/LGFXDisplay.h b/src/helpers/ui/LGFXDisplay.h index 81d0239f..ad7212ec 100644 --- a/src/helpers/ui/LGFXDisplay.h +++ b/src/helpers/ui/LGFXDisplay.h @@ -1,9 +1,3 @@ - -/* - * Base class for LovyanGFX supported display (works on ESP32 mainly) - * You can extend this class to support your display, providing your own LGFX - */ - #pragma once #include @@ -20,11 +14,12 @@ protected: LGFX_Device* display; LGFX_Sprite buffer; - bool _isOn; + bool _isOn = false; int _color = TFT_WHITE; public: - LGFXDisplay(int w, int h):DisplayDriver(w/UI_ZOOM, h/UI_ZOOM) {_isOn = false;} + LGFXDisplay(int w, int h, LGFX_Device &disp) + : DisplayDriver(w/UI_ZOOM, h/UI_ZOOM), display(&disp) {} bool begin(); bool isOn() override { return _isOn; } void turnOn() override; diff --git a/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h b/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h index 6a7e3177..aabedd24 100644 --- a/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h +++ b/variants/sensecap_indicator-espnow/SCIndicatorDisplay.h @@ -124,6 +124,5 @@ public: class SCIndicatorDisplay : public LGFXDisplay { LGFX disp; public: - SCIndicatorDisplay() : LGFXDisplay(480, 480) - { display=&disp; } + SCIndicatorDisplay() : LGFXDisplay(480, 480, disp) {} }; From 37dc715a8e5fbec0f0e2cb9537ad418922c9a8b5 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 18 Oct 2025 23:37:58 +0200 Subject: [PATCH 056/115] SensorManager: remove setSettingByKey --- examples/simple_repeater/MyMesh.h | 2 +- examples/simple_room_server/MyMesh.h | 2 +- examples/simple_sensor/SensorMesh.h | 2 +- src/helpers/CommonCLI.cpp | 6 +++--- src/helpers/SensorManager.h | 10 ---------- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c45c141d..729c5b7d 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -137,7 +137,7 @@ protected: #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { - sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); } #endif diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 60ef1e73..f6ce31e5 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -151,7 +151,7 @@ protected: #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { - sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); } #endif diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index cdc3940c..ff0698dc 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -151,7 +151,7 @@ private: #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { - sensors.setSettingByKey("gps", _prefs.gps_enabled?"1":"0"); + sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); } #endif }; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 87f20f5a..77f0b085 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -545,7 +545,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); const char *key = (num > 0) ? parts[0] : ""; const char *value = (num > 1) ? parts[1] : "null"; - if (sensors.setSettingByKey(key, value)) { + if (sensors.setSettingValue(key, value)) { strcpy(reply, "ok"); } else { strcpy(reply, "can't find custom var"); @@ -577,7 +577,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } #if ENV_INCLUDE_GPS == 1 } else if (memcmp(command, "gps on", 6) == 0) { - if (sensors.setSettingByKey("gps", "1")) { + if (sensors.setSettingValue("gps", "1")) { _prefs->gps_enabled = 1; savePrefs(); strcpy(reply, "ok"); @@ -585,7 +585,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "gps toggle not found"); } } else if (memcmp(command, "gps off", 7) == 0) { - if (sensors.setSettingByKey("gps", "0")) { + if (sensors.setSettingValue("gps", "0")) { _prefs->gps_enabled = 0; savePrefs(); strcpy(reply, "ok"); diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 38c1d806..89a174c2 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -34,14 +34,4 @@ public: } return NULL; } - - bool setSettingByKey(const char* key, const char* value) { - int num = getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(getSettingName(i), key) == 0) { - return setSettingValue(key, value); - } - } - return false; - } }; From a421215e848d7ce6e2afc9559bdeeae3fd7a2435 Mon Sep 17 00:00:00 2001 From: recrof Date: Sat, 18 Oct 2025 23:42:28 +0200 Subject: [PATCH 057/115] all nrf52 devices: force framework-arduinoadafruitnrf52 version to 1.10700.0 --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index d4600d00..1200f599 100644 --- a/platformio.ini +++ b/platformio.ini @@ -76,6 +76,8 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ [nrf52_base] extends = arduino_base platform = nordicnrf52 +platform_packages = + framework-arduinoadafruitnrf52 @ 1.10700.0 extra_scripts = create-uf2.py build_flags = ${arduino_base.build_flags} -D NRF52_PLATFORM From a5070077ba058e3dfff7e1668a0a355d00cf07e8 Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 19 Oct 2025 00:02:38 +0200 Subject: [PATCH 058/115] equalize RAK with all other nrf52 variants and use newer platform with all important fixes --- boards/rak4631.json | 72 ++++++++++ variants/rak4631/platformio.ini | 3 +- variants/rak4631/variant.cpp | 49 +++++++ variants/rak4631/variant.h | 172 ++++++++++++++++++++++++ variants/rak_wismesh_tag/platformio.ini | 3 +- 5 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 boards/rak4631.json create mode 100644 variants/rak4631/variant.cpp create mode 100644 variants/rak4631/variant.h diff --git a/boards/rak4631.json b/boards/rak4631.json new file mode 100644 index 00000000..8d820fce --- /dev/null +++ b/boards/rak4631.json @@ -0,0 +1,72 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x8029" + ], + [ + "0x239A", + "0x0029" + ], + [ + "0x239A", + "0x002A" + ], + [ + "0x239A", + "0x802A" + ] + ], + "usb_product": "WisCore RAK4631 Board", + "mcu": "nrf52840", + "variant": "WisCore_RAK4631_Board", + "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", + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino" + ], + "name": "WisCore RAK4631 Board", + "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://www.rakwireless.com", + "vendor": "RAKwireless" +} diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index a8807e36..9a0504dc 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -1,7 +1,6 @@ [rak4631] extends = nrf52_base -platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak -board = wiscore_rak4631 +board = rak4631 board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} diff --git a/variants/rak4631/variant.cpp b/variants/rak4631/variant.cpp new file mode 100644 index 00000000..bd85e971 --- /dev/null +++ b/variants/rak4631/variant.cpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // P0 + 0 , 1 , 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() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2);; +} + diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h new file mode 100644 index 00000000..e83d1339 --- /dev/null +++ b/variants/rak4631/variant.h @@ -0,0 +1,172 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + /* + * WisBlock Base GPIO definitions + */ + static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B + static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B + static const uint8_t WB_IO3 = 21; // SLOT_C + static const uint8_t WB_IO4 = 4; // SLOT_C + static const uint8_t WB_IO5 = 9; // SLOT_D + static const uint8_t WB_IO6 = 10; // SLOT_D + static const uint8_t WB_SW1 = 33; // IO_SLOT + static const uint8_t WB_A0 = 5; // IO_SLOT + static const uint8_t WB_A1 = 31; // IO_SLOT + static const uint8_t WB_I2C1_SDA = 13; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C1_SCL = 14; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C2_SDA = 24; // IO_SLOT + static const uint8_t WB_I2C2_SCL = 25; // IO_SLOT + static const uint8_t WB_SPI_CS = 26; // IO_SLOT + static const uint8_t WB_SPI_CLK = 3; // IO_SLOT + static const uint8_t WB_SPI_MISO = 29; // IO_SLOT + static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Buttons + */ +// RAK4631 has no buttons + +/* + * Analog pins + */ +#define PIN_A0 (5) //(3) +#define PIN_A1 (31) //(4) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + + static const uint8_t A0 = PIN_A0; + static const uint8_t A1 = PIN_A1; + static const uint8_t A2 = PIN_A2; + static const uint8_t A3 = PIN_A3; + static const uint8_t A4 = PIN_A4; + static const uint8_t A5 = PIN_A5; + static const uint8_t A6 = PIN_A6; + static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + + static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +// TXD1 RXD1 on Base Board +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// TXD0 RXD0 on Base Board +#define PIN_SERIAL2_RX (19) +#define PIN_SERIAL2_TX (20) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (29) +#define PIN_SPI_MOSI (30) +#define PIN_SPI_SCK (3) + + static const uint8_t SS = 26; + static const uint8_t MOSI = PIN_SPI_MOSI; + static const uint8_t MISO = PIN_SPI_MISO; + static const uint8_t SCK = PIN_SPI_SCK; + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA (13) +#define PIN_WIRE_SCL (14) + +#define PIN_WIRE1_SDA (24) +#define PIN_WIRE1_SCL (25) + + // QSPI Pins + // QSPI occupied by GPIO's + #define PIN_QSPI_SCK 3 // 19 + #define PIN_QSPI_CS 26 // 17 + #define PIN_QSPI_IO0 30 // 20 + #define PIN_QSPI_IO1 29 // 21 + #define PIN_QSPI_IO2 28 // 22 + #define PIN_QSPI_IO3 2 // 23 + + // On-board QSPI Flash + // No onboard flash + #define EXTERNAL_FLASH_DEVICES IS25LP080D + #define EXTERNAL_FLASH_USE_QSPI + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini index a55cec19..37593f61 100644 --- a/variants/rak_wismesh_tag/platformio.ini +++ b/variants/rak_wismesh_tag/platformio.ini @@ -1,7 +1,6 @@ [rak_wismesh_tag] extends = nrf52_base -platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak -board = wiscore_rak4631 +board = rak4631 board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} From 31b8f7252a6078782818cd39a657ba740dcba130 Mon Sep 17 00:00:00 2001 From: Tomas P Date: Sun, 19 Oct 2025 20:44:27 +0200 Subject: [PATCH 059/115] Support for Elecrow Thinknode M2 --- boards/ESP32-S3-WROOM-1-N4.json | 39 +++++ variants/thinknode_m2/ThinknodeM2Board.cpp | 32 ++++ variants/thinknode_m2/ThinknodeM2Board.h | 18 +++ variants/thinknode_m2/pins_arduino.h | 28 ++++ variants/thinknode_m2/platformio.ini | 171 +++++++++++++++++++++ variants/thinknode_m2/target.cpp | 57 +++++++ variants/thinknode_m2/target.h | 32 ++++ variants/thinknode_m2/variant.h | 15 ++ 8 files changed, 392 insertions(+) create mode 100644 boards/ESP32-S3-WROOM-1-N4.json create mode 100644 variants/thinknode_m2/ThinknodeM2Board.cpp create mode 100644 variants/thinknode_m2/ThinknodeM2Board.h create mode 100644 variants/thinknode_m2/pins_arduino.h create mode 100644 variants/thinknode_m2/platformio.ini create mode 100644 variants/thinknode_m2/target.cpp create mode 100644 variants/thinknode_m2/target.h create mode 100644 variants/thinknode_m2/variant.h diff --git a/boards/ESP32-S3-WROOM-1-N4.json b/boards/ESP32-S3-WROOM-1-N4.json new file mode 100644 index 00000000..160926b2 --- /dev/null +++ b/boards/ESP32-S3-WROOM-1-N4.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-D ARDUINO_USB_CDC_ON_BOOT=0", + "-D ARDUINO_USB_MSC_ON_BOOT=0", + "-D ARDUINO_USB_DFU_ON_BOOT=0", + "-D ARDUINO_USB_MODE=0", + "-D ARDUINO_RUNNING_CORE=1", + "-D ARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "ESP32-S3-WROOM-1-N4" + }, + "connectivity": ["wifi", "bluetooth"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "ESP32-S3-WROOM-1-N4 (4 MB Flash, No PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 524288, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", + "vendor": "Espressif" +} diff --git a/variants/thinknode_m2/ThinknodeM2Board.cpp b/variants/thinknode_m2/ThinknodeM2Board.cpp new file mode 100644 index 00000000..3a2f067e --- /dev/null +++ b/variants/thinknode_m2/ThinknodeM2Board.cpp @@ -0,0 +1,32 @@ +#include "ThinknodeM2Board.h" + + + +void ThinknodeM2Board::begin() { + ESP32Board::begin(); + pinMode(PIN_VEXT_EN, OUTPUT); // init display + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // pin needs to be high + delay(10); + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // need to do this twice. do not know why.. + pinMode(PIN_STATUS_LED, OUTPUT); // init power led + } + + void ThinknodeM2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_deep_sleep_start(); + } + + void ThinknodeM2Board::powerOff() { + enterDeepSleep(0); + } + + uint16_t ThinknodeM2Board::getBattMilliVolts() { + analogReadResolution(12); + delay(10); + float volts = (analogRead(PIN_VBAT_READ) * ADC_MULTIPLIER * AREF_VOLTAGE) / 4096; + analogReadResolution(10); + return volts * 1000; + } + + const char* ThinknodeM2Board::getManufacturerName() const { + return "Elecrow ThinkNode M2"; + } \ No newline at end of file diff --git a/variants/thinknode_m2/ThinknodeM2Board.h b/variants/thinknode_m2/ThinknodeM2Board.h new file mode 100644 index 00000000..8011fae6 --- /dev/null +++ b/variants/thinknode_m2/ThinknodeM2Board.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include + +class ThinknodeM2Board : public ESP32Board { + +public: + + void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override ; + +}; \ No newline at end of file diff --git a/variants/thinknode_m2/pins_arduino.h b/variants/thinknode_m2/pins_arduino.h new file mode 100644 index 00000000..a5dee363 --- /dev/null +++ b/variants/thinknode_m2/pins_arduino.h @@ -0,0 +1,28 @@ +// Need this file for ESP32-S3 +// No need to modify this file, changes to pins imported from variant.h +// Most is similar to https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Serial +static const uint8_t TX = GPS_TX; +static const uint8_t RX = GPS_RX; + +// Default SPI will be mapped to Radio +static const uint8_t SS = P_LORA_NSS; +static const uint8_t SCK = P_LORA_SCLK; +static const uint8_t MOSI = P_LORA_MISO; +static const uint8_t MISO = P_LORA_MOSI; + +// The default Wire will be mapped to PMU and RTC +static const uint8_t SCL = PIN_BOARD_SCL; +static const uint8_t SDA = PIN_BOARD_SDA; + +#endif /* Pins_Arduino_h */ \ No newline at end of file diff --git a/variants/thinknode_m2/platformio.ini b/variants/thinknode_m2/platformio.ini new file mode 100644 index 00000000..0238947f --- /dev/null +++ b/variants/thinknode_m2/platformio.ini @@ -0,0 +1,171 @@ +[ThinkNode_M2] +extends = esp32_base +board = ESP32-S3-WROOM-1-N4 +build_flags = ${esp32_base.build_flags} + -I variants/thinknode_m2 + -D THINKNODE_M2 + -D GPS_RX=44 + -D GPS_TX=43 + -D PIN_VEXT_EN=46 + -D PIN_BUZZER=5 + -D PIN_VEXT_EN_ACTIVE=HIGH + -D PIN_BOARD_SCL=15 + -D PIN_BOARD_SDA=16 + -D P_LORA_DIO_1=3 + -D P_LORA_NSS=10 + -D P_LORA_RESET=21 ; RADIOLIB_NC + -D P_LORA_BUSY=14 ; DIO2 = 38 + -D P_LORA_SCLK=12 + -D P_LORA_MISO=13 + -D P_LORA_MOSI=11 + -D PIN_USER_BTN=47 + -D PIN_STATUS_LED=6 + -D PIN_LED=6 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=3.3 + -D SX126X_CURRENT_LIMIT=140 + -D DISPLAY_CLASS=SH1106Display + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 + -D MESH_DEBUG=1 +build_src_filter = ${esp32_base.build_src_filter} + + + + + + + +<../variants/thinknode_m2> +lib_deps = ${esp32_base.lib_deps} + adafruit/Adafruit SH110X @ ~2.1.13 + adafruit/Adafruit GFX Library @ ^1.12.1 + +[env:ThinkNode_M2_Repeater] +extends = ThinkNode_M2 +build_src_filter = ${ThinkNode_M2.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${ThinkNode_M2.build_flags} + -D ADVERT_NAME='"Thinknode M2 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${ThinkNode_M2.lib_deps} + ${esp32_ota.lib_deps} + +; [env:ThinkNode_M2_Repeater_bridge_rs232] +; extends = ThinkNode_M2 +; build_src_filter = ${ThinkNode_M2.build_src_filter} +; + +; +<../examples/simple_repeater/*.cpp> +; build_flags = +; ${ThinkNode_M2.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${ThinkNode_M2.lib_deps} +; ${esp32_ota.lib_deps} + +[env:ThinkNode_M2_Repeater_bridge_espnow] +extends = ThinkNode_M2 +build_src_filter = ${ThinkNode_M2.build_src_filter} + + + +<../examples/simple_repeater/*.cpp> +build_flags = + ${ThinkNode_M2.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${ThinkNode_M2.lib_deps} + ${esp32_ota.lib_deps} + +[env:ThinkNode_M2_room_server] +extends = ThinkNode_M2 +build_src_filter = ${ThinkNode_M2.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${ThinkNode_M2.build_flags} + -D ADVERT_NAME='"Thinknode M2 Room Server"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${ThinkNode_M2.lib_deps} + ${esp32_ota.lib_deps} + +[env:ThinkNode_M2_terminal_chat] +extends = ThinkNode_M2 +build_flags = + ${ThinkNode_M2.build_flags} + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ThinkNode_M2.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${ThinkNode_M2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:ThinkNode_M2_companion_radio_ble] +extends = ThinkNode_M2 +build_flags = + ${ThinkNode_M2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ThinkNode_M2.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + +lib_deps = + ${ThinkNode_M2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:ThinkNode_M2_companion_radio_serial] +extends = ThinkNode_M2 +build_flags = + ${ThinkNode_M2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D SERIAL_TX=D6 + -D SERIAL_RX=D7 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${ThinkNode_M2.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${ThinkNode_M2.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/thinknode_m2/target.cpp b/variants/thinknode_m2/target.cpp new file mode 100644 index 00000000..cb3c1624 --- /dev/null +++ b/variants/thinknode_m2/target.cpp @@ -0,0 +1,57 @@ +#include +#include "target.h" + +ThinknodeM2Board board; + +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + pinMode(21, INPUT); + pinMode(48, OUTPUT); + #if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} + diff --git a/variants/thinknode_m2/target.h b/variants/thinknode_m2/target.h new file mode 100644 index 00000000..b05def8a --- /dev/null +++ b/variants/thinknode_m2/target.h @@ -0,0 +1,32 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +//#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif + +extern ThinknodeM2Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); + + \ No newline at end of file diff --git a/variants/thinknode_m2/variant.h b/variants/thinknode_m2/variant.h new file mode 100644 index 00000000..928f56ec --- /dev/null +++ b/variants/thinknode_m2/variant.h @@ -0,0 +1,15 @@ +#define I2C_SCL 15 +#define I2C_SDA 16 +#define PIN_VBAT_READ 17 +#define AREF_VOLTAGE (3.0) +#define ADC_MULTIPLIER (1.548F) +#define PIN_BUZZER 5 +#define PIN_VEXT_EN_ACTIVE HIGH +#define PIN_VEXT_EN 46 +#define PIN_USER_BTN 47 +#define PIN_LED 6 +#define PIN_STATUS_LED 6 +#define PIN_PWRBTN 4 + + + From 5d495d505a1db06254254f5625a18e96647e46c3 Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 21 Oct 2025 00:34:57 +1100 Subject: [PATCH 060/115] Revert Heltec T114 power savings As discussed on discord with @recrof people are having issues, possibly due to these changes. See https://github.com/meshcore-dev/MeshCore/issues/746 This reverts commit a16e011bd21cf3070f04b3513d562add0d090838. --- variants/heltec_t114/T114Board.cpp | 39 ------------------------------ variants/heltec_t114/T114Board.h | 5 ---- 2 files changed, 44 deletions(-) diff --git a/variants/heltec_t114/T114Board.cpp b/variants/heltec_t114/T114Board.cpp index 3b40e7cf..f8d170b5 100644 --- a/variants/heltec_t114/T114Board.cpp +++ b/variants/heltec_t114/T114Board.cpp @@ -24,45 +24,6 @@ void T114Board::begin() { pinMode(PIN_VBAT_READ, INPUT); - // Enable SoftDevice low-power mode - sd_power_mode_set(NRF_POWER_MODE_LOWPWR); - - // Enable DC/DC converter for better efficiency (REG1 stage) - NRF_POWER->DCDCEN = 1; - - // Power down unused communication peripherals - // UART1 - Not used on T114 - NRF_UARTE1->ENABLE = 0; - - // SPIM2/SPIS2 - Not used (SPI is on SPIM0) - NRF_SPIM2->ENABLE = 0; - NRF_SPIS2->ENABLE = 0; - - // TWI1 (I2C1) - Not used (I2C is on TWI0) - NRF_TWIM1->ENABLE = 0; - NRF_TWIS1->ENABLE = 0; - - // PWM modules - Not used for standard T114 functions - NRF_PWM1->ENABLE = 0; - NRF_PWM2->ENABLE = 0; - NRF_PWM3->ENABLE = 0; - - // PDM (Digital Microphone Interface) - Not used - NRF_PDM->ENABLE = 0; - - // I2S - Not used - NRF_I2S->ENABLE = 0; - - // QSPI - Not used (no external flash) - NRF_QSPI->ENABLE = 0; - - // Disable unused analog peripherals - // SAADC channels - only keep what's needed for battery monitoring - NRF_SAADC->ENABLE = 0; // Re-enable only when needed for measurements - - // COMP - Comparator not used - NRF_COMP->ENABLE = 0; - #if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); #endif diff --git a/variants/heltec_t114/T114Board.h b/variants/heltec_t114/T114Board.h index 7a7a654b..49d1ec37 100644 --- a/variants/heltec_t114/T114Board.h +++ b/variants/heltec_t114/T114Board.h @@ -27,9 +27,6 @@ public: uint16_t getBattMilliVolts() override { int adcvalue = 0; - - NRF_SAADC->ENABLE = 1; - analogReadResolution(12); analogReference(AR_INTERNAL_3_0); pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin 6 set to high @@ -39,8 +36,6 @@ public: adcvalue = analogRead(PIN_VBAT_READ); digitalWrite(6, 0); - NRF_SAADC->ENABLE = 0; - return (uint16_t)((float)adcvalue * MV_LSB * 4.9); } From ec05d40b3c948aec9e01ac0560204d3388f5336b Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Mon, 20 Oct 2025 18:49:34 +0200 Subject: [PATCH 061/115] Add Seeed Wio WM1110 Dev Board variant --- variants/wio_wm1110/WioWM1110Board.cpp | 75 +++++++++++++ variants/wio_wm1110/WioWM1110Board.h | 58 ++++++++++ variants/wio_wm1110/platformio.ini | 85 +++++++++++++++ variants/wio_wm1110/target.cpp | 110 +++++++++++++++++++ variants/wio_wm1110/target.h | 21 ++++ variants/wio_wm1110/variant.cpp | 92 ++++++++++++++++ variants/wio_wm1110/variant.h | 145 +++++++++++++++++++++++++ 7 files changed, 586 insertions(+) create mode 100644 variants/wio_wm1110/WioWM1110Board.cpp create mode 100644 variants/wio_wm1110/WioWM1110Board.h create mode 100644 variants/wio_wm1110/platformio.ini create mode 100644 variants/wio_wm1110/target.cpp create mode 100644 variants/wio_wm1110/target.h create mode 100644 variants/wio_wm1110/variant.cpp create mode 100644 variants/wio_wm1110/variant.h diff --git a/variants/wio_wm1110/WioWM1110Board.cpp b/variants/wio_wm1110/WioWM1110Board.cpp new file mode 100644 index 00000000..ca3638b3 --- /dev/null +++ b/variants/wio_wm1110/WioWM1110Board.cpp @@ -0,0 +1,75 @@ +#ifdef WIO_WM1110 + +#include +#include +#include + +#include "WioWM1110Board.h" + +static BLEDfu bledfu; + +static void connect_callback(uint16_t conn_handle) { + (void)conn_handle; + MESH_DEBUG_PRINTLN("BLE client connected"); +} + +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { + (void)conn_handle; + (void)reason; + + MESH_DEBUG_PRINTLN("BLE client disconnected"); +} + +void WioWM1110Board::begin() { + startup_reason = BD_STARTUP_NORMAL; + + sd_power_mode_set(NRF_POWER_MODE_LOWPWR); + NRF_POWER->DCDCEN = 1; + + pinMode(BATTERY_PIN, INPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_RED, OUTPUT); + pinMode(SENSOR_POWER_PIN, OUTPUT); + + digitalWrite(LED_GREEN, HIGH); + digitalWrite(LED_RED, LOW); + digitalWrite(SENSOR_POWER_PIN, LOW); + + Serial1.begin(115200); + +#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) + Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL); +#endif + + Wire.begin(); + + delay(10); +} + +bool WioWM1110Board::startOTAUpdate(const char *id, char reply[]) { + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + Bluefruit.setTxPower(4); + Bluefruit.setName("WM1110_OTA"); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + bledfu.begin(); + + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); + Bluefruit.Advertising.setFastTimeout(30); + Bluefruit.Advertising.start(0); + + strcpy(reply, "OK - started"); + return true; +} + +#endif + diff --git a/variants/wio_wm1110/WioWM1110Board.h b/variants/wio_wm1110/WioWM1110Board.h new file mode 100644 index 00000000..823acbc7 --- /dev/null +++ b/variants/wio_wm1110/WioWM1110Board.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#ifdef WIO_WM1110 + +#ifdef Serial + #undef Serial +#endif +#define Serial Serial1 + +class WioWM1110Board : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin(); + uint8_t getStartupReason() const override { return startup_reason; } + +#if defined(LED_GREEN) + void onBeforeTransmit() override { + digitalWrite(LED_RED, HIGH); + } + void onAfterTransmit() override { + digitalWrite(LED_RED, LOW); + } +#endif + + uint16_t getBattMilliVolts() override { + int adcvalue = 0; + analogReadResolution(12); + analogReference(AR_INTERNAL_3_0); + delay(10); + adcvalue = analogRead(BATTERY_PIN); + return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE * 1000.0) / 4096.0; + } + + const char* getManufacturerName() const override { + return "Seeed Wio WM1110"; + } + + void reboot() override { + NVIC_SystemReset(); + } + + bool startOTAUpdate(const char* id, char reply[]) override; + + void enableSensorPower(bool enable) { + digitalWrite(SENSOR_POWER_PIN, enable ? HIGH : LOW); + if (enable) { + delay(100); + } + } +}; + +#endif + diff --git a/variants/wio_wm1110/platformio.ini b/variants/wio_wm1110/platformio.ini new file mode 100644 index 00000000..313430ff --- /dev/null +++ b/variants/wio_wm1110/platformio.ini @@ -0,0 +1,85 @@ +[wio_wm1110] +extends = nrf52_base +board = seeed-xiao-afruitnrf52-nrf52840 +board_build.ldscript = boards/nrf52840_s140_v7.ld +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I lib/nrf52/s140_nrf52_7.3.0_API/include + -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 + -I variants/wio_wm1110 + -D NRF52_PLATFORM + -D WIO_WM1110 +; -D MESH_DEBUG=1 + -D RADIO_CLASS=CustomLR1110 + -D WRAPPER_CLASS=CustomLR1110Wrapper + -D LORA_TX_POWER=22 + -D RX_BOOSTED_GAIN=true + -D P_LORA_DIO_1=40 + -D P_LORA_RESET=42 + -D P_LORA_BUSY=43 + -D P_LORA_NSS=44 + -D P_LORA_SCLK=45 + -D P_LORA_MOSI=46 + -D P_LORA_MISO=47 + -D LR11X0_DIO_AS_RF_SWITCH=true + -D LR11X0_DIO3_TCXO_VOLTAGE=1.8 + -D RF_SWITCH_TABLE +build_src_filter = ${nrf52_base.build_src_filter} + + + + + +<../variants/wio_wm1110> +debug_tool = jlink +upload_protocol = jlink +lib_deps = ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit LIS3DH @ ^1.2.4 + adafruit/Adafruit SHT4x Library @ ^1.0.4 + +[env:wio_wm1110_repeater] +extends = wio_wm1110 +build_flags = + ${wio_wm1110.build_flags} + -D ADVERT_NAME='"WM1110 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${wio_wm1110.build_src_filter} + +<../examples/simple_repeater/*.cpp> + +[env:wio_wm1110_room_server] +extends = wio_wm1110 +build_flags = + ${wio_wm1110.build_flags} + -D ADVERT_NAME='"WM1110 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${wio_wm1110.build_src_filter} + +<../examples/simple_room_server/*.cpp> + +[env:wio_wm1110_companion_radio_ble] +extends = wio_wm1110 +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld +board_upload.maximum_size = 708608 +build_flags = + ${wio_wm1110.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D QSPIFLASH=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${wio_wm1110.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${wio_wm1110.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/wio_wm1110/target.cpp b/variants/wio_wm1110/target.cpp new file mode 100644 index 00000000..cc302a85 --- /dev/null +++ b/variants/wio_wm1110/target.cpp @@ -0,0 +1,110 @@ +#include +#include "target.h" +#include +#include + +class WM1110LocationProvider : public LocationProvider { +public: + long getLatitude() override { return 0; } + long getLongitude() override { return 0; } + long getAltitude() override { return 0; } + long satellitesCount() override { return 0; } + bool isValid() override { return false; } + long getTimestamp() override { return 0; } + void sendSentence(const char* sentence) override {} + void reset() override {} + void begin() override {} + void stop() override {} + void loop() override {} + bool isEnabled() override { return false; } +}; + +WioWM1110Board board; + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock rtc_clock; +WM1110LocationProvider location_provider; +EnvironmentSensorManager sensors(location_provider); + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +#ifdef RF_SWITCH_TABLE +static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = { + RADIOLIB_LR11X0_DIO5, + RADIOLIB_LR11X0_DIO6, + RADIOLIB_LR11X0_DIO7, + RADIOLIB_LR11X0_DIO8, + RADIOLIB_NC +}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 DIO7 DIO8 + { LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW }}, + { LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH }}, + { LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH }}, + { LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH }}, + { LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW }}, + { LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW }}, + { LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW }}, + END_OF_MODE_TABLE, +}; +#endif + +bool radio_init() { + board.enableSensorPower(true); + +#ifdef LR11X0_DIO3_TCXO_VOLTAGE + float tcxo = LR11X0_DIO3_TCXO_VOLTAGE; +#else + float tcxo = 1.8f; +#endif + + SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); + SPI.begin(); + + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + + radio.setCRC(2); + radio.explicitHeader(); + +#ifdef RF_SWITCH_TABLE + radio.setRfSwitchTable(rfswitch_dios, rfswitch_table); +#endif + +#ifdef RX_BOOSTED_GAIN + radio.setRxBoostedGainMode(RX_BOOSTED_GAIN); +#endif + + return true; // success +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} + diff --git a/variants/wio_wm1110/target.h b/variants/wio_wm1110/target.h new file mode 100644 index 00000000..9bd4a22b --- /dev/null +++ b/variants/wio_wm1110/target.h @@ -0,0 +1,21 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include "WioWM1110Board.h" +#include +#include +#include + +extern WioWM1110Board board; +extern WRAPPER_CLASS radio_driver; +extern VolatileRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); + diff --git a/variants/wio_wm1110/variant.cpp b/variants/wio_wm1110/variant.cpp new file mode 100644 index 00000000..9691a304 --- /dev/null +++ b/variants/wio_wm1110/variant.cpp @@ -0,0 +1,92 @@ +/* + * variant.cpp - Seeed Wio WM1110 Dev Board + * Pin mapping for nRF52840 + */ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] = +{ + 0, // P0.00 + 1, // P0.01 + 2, // P0.02, AIN0, SENSOR_AIN_0 + 3, // P0.03, AIN1, SENSOR_AIN_1 + 4, // P0.04, AIN2, SENSOR_AIN_2 + 5, // P0.05, AIN3, SENSOR_AIN_3 + 6, // P0.06, PIN_SERIAL2_RX, SENSOR_RXD + 7, // P0.07, SENSOR_POWER_PIN + 8, // P0.08, PIN_SERIAL2_TX, SENSOR_TXD + 9, // P0.09 + 10, // P0.10 + 11, // P0.11, LIS3DH_INT_PIN_1, SENSOR_INT_1 + 12, // P0.12, LIS3DH_INT_PIN_2, SENSOR_INT_2 + 13, // P0.13, LED_GREEN, USER_LED_G + 14, // P0.14, LED_RED, USER_LED_R + 15, // P0.15 + 16, // P0.16 + 17, // P0.17 + 18, // P0.18 + 19, // P0.19 + 20, // P0.20 + 21, // P0.21 + 22, // P0.22, PIN_SERIAL1_RX, DEBUG_RX_PIN + 23, // P0.23 + 24, // P0.24, PIN_SERIAL1_TX, DEBUG_TX_PIN + 25, // P0.25 + 26, // P0.26, PIN_WIRE_SCL, SENSOR_SCL + 27, // P0.27, PIN_WIRE_SDA, SENSOR_SDA + 28, // P0.28, AIN4, SENSOR_AIN_4 + 29, // P0.29, AIN5, SENSOR_AIN_5 + 30, // P0.30, AIN6, SENSOR_AIN_6 + 31, // P0.31, AIN7, SENSOR_AIN_7, BATTERY_PIN + 32, // P1.00 + 33, // P1.01 + 34, // P1.02 + 35, // P1.03 + 36, // P1.04 + 37, // P1.05, LR1110_GNSS_ANT_PIN + 38, // P1.06 + 39, // P1.07 + 40, // P1.08, LORA_DIO_1, LR1110_IRQ_PIN + 41, // P1.09 + 42, // P1.10, LORA_RESET, LR1110_NRESET_PIN + 43, // P1.11, LORA_BUSY, LR1110_BUSY_PIN + 44, // P1.12, PIN_SPI_NSS, LR1110_SPI_NSS_PIN + 45, // P1.13, PIN_SPI_SCK, LR1110_SPI_SCK_PIN + 46, // P1.14, PIN_SPI_MOSI, LR1110_SPI_MOSI_PIN + 47, // P1.15, PIN_SPI_MISO, LR1110_SPI_MISO_PIN + 255, // NRFX_SPIM_PIN_NOT_USED +}; + +void initVariant() +{ + // All pins output HIGH by default. + // https://github.com/Seeed-Studio/Adafruit_nRF52_Arduino/blob/fab7d30a997a1dfeef9d1d59bfb549adda73815a/cores/nRF5/wiring.c#L65-L69 + + // Set analog input pins + pinMode(BATTERY_PIN, INPUT); + pinMode(SENSOR_AIN_0, INPUT); + pinMode(SENSOR_AIN_1, INPUT); + pinMode(SENSOR_AIN_2, INPUT); + pinMode(SENSOR_AIN_3, INPUT); + pinMode(SENSOR_AIN_4, INPUT); + pinMode(SENSOR_AIN_5, INPUT); + pinMode(SENSOR_AIN_6, INPUT); + + // Sensor interrupts as inputs + pinMode(LIS3DH_INT_PIN_1, INPUT); + pinMode(LIS3DH_INT_PIN_2, INPUT); + + // Set output pins + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_RED, OUTPUT); + pinMode(SENSOR_POWER_PIN, OUTPUT); + + // Initialize outputs to safe states + digitalWrite(LED_GREEN, HIGH); // Power indicator LED on + digitalWrite(LED_RED, LOW); + digitalWrite(SENSOR_POWER_PIN, LOW); // Sensors powered off initially +} + diff --git a/variants/wio_wm1110/variant.h b/variants/wio_wm1110/variant.h new file mode 100644 index 00000000..cc72c328 --- /dev/null +++ b/variants/wio_wm1110/variant.h @@ -0,0 +1,145 @@ +/* + * variant.h - Seeed Wio WM1110 Dev Board + * nRF52840 + LR1110 (LoRa + GNSS + WiFi Scanner) + */ + +#pragma once + +#include "WVariant.h" + +//////////////////////////////////////////////////////////////////////////////// +// Low frequency clock source + +#define USE_LFXO // 32.768 kHz crystal oscillator +#define VARIANT_MCK (64000000ul) + +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define BATTERY_PIN (31) // AIN7 +#define BATTERY_IMMUTABLE +#define ADC_MULTIPLIER (2.0F) + +#define ADC_RESOLUTION (14) +#define BATTERY_SENSE_RES (12) + +#define AREF_VOLTAGE (3.0) + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (8) +#define NUM_ANALOG_OUTPUTS (0) + +//////////////////////////////////////////////////////////////////////////////// +// UART pin definition + +#define PIN_SERIAL1_RX (22) +#define PIN_SERIAL1_TX (24) + +#define PIN_SERIAL2_RX (6) +#define PIN_SERIAL2_TX (8) + +//////////////////////////////////////////////////////////////////////////////// +// I2C pin definition + +#define HAS_WIRE (1) +#define WIRE_INTERFACES_COUNT (1) + +#define PIN_WIRE_SDA (27) +#define PIN_WIRE_SCL (26) +#define I2C_NO_RESCAN + +#define SENSOR_POWER_PIN (7) + +#define HAS_LIS3DH (1) +#define LIS3DH_INT_PIN_1 (11) +#define LIS3DH_INT_PIN_2 (12) + +#define HAS_SHT41 (1) + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT (1) + +#define PIN_SPI_MISO (47) +#define PIN_SPI_MOSI (46) +#define PIN_SPI_SCK (45) +#define PIN_SPI_NSS (44) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs + +#define LED_BUILTIN (13) +#define LED_GREEN (13) +#define LED_RED (14) +#define LED_BLUE LED_RED +#define LED_PIN LED_GREEN + +#define LED_STATE_ON HIGH + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (-1) +#define BUTTON_PIN PIN_BUTTON1 + +//////////////////////////////////////////////////////////////////////////////// +// LR1110 LoRa Radio + GNSS + WiFi + +#define LORA_DIO_1 (40) // P1.8 - LR1110_IRQ_PIN +#define LORA_NSS (PIN_SPI_NSS) // P1.12 +#define LORA_RESET (42) // P1.10 - LR1110_NRESET_PIN +#define LORA_BUSY (43) // P1.11 - LR1110_BUSY_PIN +#define LORA_SCLK (PIN_SPI_SCK) // P1.13 +#define LORA_MISO (PIN_SPI_MISO) // P1.15 +#define LORA_MOSI (PIN_SPI_MOSI) // P1.14 +#define LORA_CS PIN_SPI_NSS // P1.12 + +// LR1110 specific settings +#define LR11X0_DIO_AS_RF_SWITCH true +#define LR11X0_DIO3_TCXO_VOLTAGE 1.8 +#define LR1110_GNSS_ANT_PIN (37) // P1.5 + +// Pin aliases for LR1110 driver compatibility +#define LR1110_IRQ_PIN LORA_DIO_1 +#define LR1110_NRESET_PIN LORA_RESET +#define LR1110_BUSY_PIN LORA_BUSY +#define LR1110_SPI_NSS_PIN LORA_CS +#define LR1110_SPI_SCK_PIN LORA_SCLK +#define LR1110_SPI_MOSI_PIN LORA_MOSI +#define LR1110_SPI_MISO_PIN LORA_MISO + +//////////////////////////////////////////////////////////////////////////////// +// Analog Input Pins + +#define SENSOR_AIN_0 (2) +#define SENSOR_AIN_1 (3) +#define SENSOR_AIN_2 (4) +#define SENSOR_AIN_3 (5) +#define SENSOR_AIN_4 (28) +#define SENSOR_AIN_5 (29) +#define SENSOR_AIN_6 (30) +#define SENSOR_AIN_7 (31) + +static const uint8_t A0 = SENSOR_AIN_0; +static const uint8_t A1 = SENSOR_AIN_1; +static const uint8_t A2 = SENSOR_AIN_2; +static const uint8_t A3 = SENSOR_AIN_3; +static const uint8_t A4 = SENSOR_AIN_4; +static const uint8_t A5 = SENSOR_AIN_5; +static const uint8_t A6 = SENSOR_AIN_6; +static const uint8_t A7 = SENSOR_AIN_7; + +//////////////////////////////////////////////////////////////////////////////// +// GPS/GNSS + +#define HAS_GPS 0 +#define PIN_GPS_TX (-1) +#define PIN_GPS_RX (-1) +#define GPS_EN (-1) +#define GPS_RESET (-1) + From 87677fda76dc5c83cbc6feac8f74d041fab2597e Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 22 Oct 2025 15:15:29 +0200 Subject: [PATCH 062/115] allow spreading factor from 5 and bandwidth from 7.8kHz --- examples/companion_radio/MyMesh.cpp | 4 ++-- src/helpers/CommonCLI.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 3772fc1b..02f1a21d 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -706,8 +706,8 @@ void MyMesh::begin(bool has_display) { _prefs.rx_delay_base = constrain(_prefs.rx_delay_base, 0, 20.0f); _prefs.airtime_factor = constrain(_prefs.airtime_factor, 0, 9.0f); _prefs.freq = constrain(_prefs.freq, 400.0f, 2500.0f); - _prefs.bw = constrain(_prefs.bw, 62.5f, 500.0f); - _prefs.sf = constrain(_prefs.sf, 7, 12); + _prefs.bw = constrain(_prefs.bw, 7.8f, 500.0f); + _prefs.sf = constrain(_prefs.sf, 5, 12); _prefs.cr = constrain(_prefs.cr, 5, 8); _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b8bb698a..32ef632d 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -77,8 +77,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_tx_delay_factor = constrain(_prefs->direct_tx_delay_factor, 0, 2.0f); _prefs->airtime_factor = constrain(_prefs->airtime_factor, 0, 9.0f); _prefs->freq = constrain(_prefs->freq, 400.0f, 2500.0f); - _prefs->bw = constrain(_prefs->bw, 62.5f, 500.0f); - _prefs->sf = constrain(_prefs->sf, 7, 12); + _prefs->bw = constrain(_prefs->bw, 7.8f, 500.0f); + _prefs->sf = constrain(_prefs->sf, 5, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); From ac1513129627e505544264b8ff46282a4a46f3c1 Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Wed, 22 Oct 2025 16:17:06 -0400 Subject: [PATCH 063/115] Add support for bmp085/bmp180 temperature/pressure sensor --- platformio.ini | 2 ++ .../sensors/EnvironmentSensorManager.cpp | 29 +++++++++++++++++++ .../sensors/EnvironmentSensorManager.h | 1 + 3 files changed, 32 insertions(+) diff --git a/platformio.ini b/platformio.ini index 1200f599..56a51719 100644 --- a/platformio.ini +++ b/platformio.ini @@ -128,6 +128,7 @@ build_flags = -D ENV_INCLUDE_MLX90614=1 -D ENV_INCLUDE_VL53L0X=1 -D ENV_INCLUDE_BME680=1 + -D ENV_INCLUDE_BMP085=1 lib_deps = adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 @@ -143,3 +144,4 @@ lib_deps = adafruit/Adafruit_VL53L0X @ ^1.2.4 stevemarple/MicroNMEA @ ^2.0.6 adafruit/Adafruit BME680 Library @ ^2.0.4 + adafruit/Adafruit BMP085 Library @ ^1.2.4 diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index bb70c0b5..41d50e92 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -15,6 +15,12 @@ static Adafruit_BME680 BME680; #endif +#ifdef ENV_INCLUDE_BMP085 +#define TELEM_BMP085_SEALEVELPRESSURE_HPA (1013.25) +#include +static Adafruit_BMP085 BMP085; +#endif + #if ENV_INCLUDE_AHTX0 #define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address #include @@ -305,6 +311,21 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_BMP085 + // first arg is MODE + // 0: ULTRALOWPOWER + // 1: STANDARD + // 2: HIGHRES + // 3: ULTRAHIGHRES + if (BMP085.begin(1, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found sensor BMP085"); + BMP085_initialized = true; + } else { + BMP085_initialized = false; + MESH_DEBUG_PRINTLN("BMP085 was not found at I2C address %02X", 0x77); + } + #endif + return true; } @@ -447,6 +468,14 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_BMP085 + if (BMP085_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP085.readTemperature()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP085.readPressure() / 100); + telemetry.addAltitude(TELEM_CHANNEL_SELF, BMP085.readAltitude(TELEM_BMP085_SEALEVELPRESSURE_HPA * 100)); + } + #endif + } return true; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 133d2650..5f1c08e2 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -21,6 +21,7 @@ protected: bool VL53L0X_initialized = false; bool SHT4X_initialized = false; bool BME680_initialized = false; + bool BMP085_initialized = false; bool gps_detected = false; bool gps_active = false; From 4cfbd3bad556abfdb03d5a00023b28536842b59f Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Wed, 22 Oct 2025 16:53:11 -0400 Subject: [PATCH 064/115] Switch BMP085 mode to 0 for ULTRALOWPOWER --- src/helpers/sensors/EnvironmentSensorManager.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 41d50e92..98339105 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -312,12 +312,9 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BMP085 - // first arg is MODE - // 0: ULTRALOWPOWER - // 1: STANDARD - // 2: HIGHRES - // 3: ULTRAHIGHRES - if (BMP085.begin(1, TELEM_WIRE)) { + // First argument is MODE (aka oversampling) + // choose ULTRALOWPOWER + if (BMP085.begin(0, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor BMP085"); BMP085_initialized = true; } else { From 8ca3ed28cf49ed4ee4788454044e9e86eb94a0b9 Mon Sep 17 00:00:00 2001 From: kallanreed <3761006+kallanreed@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:59:43 -0700 Subject: [PATCH 065/115] set PIN_GPS_EN in wismesh tag companion --- variants/rak_wismesh_tag/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini index 37593f61..081cb0d0 100644 --- a/variants/rak_wismesh_tag/platformio.ini +++ b/variants/rak_wismesh_tag/platformio.ini @@ -94,6 +94,7 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 + -D PIN_GPS_EN=34 ; -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 build_src_filter = ${rak_wismesh_tag.build_src_filter} From 2e249e24dcec86e184f0e354a9e3e9b4ce73879b Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Wed, 22 Oct 2025 23:55:51 -0700 Subject: [PATCH 066/115] Updated CayenneLPP to 1.6.1 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 11814a22..916bb464 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,7 +22,7 @@ lib_deps = rweather/Crypto @ ^0.4.0 adafruit/RTClib @ ^2.1.3 melopero/Melopero RV3028 @ ^1.1.0 - electroniccats/CayenneLPP @ 1.4.0 + electroniccats/CayenneLPP @ 1.6.1 build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=869.525 -D LORA_BW=250 From f1824e68b9e64384b941b06fec6ea59596ac09df Mon Sep 17 00:00:00 2001 From: liamcottle Date: Thu, 23 Oct 2025 23:24:40 +1300 Subject: [PATCH 067/115] increase repeater max uptime from 49 days to 136 years --- examples/simple_repeater/MyMesh.cpp | 9 ++++++++- examples/simple_repeater/MyMesh.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 374384bd..f328c752 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -149,7 +149,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t stats.n_packets_recv = radio_driver.getPacketsRecv(); stats.n_packets_sent = radio_driver.getPacketsSent(); stats.total_air_time_secs = getTotalAirTime() / 1000; - stats.total_up_time_secs = _ms->getMillis() / 1000; + stats.total_up_time_secs = uptime_millis / 1000; stats.n_sent_flood = getNumSentFlood(); stats.n_sent_direct = getNumSentDirect(); stats.n_recv_flood = getNumRecvFlood(); @@ -594,6 +594,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc , bridge(&_prefs, _mgr, &rtc) #endif { + last_millis = 0; + uptime_millis = 0; next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; set_radio_at = revert_radio_at = 0; @@ -891,4 +893,9 @@ void MyMesh::loop() { acl.save(_fs); dirty_contacts_expiry = 0; } + + // update uptime + uint32_t now = millis(); + uptime_millis += now - last_millis; + last_millis = now; } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c45c141d..a9ab251e 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -78,6 +78,8 @@ struct NeighbourInfo { class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; + uint32_t last_millis; + uint64_t uptime_millis; unsigned long next_local_advert, next_flood_advert; bool _logging; NodePrefs _prefs; From 273a54f104f3462754aeba6e8ace7b3a694b1f5a Mon Sep 17 00:00:00 2001 From: liamcottle Date: Thu, 23 Oct 2025 23:29:08 +1300 Subject: [PATCH 068/115] increase room server max uptime from 49 days to 136 years --- examples/simple_room_server/MyMesh.cpp | 9 ++++++++- examples/simple_room_server/MyMesh.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index a9ba7998..5d245ba1 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -144,7 +144,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t stats.n_packets_recv = radio_driver.getPacketsRecv(); stats.n_packets_sent = radio_driver.getPacketsSent(); stats.total_air_time_secs = getTotalAirTime() / 1000; - stats.total_up_time_secs = _ms->getMillis() / 1000; + stats.total_up_time_secs = uptime_millis / 1000; stats.n_sent_flood = getNumSentFlood(); stats.n_sent_direct = getNumSentDirect(); stats.n_recv_flood = getNumRecvFlood(); @@ -581,6 +581,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { + last_millis = 0; + uptime_millis = 0; next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; _logging = false; @@ -858,4 +860,9 @@ void MyMesh::loop() { } // TODO: periodically check for OLD/inactive entries in known_clients[], and evict + + // update uptime + uint32_t now = millis(); + uptime_millis += now - last_millis; + last_millis = now; } diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 60ef1e73..88e30bf2 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -88,6 +88,8 @@ struct PostInfo { class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; + uint32_t last_millis; + uint64_t uptime_millis; unsigned long next_local_advert, next_flood_advert; bool _logging; NodePrefs _prefs; From dfb4497c7aab34d2fac2e0964a2b37f2a0cd6958 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 23 Oct 2025 21:44:52 +1100 Subject: [PATCH 069/115] * T114: enabled GPS page in UITask --- variants/heltec_t114/platformio.ini | 1 + variants/heltec_t114/target.cpp | 3 +-- variants/heltec_t114/target.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 53fd5e02..c482a30a 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -170,6 +170,7 @@ build_flags = -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 + -D ENV_INCLUDE_GPS=1 ; enable the GPS page in UI ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/heltec_t114/target.cpp b/variants/heltec_t114/target.cpp index d2fa6c4c..5b786437 100644 --- a/variants/heltec_t114/target.cpp +++ b/variants/heltec_t114/target.cpp @@ -74,11 +74,10 @@ bool T114SensorManager::begin() { if (gps_detected) { MESH_DEBUG_PRINTLN("GPS detected"); - digitalWrite(GPS_EN, LOW); // Power off GPS until the setting is changed } else { MESH_DEBUG_PRINTLN("No GPS detected"); - digitalWrite(GPS_EN, LOW); } + digitalWrite(GPS_EN, LOW); // Power off GPS until the setting is changed return true; } diff --git a/variants/heltec_t114/target.h b/variants/heltec_t114/target.h index 1876aadc..6306cd69 100644 --- a/variants/heltec_t114/target.h +++ b/variants/heltec_t114/target.h @@ -30,6 +30,7 @@ public: bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; void loop() override; + LocationProvider* getLocationProvider() override { return gps_detected ? _location : NULL; } int getNumSettings() const override; const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; From 2981fc70e10102b2b9eabbcd3b9ee7c2d608362f Mon Sep 17 00:00:00 2001 From: Woodie-07 Date: Fri, 24 Oct 2025 20:12:02 +0100 Subject: [PATCH 070/115] new workaround --- src/helpers/radiolib/CustomLR1110.h | 69 +++-------------------------- 1 file changed, 7 insertions(+), 62 deletions(-) diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index f723bb7f..cdf3cc7c 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -10,74 +10,19 @@ class CustomLR1110 : public LR1110 { public: CustomLR1110(Module *mod) : LR1110(mod) { } - uint8_t shiftCount = 0; - - int16_t standby() override { - // tx resets the shift, standby is called on tx completion - // this might not actually be what resets it, but it seems to work - // more investigation needed - this->shiftCount = 0; - return LR1110::standby(); - } - size_t getPacketLength(bool update) override { size_t len = LR1110::getPacketLength(update); - if (len == 0) { - uint32_t irq = getIrqStatus(); - if (irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) { - MESH_DEBUG_PRINTLN("LR1110: got header err, assuming shift"); - this->shiftCount += 4; // uint8 will loop around to 0 at 256, perfect as rx buffer is 256 bytes - } else { - MESH_DEBUG_PRINTLN("LR1110: got zero-length packet without header err irq"); - } + if (len == 0 && getIrqStatus() & RADIOLIB_LR11X0_IRQ_HEADER_ERR) { + // we've just recieved a corrupted packet + // this may have triggered a bug causing subsequent packets to be shifted + // call standby() to return radio to known-good state + // recvRaw will call startReceive() to restart rx + MESH_DEBUG_PRINTLN("LR1110: got header err, calling standby()"); + standby(); } return len; } - int16_t readData(uint8_t *data, size_t len) override { - // check active modem - uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; - int16_t state = getPacketType(&modem); - RADIOLIB_ASSERT(state); - if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && - (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - // check integrity CRC - uint32_t irq = getIrqStatus(); - int16_t crcState = RADIOLIB_ERR_NONE; - // Report CRC mismatch when there's a payload CRC error, or a header error and no valid header (to avoid false alarm from previous packet) - if((irq & RADIOLIB_LR11X0_IRQ_CRC_ERR) || ((irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID))) { - crcState = RADIOLIB_ERR_CRC_MISMATCH; - } - - // get packet length - // the offset is needed since LR11x0 seems to move the buffer base by 4 bytes on every packet - uint8_t offset = 0; - size_t length = LR1110::getPacketLength(true, &offset); - if((len != 0) && (len < length)) { - // user requested less data than we got, only return what was requested - length = len; - } - - // read packet data - state = readBuffer8(data, length, (uint8_t)(offset + this->shiftCount)); // add shiftCount to offset - only change from radiolib - RADIOLIB_ASSERT(state); - - // clear the Rx buffer - state = clearRxBuffer(); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); - - // check if CRC failed - this is done after reading data to give user the option to keep them - RADIOLIB_ASSERT(crcState); - - return(state); - } - RadioLibTime_t getTimeOnAir(size_t len) override { // calculate number of symbols float N_symbol = 0; From 0e259a63ed07099e9a31c377f077a3ab5dfd9793 Mon Sep 17 00:00:00 2001 From: Woodie-07 Date: Sat, 25 Oct 2025 22:12:30 +0100 Subject: [PATCH 071/115] lr1110 irq fixes fix incorrect irqs used in isReceiving. also remove getTimeOnAir override as fixed upstream --- src/helpers/radiolib/CustomLR1110.h | 60 +---------------------------- 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index cdf3cc7c..2e536de5 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -3,9 +3,6 @@ #include #include "MeshCore.h" -#define LR1110_IRQ_HAS_PREAMBLE 0b0000000100 // 4 4 valid LoRa header received -#define LR1110_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received - class CustomLR1110 : public LR1110 { public: CustomLR1110(Module *mod) : LR1110(mod) { } @@ -22,63 +19,10 @@ class CustomLR1110 : public LR1110 { } return len; } - - RadioLibTime_t getTimeOnAir(size_t len) override { - // calculate number of symbols - float N_symbol = 0; - if(this->codingRate <= RADIOLIB_LR11X0_LORA_CR_4_8_SHORT) { - // legacy coding rate - nice and simple - // get SF coefficients - float coeff1 = 0; - int16_t coeff2 = 0; - int16_t coeff3 = 0; - if(this->spreadingFactor < 7) { - // SF5, SF6 - coeff1 = 6.25; - coeff2 = 4*this->spreadingFactor; - coeff3 = 4*this->spreadingFactor; - } else if(this->spreadingFactor < 11) { - // SF7. SF8, SF9, SF10 - coeff1 = 4.25; - coeff2 = 4*this->spreadingFactor + 8; - coeff3 = 4*this->spreadingFactor; - } else { - // SF11, SF12 - coeff1 = 4.25; - coeff2 = 4*this->spreadingFactor + 8; - coeff3 = 4*(this->spreadingFactor - 2); - } - - // get CRC length - int16_t N_bitCRC = 16; - if(this->crcTypeLoRa == RADIOLIB_LR11X0_LORA_CRC_DISABLED) { - N_bitCRC = 0; - } - - // get header length - int16_t N_symbolHeader = 20; - if(this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) { - N_symbolHeader = 0; - } - - // calculate number of LoRa preamble symbols - NO! Lora preamble is already in symbols - // uint32_t N_symbolPreamble = (this->preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((this->preambleLengthLoRa & 0xF0) >> 4)); - - // calculate the number of symbols - nope - // N_symbol = (float)N_symbolPreamble + coeff1 + 8.0f + ceilf((float)RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * (float)(this->codingRate + 4); - // calculate the number of symbols - using only preamblelora because it's already in symbols - N_symbol = (float)preambleLengthLoRa + coeff1 + 8.0f + ceilf((float)RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * (float)(this->codingRate + 4); - } else { - // long interleaving - not needed for this modem - } - - // get time-on-air in us - return(((uint32_t(1) << this->spreadingFactor) / this->bandwidthKhz) * N_symbol * 1000.0f); -} - + bool isReceiving() { uint16_t irq = getIrqStatus(); - bool detected = ((irq & LR1110_IRQ_HEADER_VALID) || (irq & LR1110_IRQ_HAS_PREAMBLE)); + bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED)); return detected; } }; \ No newline at end of file From d4eb04d6e96ad2e776c5e1d7ec00a5efb6c67239 Mon Sep 17 00:00:00 2001 From: WattleFoxxo Date: Wed, 29 Oct 2025 15:20:31 +1100 Subject: [PATCH 072/115] Switch xiao rp2040 to std init --- variants/xiao_rp2040/target.cpp | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/variants/xiao_rp2040/target.cpp b/variants/xiao_rp2040/target.cpp index a801aae8..b7c19975 100644 --- a/variants/xiao_rp2040/target.cpp +++ b/variants/xiao_rp2040/target.cpp @@ -20,34 +20,12 @@ SensorManager sensors; bool radio_init() { rtc_clock.begin(Wire); -#ifdef SX126X_DIO3_TCXO_VOLTAGE - float tcxo = SX126X_DIO3_TCXO_VOLTAGE; +#if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); #else - float tcxo = 1.6f; + return radio.std_init(); #endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); - - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - - radio.setCRC(1); - -#ifdef SX126X_CURRENT_LIMIT - radio.setCurrentLimit(SX126X_CURRENT_LIMIT); -#endif - -#ifdef SX126X_DIO2_AS_RF_SWITCH - radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); -#endif - -#ifdef SX126X_RX_BOOSTED_GAIN - radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); -#endif - - return true; // success } uint32_t radio_get_rng_seed() { From 81ab9446824aef47a3cb252f87d963c97784ddc4 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 7 Oct 2025 09:45:45 -0700 Subject: [PATCH 073/115] Adds serial commands to get stats - Added formatStatsReply, formatRadioStatsReply, and formatPacketStatsReply methods in MyMesh for both simple_repeater, simple_room_server, and simple_sensor. - Updated CommonCLI to handle new stats commands. --- examples/simple_repeater/MyMesh.cpp | 13 +++++++ examples/simple_repeater/MyMesh.h | 4 ++ examples/simple_room_server/MyMesh.cpp | 13 +++++++ examples/simple_room_server/MyMesh.h | 4 ++ examples/simple_sensor/SensorMesh.cpp | 13 +++++++ examples/simple_sensor/SensorMesh.h | 4 ++ src/helpers/CommonCLI.cpp | 6 +++ src/helpers/CommonCLI.h | 3 ++ src/helpers/StatsFormatHelper.h | 54 ++++++++++++++++++++++++++ 9 files changed, 114 insertions(+) create mode 100644 src/helpers/StatsFormatHelper.h diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f328c752..abd284ff 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -787,6 +787,19 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) { #endif } +void MyMesh::formatStatsReply(char *reply) { + StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr); +} + +void MyMesh::formatRadioStatsReply(char *reply) { + StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime()); +} + +void MyMesh::formatPacketStatsReply(char *reply) { + StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(), + getNumRecvFlood(), getNumRecvDirect()); +} + void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index a9ab251e..694e8ff9 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #ifdef WITH_BRIDGE @@ -183,6 +184,9 @@ public: void setTxPower(uint8_t power_dbm) override; void formatNeighborsReply(char *reply) override; void removeNeighbor(const uint8_t* pubkey, int key_len) override; + void formatStatsReply(char *reply) override; + void formatRadioStatsReply(char *reply) override; + void formatPacketStatsReply(char *reply) override; mesh::LocalIdentity& getSelfId() override { return self_id; } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 5d245ba1..592861bf 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -729,6 +729,19 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } +void MyMesh::formatStatsReply(char *reply) { + StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr); +} + +void MyMesh::formatRadioStatsReply(char *reply) { + StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime()); +} + +void MyMesh::formatPacketStatsReply(char *reply) { + StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(), + getNumRecvFlood(), getNumRecvDirect()); +} + void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { while (*command == ' ') command++; // skip leading spaces diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 88e30bf2..d149b37b 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -192,6 +193,9 @@ public: void formatNeighborsReply(char *reply) override { strcpy(reply, "not supported"); } + void formatStatsReply(char *reply) override; + void formatRadioStatsReply(char *reply) override; + void formatPacketStatsReply(char *reply) override; mesh::LocalIdentity& getSelfId() override { return self_id; } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 8f8a11fe..f914a6b6 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -769,6 +769,19 @@ void SensorMesh::setTxPower(uint8_t power_dbm) { radio_set_tx_power(power_dbm); } +void SensorMesh::formatStatsReply(char *reply) { + StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr); +} + +void SensorMesh::formatRadioStatsReply(char *reply) { + StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime()); +} + +void SensorMesh::formatPacketStatsReply(char *reply) { + StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(), + getNumRecvFlood(), getNumRecvDirect()); +} + float SensorMesh::getTelemValue(uint8_t channel, uint8_t type) { auto buf = telemetry.getBuffer(); uint8_t size = telemetry.getSize(); diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index cdc3940c..ba55bc70 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,9 @@ public: void formatNeighborsReply(char *reply) override { strcpy(reply, "not supported"); } + void formatStatsReply(char *reply) override; + void formatRadioStatsReply(char *reply) override; + void formatPacketStatsReply(char *reply) override; mesh::LocalIdentity& getSelfId() override { return self_id; } void saveIdentity(const mesh::LocalIdentity& new_id) override; void clearStats() override { } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b8bb698a..eac2698e 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -663,6 +663,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else if (sender_timestamp == 0 && memcmp(command, "log", 3) == 0) { _callbacks->dumpLogFile(); strcpy(reply, " EOF"); + } else if (sender_timestamp == 0 && memcmp(command, "stats-packets", 13) == 0 && (command[13] == 0 || command[13] == ' ')) { + _callbacks->formatPacketStatsReply(reply); + } else if (sender_timestamp == 0 && memcmp(command, "stats-radio", 11) == 0 && (command[11] == 0 || command[11] == ' ')) { + _callbacks->formatRadioStatsReply(reply); + } else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) { + _callbacks->formatStatsReply(reply); } else { strcpy(reply, "Unknown command"); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ea59aa92..3cfca46c 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -66,6 +66,9 @@ public: virtual void removeNeighbor(const uint8_t* pubkey, int key_len) { // no op by default }; + virtual void formatStatsReply(char *reply) = 0; + virtual void formatRadioStatsReply(char *reply) = 0; + virtual void formatPacketStatsReply(char *reply) = 0; virtual mesh::LocalIdentity& getSelfId() = 0; virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; diff --git a/src/helpers/StatsFormatHelper.h b/src/helpers/StatsFormatHelper.h new file mode 100644 index 00000000..d0107f3b --- /dev/null +++ b/src/helpers/StatsFormatHelper.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Mesh.h" + +class StatsFormatHelper { +public: + static void formatCoreStats(char* reply, + mesh::MainBoard& board, + mesh::MillisecondClock& ms, + uint16_t err_flags, + mesh::PacketManager* mgr) { + sprintf(reply, + "{\"battery_mv\":%u,\"uptime_secs\":%u,\"errors\":%u,\"queue_len\":%u}", + board.getBattMilliVolts(), + ms.getMillis() / 1000, + err_flags, + mgr->getOutboundCount(0xFFFFFFFF) + ); + } + + template + static void formatRadioStats(char* reply, + mesh::Radio* radio, + RadioDriverType& driver, + uint32_t total_air_time_ms, + uint32_t total_rx_air_time_ms) { + sprintf(reply, + "{\"noise_floor\":%d,\"last_rssi\":%d,\"last_snr\":%.2f,\"tx_air_secs\":%u,\"rx_air_secs\":%u}", + (int16_t)radio->getNoiseFloor(), + (int16_t)driver.getLastRSSI(), + driver.getLastSNR(), + total_air_time_ms / 1000, + total_rx_air_time_ms / 1000 + ); + } + + template + static void formatPacketStats(char* reply, + RadioDriverType& driver, + uint32_t n_sent_flood, + uint32_t n_sent_direct, + uint32_t n_recv_flood, + uint32_t n_recv_direct) { + sprintf(reply, + "{\"recv\":%u,\"sent\":%u,\"flood_tx\":%u,\"direct_tx\":%u,\"flood_rx\":%u,\"direct_rx\":%u}", + driver.getPacketsRecv(), + driver.getPacketsSent(), + n_sent_flood, + n_sent_direct, + n_recv_flood, + n_recv_direct + ); + } +}; From 1bbc2151f10b4ee07344ee613234bb63d6fdd8c0 Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 29 Oct 2025 10:32:39 +0100 Subject: [PATCH 074/115] remove vision master boards because of issues with display drivers --- variants/heltec_e213/platformio.ini | 12 ++++++------ variants/heltec_e290/platformio.ini | 12 ++++++------ variants/heltec_t190/platformio.ini | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index 74598a2d..41824e1c 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -40,7 +40,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E213_companion_radio_ble] +[env:Heltec_E213_companion_ble] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -60,7 +60,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E213_companion_radio_usb] +[env:Heltec_E213_companion_usb] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -78,7 +78,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E213_repeater] +[env:Heltec_E213_rptr] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -95,7 +95,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_E213_repeater_bridge_rs232] +; [env:Heltec_E213_rptr_bridge_rs232] ; extends = Heltec_E213_base ; build_flags = ; ${Heltec_E213_base.build_flags} @@ -119,7 +119,7 @@ lib_deps = ; ${Heltec_E213_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_E213_repeater_bridge_espnow] +[env:Heltec_E213_rptr_bridge_espnow] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -141,7 +141,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_E213_room_server] +[env:Heltec_E213_room_svr] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 3289c975..ccc81b4e 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -34,7 +34,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E290_companion_radio_ble] +[env:Heltec_E290_companion_ble] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -54,7 +54,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E290_companion_radio_usb] +[env:Heltec_E290_companion_usb] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -74,7 +74,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E290_repeater] +[env:Heltec_E290_rptr] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -91,7 +91,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_E290_repeater_bridge_rs232] +; [env:Heltec_E290_rptr_bridge_rs232] ; extends = Heltec_E290_base ; build_flags = ; ${Heltec_E290_base.build_flags} @@ -115,7 +115,7 @@ lib_deps = ; ${Heltec_E290_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_E290_repeater_bridge_espnow] +[env:Heltec_E290_rptr_bridge_espnow] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -137,7 +137,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_E290_room_server] +[env:Heltec_E290_room_svr] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index 72a40dec..b27e7f2b 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -47,7 +47,7 @@ lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit GFX Library @ ^1.12.1 -[env:Heltec_T190_companion_radio_ble] +[env:Heltec_T190_companion_ble] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -65,7 +65,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_T190_companion_radio_usb] +[env:Heltec_T190_companion_usb] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -81,7 +81,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_T190_repeater] +[env:Heltec_T190_rptr] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -96,7 +96,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_T190_repeater_bridge_rs232] +; [env:Heltec_T190_rptr_bridge_rs232] ; extends = Heltec_T190_base ; build_flags = ; ${Heltec_T190_base.build_flags} @@ -118,7 +118,7 @@ lib_deps = ; ${Heltec_T190_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_T190_repeater_bridge_espnow] +[env:Heltec_T190_rptr_bridge_espnow] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -138,7 +138,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_T190_room_server] +[env:Heltec_T190_room_svr] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} From 1c052d8ad2097bbb72816d9e07dc93b2e0924090 Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 29 Oct 2025 13:14:27 +0100 Subject: [PATCH 075/115] use different strategy in renaming the envs in order to avoid building --- variants/heltec_e213/platformio.ini | 12 ++++++------ variants/heltec_e290/platformio.ini | 10 +++++----- variants/heltec_t190/platformio.ini | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index 41824e1c..93bdc000 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -40,7 +40,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E213_companion_ble] +[env:Heltec_E213_companion_radio_ble_] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -60,7 +60,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E213_companion_usb] +[env:Heltec_E213_companion_radio_usb_] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -78,7 +78,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E213_rptr] +[env:Heltec_E213_repeater_] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -95,7 +95,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_E213_rptr_bridge_rs232] +; [env:Heltec_E213_repeater_bridge_rs232_] ; extends = Heltec_E213_base ; build_flags = ; ${Heltec_E213_base.build_flags} @@ -119,7 +119,7 @@ lib_deps = ; ${Heltec_E213_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_E213_rptr_bridge_espnow] +[env:Heltec_E213_repeater_bridge_espnow_] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -141,7 +141,7 @@ lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_E213_room_svr] +[env:Heltec_E213_room_server_] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index ccc81b4e..2039b526 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -34,7 +34,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E290_companion_ble] +[env:Heltec_E290_companion_ble_] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -54,7 +54,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E290_companion_usb] +[env:Heltec_E290_companion_usb_] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -91,7 +91,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_E290_rptr_bridge_rs232] +; [env:Heltec_E290_repeater_bridge_rs232_] ; extends = Heltec_E290_base ; build_flags = ; ${Heltec_E290_base.build_flags} @@ -115,7 +115,7 @@ lib_deps = ; ${Heltec_E290_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_E290_rptr_bridge_espnow] +[env:Heltec_E290_repeater_bridge_espnow_] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -137,7 +137,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_E290_room_svr] +[env:Heltec_E290_room_server_] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index b27e7f2b..01e9dfc9 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -47,7 +47,7 @@ lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit GFX Library @ ^1.12.1 -[env:Heltec_T190_companion_ble] +[env:Heltec_T190_companion_radio_ble_] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -65,7 +65,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_T190_companion_usb] +[env:Heltec_T190_companion_radio_usb_] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -81,7 +81,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_T190_rptr] +[env:Heltec_T190_repeater_] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -96,7 +96,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} ${esp32_ota.lib_deps} -; [env:Heltec_T190_rptr_bridge_rs232] +; [env:Heltec_T190_repeater_bridge_rs232_] ; extends = Heltec_T190_base ; build_flags = ; ${Heltec_T190_base.build_flags} @@ -118,7 +118,7 @@ lib_deps = ; ${Heltec_T190_base.lib_deps} ; ${esp32_ota.lib_deps} -[env:Heltec_T190_rptr_bridge_espnow] +[env:Heltec_T190_repeater_bridge_espnow_] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} @@ -138,7 +138,7 @@ lib_deps = ${Heltec_T190_base.lib_deps} ${esp32_ota.lib_deps} -[env:Heltec_T190_room_svr] +[env:Heltec_T190_room_server_] extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} From 377f9ff67dc4902609904199f10f98cf7dbc747b Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 29 Oct 2025 13:22:11 +0100 Subject: [PATCH 076/115] renamed esp32c6 variants, so they are not included in release. added disclaimer about pioarduino builds --- platformio.ini | 1 + variants/lilygo_tlora_c6/platformio.ini | 6 +++--- variants/xiao_c6/platformio.ini | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/platformio.ini b/platformio.ini index b5bce215..b1dd2367 100644 --- a/platformio.ini +++ b/platformio.ini @@ -67,6 +67,7 @@ lib_deps = file://arch/esp32/AsyncElegantOTA ; esp32c6 uses arduino framework 3.x +; WARNING: experimental. pioarduino on esp32c6 needs work - it's not considered stable and has issues. [esp32c6_base] extends = esp32_base platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.12/platform-espressif32.zip diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini index 308c85eb..88a811b5 100644 --- a/variants/lilygo_tlora_c6/platformio.ini +++ b/variants/lilygo_tlora_c6/platformio.ini @@ -30,7 +30,7 @@ build_flags = build_src_filter = ${esp32c6_base.build_src_filter} +<../variants/lilygo_tlora_c6> -[env:LilyGo_Tlora_C6_repeater] +[env:LilyGo_Tlora_C6_repeater_] extends = tlora_c6 build_src_filter = ${tlora_c6.build_src_filter} +<../examples/simple_repeater/*.cpp> @@ -47,7 +47,7 @@ lib_deps = ${tlora_c6.lib_deps} ; ${esp32_ota.lib_deps} -[env:LilyGo_Tlora_C6_room_server] +[env:LilyGo_Tlora_C6_room_server_] extends = tlora_c6 build_src_filter = ${tlora_c6.build_src_filter} +<../examples/simple_room_server> @@ -64,7 +64,7 @@ lib_deps = ${tlora_c6.lib_deps} ; ${esp32_ota.lib_deps} -[env:LilyGo_Tlora_C6_companion_radio_ble] +[env:LilyGo_Tlora_C6_companion_radio_ble_] extends = tlora_c6 build_flags = ${tlora_c6.build_flags} -D MAX_CONTACTS=300 diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 6e963432..9c971083 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -30,7 +30,7 @@ build_src_filter = ${esp32c6_base.build_src_filter} +<../variants/xiao_c6> + -[env:Xiao_C6_repeater] +[env:Xiao_C6_repeater_] extends = Xiao_C6 build_src_filter = ${Xiao_C6.build_src_filter} +<../examples/simple_repeater/*.cpp> @@ -47,7 +47,7 @@ lib_deps = ${Xiao_C6.lib_deps} ; ${esp32_ota.lib_deps} -[env:Xiao_C6_companion_radio_ble] +[env:Xiao_C6_companion_radio_ble_] extends = Xiao_C6 build_flags = ${Xiao_C6.build_flags} -D MAX_CONTACTS=300 @@ -90,10 +90,10 @@ build_flags = -D SX126X_RX_BOOSTED_GAIN=1 -D USE_XIAO_ESP32C6_EXTERNAL_ANTENNA=1 -[env:Meshimi_repeater] +[env:Meshimi_repeater_] extends = Meshimi build_src_filter = ${Meshimi.build_src_filter} - +<../examples/simple_repeater/*.cpp> + +<../examples/simple_repeater/*.cpp> build_flags = ${Meshimi.build_flags} -D ADVERT_NAME='"Meshimi Repeater"' @@ -104,7 +104,7 @@ build_flags = lib_deps = ${Meshimi.lib_deps} -[env:Meshimi_companion_radio_ble] +[env:Meshimi_companion_radio_ble_] extends = Meshimi build_flags = ${Meshimi.build_flags} -D MAX_CONTACTS=300 @@ -115,9 +115,9 @@ build_flags = ${Meshimi.build_flags} -D ENABLE_PRIVATE_KEY_IMPORT=1 -D ENABLE_PRIVATE_KEY_EXPORT=1 build_src_filter = ${Meshimi.build_src_filter} - + - - - +<../examples/companion_radio/*.cpp> + + + - + +<../examples/companion_radio/*.cpp> lib_deps = ${Meshimi.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -147,7 +147,7 @@ build_flags = -USX126X_DIO2_AS_RF_SWITCH -USX126X_DIO3_TCXO_VOLTAGE -[env:WHY2025_badge_repeater] +[env:WHY2025_badge_repeater_] extends = WHY2025_badge build_src_filter = ${WHY2025_badge.build_src_filter} +<../examples/simple_repeater/*.cpp> @@ -164,7 +164,7 @@ lib_deps = ${WHY2025_badge.lib_deps} ; ${esp32_ota.lib_deps} -[env:WHY2025_badge_companion_radio_ble] +[env:WHY2025_badge_companion_radio_ble_] extends = WHY2025_badge build_flags = ${WHY2025_badge.build_flags} -D MAX_CONTACTS=300 From 4aef69662074674da99e431887c5202b154351aa Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 29 Oct 2025 13:27:26 +0100 Subject: [PATCH 077/115] missed one definition --- variants/heltec_e290/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 2039b526..201b3631 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -74,7 +74,7 @@ lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_E290_rptr] +[env:Heltec_E290_repeater_] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} From 8cbcd2271d586997dd3f92011e6c024477c36798 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 29 Oct 2025 23:35:46 +1100 Subject: [PATCH 078/115] * experimental: retransmit delay, removing the 6 'slots' --- examples/simple_repeater/MyMesh.cpp | 4 ++-- examples/simple_room_server/MyMesh.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index abd284ff..9a974565 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -397,11 +397,11 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const { uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.tx_delay_factor); - return getRNG()->nextInt(0, 6) * t; + return getRNG()->nextInt(0, 5*t); } uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); - return getRNG()->nextInt(0, 6) * t; + return getRNG()->nextInt(0, 5*t); } void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 592861bf..cc657800 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -262,11 +262,11 @@ const char *MyMesh::getLogDateTime() { uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.tx_delay_factor); - return getRNG()->nextInt(0, 6) * t; + return getRNG()->nextInt(0, 5*t); } uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); - return getRNG()->nextInt(0, 6) * t; + return getRNG()->nextInt(0, 5*t); } bool MyMesh::allowPacketForward(const mesh::Packet *packet) { From 80f0405600d4510f89742b9694f8802150324fb1 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 30 Oct 2025 00:03:10 +1100 Subject: [PATCH 079/115] * direct.txdelay default now 0.2 (was zero) --- examples/simple_repeater/MyMesh.cpp | 1 + examples/simple_room_server/MyMesh.cpp | 1 + examples/simple_sensor/SensorMesh.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 9a974565..9c9471f5 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -610,6 +610,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.airtime_factor = 1.0; // one half _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f + _prefs.direct_tx_delay_factor = 0.2f; // was zero StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index cc657800..a6ccb140 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -593,6 +593,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.airtime_factor = 1.0; // one half _prefs.rx_delay_base = 0.0f; // off by default, was 10.0 _prefs.tx_delay_factor = 0.5f; // was 0.25f; + _prefs.direct_tx_delay_factor = 0.2f; // was zero StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index f914a6b6..6564c4ef 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -664,6 +664,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.airtime_factor = 1.0; // one half _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f + _prefs.direct_tx_delay_factor = 0.2f; // was zero StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; From 3d9378d91eab4ff94e6225e8ac6d357743ee9684 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 30 Oct 2025 16:45:50 +1100 Subject: [PATCH 080/115] * Fix for VolatileRTCClock wrapping around to initial synced time every 49 days --- examples/companion_radio/main.cpp | 1 + examples/simple_repeater/main.cpp | 1 + examples/simple_room_server/main.cpp | 1 + examples/simple_secure_chat/main.cpp | 3 ++- examples/simple_sensor/main.cpp | 1 + src/MeshCore.h | 5 +++++ src/helpers/ArduinoHelpers.h | 16 ++++++++++++---- src/helpers/AutoDiscoverRTCClock.h | 4 ++++ 8 files changed, 27 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 89adca59..82c8c21d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -227,4 +227,5 @@ void loop() { #ifdef DISPLAY_CLASS ui_task.loop(); #endif + rtc_clock.tick(); } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 7db0e7d4..5843df74 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -114,4 +114,5 @@ void loop() { #ifdef DISPLAY_CLASS ui_task.loop(); #endif + rtc_clock.tick(); } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 8f6b6d58..1a3b4d6e 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -110,4 +110,5 @@ void loop() { #ifdef DISPLAY_CLASS ui_task.loop(); #endif + rtc_clock.tick(); } diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index eac35898..da1bac5b 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -548,7 +548,7 @@ public: StdRNG fast_rng; SimpleMeshTables tables; -MyMesh the_mesh(radio_driver, fast_rng, *new VolatileRTCClock(), tables); // TODO: test with 'rtc_clock' in target.cpp +MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables); void halt() { while (1) ; @@ -587,4 +587,5 @@ void setup() { void loop() { the_mesh.loop(); + rtc_clock.tick(); } diff --git a/examples/simple_sensor/main.cpp b/examples/simple_sensor/main.cpp index 2dacd1b4..a5fcc148 100644 --- a/examples/simple_sensor/main.cpp +++ b/examples/simple_sensor/main.cpp @@ -144,4 +144,5 @@ void loop() { #ifdef DISPLAY_CLASS ui_task.loop(); #endif + rtc_clock.tick(); } diff --git a/src/MeshCore.h b/src/MeshCore.h index 5c7e1760..94bf351d 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -72,6 +72,11 @@ public: */ virtual void setCurrentTime(uint32_t time) = 0; + /** + * override in classes that need to periodically update internal state + */ + virtual void tick() { /* no op */} + uint32_t getCurrentTimeUnique() { uint32_t t = getCurrentTime(); if (t <= last_unique) { diff --git a/src/helpers/ArduinoHelpers.h b/src/helpers/ArduinoHelpers.h index a736c9b0..97596daa 100644 --- a/src/helpers/ArduinoHelpers.h +++ b/src/helpers/ArduinoHelpers.h @@ -4,11 +4,19 @@ #include class VolatileRTCClock : public mesh::RTCClock { - long millis_offset; + uint32_t base_time; + uint64_t accumulator; + unsigned long prev_millis; public: - VolatileRTCClock() { millis_offset = 1715770351; } // 15 May 2024, 8:50pm - uint32_t getCurrentTime() override { return (millis()/1000 + millis_offset); } - void setCurrentTime(uint32_t time) override { millis_offset = time - millis()/1000; } + VolatileRTCClock() { base_time = 1715770351; accumulator = 0; prev_millis = millis(); } // 15 May 2024, 8:50pm + uint32_t getCurrentTime() override { return base_time + accumulator/1000; } + void setCurrentTime(uint32_t time) override { base_time = time; accumulator = 0; prev_millis = millis(); } + + void tick() override { + unsigned long now = millis(); + accumulator += (now - prev_millis); + prev_millis = now; + } }; class ArduinoMillis : public mesh::MillisecondClock { diff --git a/src/helpers/AutoDiscoverRTCClock.h b/src/helpers/AutoDiscoverRTCClock.h index 02eedf52..11364cd8 100644 --- a/src/helpers/AutoDiscoverRTCClock.h +++ b/src/helpers/AutoDiscoverRTCClock.h @@ -14,4 +14,8 @@ public: void begin(TwoWire& wire); uint32_t getCurrentTime() override; void setCurrentTime(uint32_t time) override; + + void tick() override { + _fallback->tick(); // is typically VolatileRTCClock, which now needs tick() + } }; From 96e786fa9e17b1f35406830aa812c60aa812f283 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 30 Oct 2025 19:11:04 +1100 Subject: [PATCH 081/115] * FIX: for divide by zero crash --- examples/simple_repeater/MyMesh.cpp | 4 ++-- examples/simple_room_server/MyMesh.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 9c9471f5..5e62ab6d 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -397,11 +397,11 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const { uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.tx_delay_factor); - return getRNG()->nextInt(0, 5*t); + return getRNG()->nextInt(0, 5*t + 1); } uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); - return getRNG()->nextInt(0, 5*t); + return getRNG()->nextInt(0, 5*t + 1); } void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index a6ccb140..4d953c9c 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -262,11 +262,11 @@ const char *MyMesh::getLogDateTime() { uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.tx_delay_factor); - return getRNG()->nextInt(0, 5*t); + return getRNG()->nextInt(0, 5*t + 1); } uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); - return getRNG()->nextInt(0, 5*t); + return getRNG()->nextInt(0, 5*t + 1); } bool MyMesh::allowPacketForward(const mesh::Packet *packet) { From 5088444f858a3fe7135ed5f77892be69285c76a6 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Thu, 30 Oct 2025 16:33:02 +0100 Subject: [PATCH 082/115] Update Wio WM1110 configuration to disable GPS and clean up location provider code --- variants/wio_wm1110/platformio.ini | 1 + variants/wio_wm1110/target.cpp | 20 +------------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/variants/wio_wm1110/platformio.ini b/variants/wio_wm1110/platformio.ini index 313430ff..ec65e706 100644 --- a/variants/wio_wm1110/platformio.ini +++ b/variants/wio_wm1110/platformio.ini @@ -24,6 +24,7 @@ build_flags = ${nrf52_base.build_flags} -D LR11X0_DIO_AS_RF_SWITCH=true -D LR11X0_DIO3_TCXO_VOLTAGE=1.8 -D RF_SWITCH_TABLE + -D ENV_INCLUDE_GPS=0 build_src_filter = ${nrf52_base.build_src_filter} + + diff --git a/variants/wio_wm1110/target.cpp b/variants/wio_wm1110/target.cpp index cc302a85..c659d708 100644 --- a/variants/wio_wm1110/target.cpp +++ b/variants/wio_wm1110/target.cpp @@ -1,23 +1,6 @@ #include #include "target.h" #include -#include - -class WM1110LocationProvider : public LocationProvider { -public: - long getLatitude() override { return 0; } - long getLongitude() override { return 0; } - long getAltitude() override { return 0; } - long satellitesCount() override { return 0; } - bool isValid() override { return false; } - long getTimestamp() override { return 0; } - void sendSentence(const char* sentence) override {} - void reset() override {} - void begin() override {} - void stop() override {} - void loop() override {} - bool isEnabled() override { return false; } -}; WioWM1110Board board; @@ -26,8 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock rtc_clock; -WM1110LocationProvider location_provider; -EnvironmentSensorManager sensors(location_provider); +EnvironmentSensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 From 0b8159c6e51e0bdd2098b19c049d8fdd8de0afc5 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 31 Oct 2025 11:42:36 +1100 Subject: [PATCH 083/115] refactor DataStore to use openRead() and openWrite() refactored loadPrefsInt(), loadContacts(), loadChannels(), getBlobByKey() and putBlobByKey() to use openRead() and openWrite() --- examples/companion_radio/DataStore.cpp | 40 ++++---------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index f1adb05e..4441d291 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -197,11 +197,7 @@ void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) } void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& node_lat, double& node_lon) { -#if defined(RP2040_PLATFORM) - File file = _fs->open(filename, "r"); -#else - File file = _fs->open(filename); -#endif + File file = openRead(_fs, filename); if (file) { uint8_t pad[8]; @@ -262,16 +258,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ } void DataStore::loadContacts(DataStoreHost* host) { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - if (_getContactsChannelsFS()->exists("/contacts3")) { - File file = _getContactsChannelsFS()->open("/contacts3"); -#elif defined(RP2040_PLATFORM) - if (_fs->exists("/contacts3")) { - File file = _fs->open("/contacts3", "r"); -#else - if (_fs->exists("/contacts3")) { - File file = _fs->open("/contacts3", "r", false); -#endif +File file = openRead(_getContactsChannelsFS(), "/contacts3"); if (file) { bool full = false; while (!full) { @@ -299,7 +286,6 @@ void DataStore::loadContacts(DataStoreHost* host) { } file.close(); } - } } void DataStore::saveContacts(DataStoreHost* host) { @@ -332,16 +318,7 @@ void DataStore::saveContacts(DataStoreHost* host) { } void DataStore::loadChannels(DataStoreHost* host) { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - if (_getContactsChannelsFS()->exists("/channels2")) { - File file = _getContactsChannelsFS()->open("/channels2"); -#elif defined(RP2040_PLATFORM) - if (_fs->exists("/channels2")) { - File file = _fs->open("/channels2", "r"); -#else - if (_fs->exists("/channels2")) { - File file = _fs->open("/channels2", "r", false); -#endif + File file = openRead(_getContactsChannelsFS(), "/channels2"); if (file) { bool full = false; uint8_t channel_idx = 0; @@ -363,7 +340,6 @@ void DataStore::loadChannels(DataStoreHost* host) { } file.close(); } - } } void DataStore::saveChannels(DataStoreHost* host) { @@ -520,7 +496,7 @@ void DataStore::migrateToSecondaryFS() { } uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { - File file = _getContactsChannelsFS()->open("/adv_blobs"); + File file = openRead(_getContactsChannelsFS(), "/adv_blobs"); uint8_t len = 0; // 0 = not found if (file) { BlobRec tmp; @@ -539,7 +515,7 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false; checkAdvBlobFile(); - File file = _getContactsChannelsFS()->open("/adv_blobs", FILE_O_WRITE); + File file = openWrite(_getContactsChannelsFS(), "/adv_blobs"); if (file) { uint32_t pos = 0, found_pos = 0; uint32_t min_timestamp = 0xFFFFFFFF; @@ -583,11 +559,7 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b sprintf(path, "/bl/%s", fname); if (_fs->exists(path)) { -#if defined(RP2040_PLATFORM) - File f = _fs->open(path, "r"); -#else - File f = _fs->open(path); -#endif + File f = openRead(_fs, path); if (f) { int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! f.close(); From 52a3df4977f1a775324814f3115d539a4f6bd6f8 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 31 Oct 2025 15:06:29 +1100 Subject: [PATCH 084/115] revert pubBlobByKey() change --- examples/companion_radio/DataStore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 4441d291..eac027b8 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -515,7 +515,7 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false; checkAdvBlobFile(); - File file = openWrite(_getContactsChannelsFS(), "/adv_blobs"); + File file = _getContactsChannelsFS()->open("/adv_blobs", FILE_O_WRITE); if (file) { uint32_t pos = 0, found_pos = 0; uint32_t min_timestamp = 0xFFFFFFFF; From 7abe6c969382f989e8e160f617073de0e58c65aa Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 31 Oct 2025 16:54:58 +1100 Subject: [PATCH 085/115] * Upping max channel hash conflicts to 4 (was 2) --- src/Mesh.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b055d811..a480a9c3 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -201,9 +201,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (i + 2 >= pkt->payload_len) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); } else if (!_tables->hasSeen(pkt)) { - // scan channels DB, for all matching hashes of 'channel_hash' (max 2 matches supported ATM) - GroupChannel channels[2]; - int num = searchChannelsByHash(&channel_hash, channels, 2); + // scan channels DB, for all matching hashes of 'channel_hash' (max 4 matches supported ATM) + GroupChannel channels[4]; + int num = searchChannelsByHash(&channel_hash, channels, 4); // for each matching channel, try to decrypt data for (int j = 0; j < num; j++) { // decrypt, checking MAC is valid From 7755400a357dcc32eeaded3124735688e5787e93 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 31 Oct 2025 20:40:22 +1100 Subject: [PATCH 086/115] * Companion: Now using transport codes { 0, 0 } when Share contact zero hop. * Repeater: onAdvertRecv(), adverts via Share now NOT added to neighbours table --- examples/simple_repeater/MyMesh.cpp | 11 +++++++++-- src/Mesh.cpp | 13 +++++++++++++ src/Mesh.h | 6 ++++++ src/helpers/BaseChatMesh.cpp | 15 ++++++++++++--- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5e62ab6d..1afad18b 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -448,12 +448,19 @@ void MyMesh::getPeerSharedSecret(uint8_t *dest_secret, int peer_idx) { } } +static bool isShare(const mesh::Packet *packet) { + if (packet->hasTransportCodes()) { + return packet->transport_codes[0] == 0 && packet->transport_codes[1] == 0; // codes { 0, 0 } means 'send to nowhere' + } + return false; +} + void MyMesh::onAdvertRecv(mesh::Packet *packet, const mesh::Identity &id, uint32_t timestamp, const uint8_t *app_data, size_t app_data_len) { mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl - // if this a zero hop advert, add it to neighbours - if (packet->path_len == 0) { + // if this a zero hop advert (and not via 'Share'), add it to neighbours + if (packet->path_len == 0 && !isShare(packet)) { AdvertDataParser parser(app_data, app_data_len); if (parser.isValid() && parser.getType() == ADV_TYPE_REPEATER) { // just keep neigbouring Repeaters putNeighbour(id, timestamp, packet->getSNR()); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a480a9c3..53dc74f5 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -645,4 +645,17 @@ void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) { sendPacket(packet, 0, delay_millis); } +void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis) { + packet->header &= ~PH_ROUTE_MASK; + packet->header |= ROUTE_TYPE_TRANSPORT_DIRECT; + packet->transport_codes[0] = transport_codes[0]; + packet->transport_codes[1] = transport_codes[1]; + + packet->path_len = 0; // path_len of zero means Zero Hop + + _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + + sendPacket(packet, 0, delay_millis); +} + } \ No newline at end of file diff --git a/src/Mesh.h b/src/Mesh.h index a8fdb2a4..cbf1c9cf 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -196,6 +196,12 @@ public: */ void sendZeroHop(Packet* packet, uint32_t delay_millis=0); + /** + * \brief send a locally-generated Packet to just neigbor nodes (zero hops), with specific transort codes + * \param transport_codes array of 2 codes to attach to packet + */ + void sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); + }; } diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index e03dd088..9b1eb1ce 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -68,9 +68,16 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, } // save a copy of raw advert packet (to support "Share..." function) - int plen = packet->writeTo(temp_buf); + int plen; + { + uint8_t save = packet->header; + packet->header &= ~PH_ROUTE_MASK; + packet->header |= ROUTE_TYPE_FLOOD; // make sure transport codes are NOT saved + plen = packet->writeTo(temp_buf); + packet->header = save; + } putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); - + bool is_new = false; if (from == NULL) { if (!isAutoAddEnabled()) { @@ -405,7 +412,9 @@ bool BaseChatMesh::shareContactZeroHop(const ContactInfo& contact) { if (packet == NULL) return false; // no Packets available packet->readFrom(temp_buf, plen); // restore Packet from 'blob' - sendZeroHop(packet); + uint16_t codes[2]; + codes[0] = codes[1] = 0; // { 0, 0 } means 'send this nowhere' + sendZeroHop(packet, codes); return true; // success } From c13b4ae4816c6008d866b22e382955883953cde5 Mon Sep 17 00:00:00 2001 From: Adam Mealings Date: Fri, 31 Oct 2025 13:04:59 +0000 Subject: [PATCH 087/115] Analogue button delay based on millis --- examples/companion_radio/ui-new/UITask.cpp | 21 ++++++++++++--------- examples/companion_radio/ui-new/UITask.h | 4 ++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 7c75a089..086f8259 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -700,15 +700,18 @@ void UITask::loop() { } #endif #if defined(PIN_USER_BTN_ANA) - ev = analog_btn.check(); - if (ev == BUTTON_EVENT_CLICK) { - c = checkDisplayOn(KEY_NEXT); - } else if (ev == BUTTON_EVENT_LONG_PRESS) { - c = handleLongPress(KEY_ENTER); - } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { - c = handleDoubleClick(KEY_PREV); - } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { - c = handleTripleClick(KEY_SELECT); + if (abs(millis() - _analogue_pin_read_millis) > 10) { + ev = analog_btn.check(); + if (ev == BUTTON_EVENT_CLICK) { + c = checkDisplayOn(KEY_NEXT); + } else if (ev == BUTTON_EVENT_LONG_PRESS) { + c = handleLongPress(KEY_ENTER); + } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { + c = handleDoubleClick(KEY_PREV); + } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { + c = handleTripleClick(KEY_SELECT); + } + _analogue_pin_read_millis = millis(); } #endif #if defined(DISP_BACKLIGHT) && defined(BACKLIGHT_BTN) diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index c24d33a4..32d5f3a0 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -40,6 +40,10 @@ class UITask : public AbstractUITask { int last_led_increment = 0; #endif +#ifdef PIN_USER_BTN_ANA + unsigned long _analogue_pin_read_millis = millis(); +#endif + UIScreen* splash; UIScreen* home; UIScreen* msg_preview; From d0caa3be0447bc3eecc3d2a301c56021ba6a3734 Mon Sep 17 00:00:00 2001 From: Devin Carraway Date: Fri, 31 Oct 2025 22:11:24 -0700 Subject: [PATCH 088/115] Fix the sample RAK repeater build target name The actual target doesn't capitalize the 'r' in repeater. --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 732dac43..f2127941 100755 --- a/build.sh +++ b/build.sh @@ -15,8 +15,8 @@ Commands: build-room-server-firmwares: Build all chat room server firmwares for all build targets. Examples: -Build firmware for the "RAK_4631_Repeater" device target -$ sh build.sh build-firmware RAK_4631_Repeater +Build firmware for the "RAK_4631_repeater" device target +$ sh build.sh build-firmware RAK_4631_repeater Build all firmwares for device targets containing the string "RAK_4631" $ sh build.sh build-matching-firmwares From 03fc94901477b4c7fc15fa6757d1a1940d7eaa34 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 3 Nov 2025 14:23:32 +1100 Subject: [PATCH 089/115] * setting up framework for Regions, TransportKeys, etc --- examples/simple_repeater/MyMesh.cpp | 22 +++++++++++++-- examples/simple_repeater/MyMesh.h | 5 ++++ src/helpers/RegionMap.cpp | 35 +++++++++++++++++++++++ src/helpers/RegionMap.h | 39 +++++++++++++++++++++++++ src/helpers/TransportKeyStore.cpp | 44 +++++++++++++++++++++++++++++ src/helpers/TransportKeyStore.h | 23 +++++++++++++++ 6 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/helpers/RegionMap.cpp create mode 100644 src/helpers/RegionMap.h create mode 100644 src/helpers/TransportKeyStore.cpp create mode 100644 src/helpers/TransportKeyStore.h diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 1afad18b..21f5a76b 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -404,6 +404,23 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { return getRNG()->nextInt(0, 5*t + 1); } +mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) { + if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { + auto region = region_map.findMatch(pkt, REGION_ALLOW_FLOOD); + if (region == NULL) { + MESH_DEBUG_PRINTLN("onRecvPacket: unknown transport code for FLOOD packet"); + return ACTION_RELEASE; + } + } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { + if ((region_map.getWildcard().flags & REGION_ALLOW_FLOOD) == 0) { + MESH_DEBUG_PRINTLN("onRecvPacket: wildcard FLOOD packet not allowed"); + return ACTION_RELEASE; + } + } + // otherwise do normal processing + return mesh::Mesh::onRecvPacket(pkt); +} + void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, uint8_t *data, size_t len) { if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin @@ -593,7 +610,7 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store) #if defined(WITH_RS232_BRIDGE) , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) #endif @@ -652,8 +669,9 @@ void MyMesh::begin(FILESYSTEM *fs) { _fs = fs; // load persisted prefs _cli.loadPrefs(_fs); - acl.load(_fs); + // TODO: key_store.begin(); + region_map.load(_fs); #if defined(WITH_BRIDGE) if (_prefs.bridge_enabled) { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 694e8ff9..7e34ef5f 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef WITH_BRIDGE extern AbstractBridge* bridge; @@ -87,6 +88,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { CommonCLI _cli; uint8_t reply_data[MAX_PACKET_PAYLOAD]; ClientACL acl; + TransportKeyStore key_store; + RegionMap region_map; unsigned long dirty_contacts_expiry; #if MAX_NEIGHBOURS NeighbourInfo neighbours[MAX_NEIGHBOURS]; @@ -144,6 +147,8 @@ protected: } #endif + mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override; + void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override; diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp new file mode 100644 index 00000000..4c270ff8 --- /dev/null +++ b/src/helpers/RegionMap.cpp @@ -0,0 +1,35 @@ +#include "RegionMap.h" +#include + +void RegionMap::load(FILESYSTEM* _fs) { + // TODO +} +void RegionMap::save(FILESYSTEM* _fs) { + // TODO +} + +RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { + for (int i = 0; i < num_regions; i++) { + auto region = ®ions[i]; + if (region->flags & mask) { // does region allow this? (per 'mask' param) + TransportKey keys[4]; + int num = _store->loadKeysFor(region->name, region->id, keys, 4); + for (int j = 0; j < num; j++) { + uint16_t code = keys[j].calcTransportCode(packet); + if (packet->transport_codes[0] == code) { // a match!! + return region; + } + } + } + } + return NULL; // no matches +} + +const RegionEntry* RegionMap::findName(const char* name) const { + for (int i = 0; i < num_regions; i++) { + auto region = ®ions[i]; + if (strcmp(name, region->name) == 0) return region; + } + return NULL; // not found +} + diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h new file mode 100644 index 00000000..4620a745 --- /dev/null +++ b/src/helpers/RegionMap.h @@ -0,0 +1,39 @@ +#pragma once + +#include // needed for PlatformIO +#include +#include "TransportKeyStore.h" + +#ifndef MAX_REGION_ENTRIES + #define MAX_REGION_ENTRIES 32 +#endif + +#define REGION_ALLOW_FLOOD 0x01 + +struct RegionEntry { + uint16_t id; + uint16_t parent; + uint8_t flags; + char name[31]; +}; + +class RegionMap { + TransportKeyStore* _store; + uint16_t next_id; + uint16_t num_regions; + RegionEntry regions[MAX_REGION_ENTRIES]; + RegionEntry wildcard; + +public: + RegionMap(TransportKeyStore& store) : _store(&store) { + next_id = 1; num_regions = 0; + wildcard.id = wildcard.parent = 0; + wildcard.flags = REGION_ALLOW_FLOOD; // default behaviour, allow flood + } + void load(FILESYSTEM* _fs); + void save(FILESYSTEM* _fs); + + RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask); + const RegionEntry& getWildcard() const { return wildcard; } + const RegionEntry* findName(const char* name) const; +}; diff --git a/src/helpers/TransportKeyStore.cpp b/src/helpers/TransportKeyStore.cpp new file mode 100644 index 00000000..973ad703 --- /dev/null +++ b/src/helpers/TransportKeyStore.cpp @@ -0,0 +1,44 @@ +#include "TransportKeyStore.h" +#include + +uint16_t TransportKey::calcTransportCode(const mesh::Packet* packet) const { + uint16_t code; + SHA256 sha; + sha.resetHMAC(key, sizeof(key)); + uint8_t type = packet->getPayloadType(); + sha.update(&type, 1); + sha.update(packet->payload, packet->payload_len); + sha.finalizeHMAC(key, sizeof(key), &code, 2); + return code; +} + +int TransportKeyStore::loadKeysFor(const char* name, uint16_t id, TransportKey keys[], int max_num) { + int n = 0; + for (int i = 0; i < num_cache && n < max_num; i++) { // first, check cache + if (cache_ids[i] == id) { + keys[n++] = cache_keys[i]; + } + } + if (n > 0) return n; // cache hit! + + if (*name == '#') { // is a publicly-known hashtag region + SHA256 sha; + sha.update(name, strlen(name)); + sha.finalize(&keys[0], sizeof(keys[0].key)); + n = 1; + } else { + // TODO: retrieve from difficult-to-copy keystore + } + + // store in cache (if room) + for (int i = 0; i < n; i++) { + if (num_cache < MAX_TKS_ENTRIES) { + cache_ids[num_cache] = id; + cache_keys[num_cache] = keys[i]; + num_cache++; + } else { + // TODO: evict oldest cache entry + } + } + return n; +} diff --git a/src/helpers/TransportKeyStore.h b/src/helpers/TransportKeyStore.h new file mode 100644 index 00000000..f25ed53f --- /dev/null +++ b/src/helpers/TransportKeyStore.h @@ -0,0 +1,23 @@ +#pragma once + +#include // needed for PlatformIO +#include +#include + +struct TransportKey { + uint8_t key[16]; + + uint16_t calcTransportCode(const mesh::Packet* packet) const; +}; + +#define MAX_TKS_ENTRIES 16 + +class TransportKeyStore { + uint16_t cache_ids[MAX_TKS_ENTRIES]; + TransportKey cache_keys[MAX_TKS_ENTRIES]; + int num_cache; + +public: + TransportKeyStore() { num_cache = 0; } + int loadKeysFor(const char* name, uint16_t id, TransportKey keys[], int max_num); +}; From f797744f7c4148e1f77888dbb121451c6745f0c0 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 3 Nov 2025 18:14:44 +1100 Subject: [PATCH 090/115] * misc RegionMap and key store methods --- src/helpers/RegionMap.cpp | 67 ++++++++++++++++++++++++++++-- src/helpers/RegionMap.h | 8 +++- src/helpers/TransportKeyStore.cpp | 68 +++++++++++++++++++++++-------- src/helpers/TransportKeyStore.h | 9 +++- 4 files changed, 130 insertions(+), 22 deletions(-) diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 4c270ff8..db9ea2d5 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -1,4 +1,5 @@ #include "RegionMap.h" +#include #include void RegionMap::load(FILESYSTEM* _fs) { @@ -8,12 +9,36 @@ void RegionMap::save(FILESYSTEM* _fs) { // TODO } +RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id) { + auto region = findByName(name); + if (region) { + if (region->id == parent_id) return NULL; // ERROR: invalid parent! + + region->parent = parent_id; // re-parent / move this region in the hierarchy + } else { + if (num_regions >= MAX_REGION_ENTRIES) return NULL; // full! + + region = ®ions[num_regions++]; // alloc new RegionEntry + region->flags = 0; + region->id = next_id++; + StrHelper::strncpy(region->name, name, sizeof(region->name)); + region->parent = parent_id; + } + return region; +} + RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; - if (region->flags & mask) { // does region allow this? (per 'mask' param) + if ((region->flags & mask) == mask) { // does region allow this? (per 'mask' param) TransportKey keys[4]; - int num = _store->loadKeysFor(region->name, region->id, keys, 4); + int num; + if (region->name[0] == '#') { // auto hashtag region + _store->getAutoKeyFor(region->id, region->name, keys[0]); + num = 1; + } else { + num = _store->loadKeysFor(region->id, keys, 4); + } for (int j = 0; j < num; j++) { uint16_t code = keys[j].calcTransportCode(packet); if (packet->transport_codes[0] == code) { // a match!! @@ -25,7 +50,7 @@ RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { return NULL; // no matches } -const RegionEntry* RegionMap::findName(const char* name) const { +RegionEntry* RegionMap::findByName(const char* name) { for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; if (strcmp(name, region->name) == 0) return region; @@ -33,3 +58,39 @@ const RegionEntry* RegionMap::findName(const char* name) const { return NULL; // not found } +RegionEntry* RegionMap::findById(uint16_t id) { + if (id == 0) return &wildcard; // special root Region + + for (int i = 0; i < num_regions; i++) { + auto region = ®ions[i]; + if (region->id == id) return region; + } + return NULL; // not found +} + +bool RegionMap::removeRegion(const RegionEntry& region) { + if (region.id == 0) return false; // failed (cannot remove the wildcard Region) + + int i; // first check region has no child regions + for (i = 0; i < num_regions; i++) { + if (regions[i].parent == region.id) return false; // failed (must remove child Regions first) + } + + i = 0; + while (i < num_regions) { + if (region.id == regions[i].id) break; + i++; + } + if (i >= num_regions) return false; // failed (not found) + + num_regions--; // remove from regions array + while (i + 1 < num_regions) { + regions[i] = regions[i + 1]; + } + return true; // success +} + +bool RegionMap::clear() { + num_regions = 0; + return true; // success +} diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h index 4620a745..69c5220b 100644 --- a/src/helpers/RegionMap.h +++ b/src/helpers/RegionMap.h @@ -33,7 +33,11 @@ public: void load(FILESYSTEM* _fs); void save(FILESYSTEM* _fs); + RegionEntry* putRegion(const char* name, uint16_t parent_id); RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask); - const RegionEntry& getWildcard() const { return wildcard; } - const RegionEntry* findName(const char* name) const; + RegionEntry& getWildcard() { return wildcard; } + RegionEntry* findByName(const char* name); + RegionEntry* findById(uint16_t id); + bool removeRegion(const RegionEntry& region); + bool clear(); }; diff --git a/src/helpers/TransportKeyStore.cpp b/src/helpers/TransportKeyStore.cpp index 973ad703..4f689a12 100644 --- a/src/helpers/TransportKeyStore.cpp +++ b/src/helpers/TransportKeyStore.cpp @@ -12,7 +12,32 @@ uint16_t TransportKey::calcTransportCode(const mesh::Packet* packet) const { return code; } -int TransportKeyStore::loadKeysFor(const char* name, uint16_t id, TransportKey keys[], int max_num) { +void TransportKeyStore::putCache(uint16_t id, const TransportKey& key) { + if (num_cache < MAX_TKS_ENTRIES) { + cache_ids[num_cache] = id; + cache_keys[num_cache] = key; + num_cache++; + } else { + // TODO: evict oldest cache entry + } +} + +void TransportKeyStore::getAutoKeyFor(uint16_t id, const char* name, TransportKey& dest) { + for (int i = 0; i < num_cache; i++) { // first, check cache + if (cache_ids[i] == id) { // cache hit! + dest = cache_keys[i]; + return; + } + } + // calc key for publicly-known hashtag region name + SHA256 sha; + sha.update(name, strlen(name)); + sha.finalize(&dest.key, sizeof(dest.key)); + + putCache(id, dest); +} + +int TransportKeyStore::loadKeysFor(uint16_t id, TransportKey keys[], int max_num) { int n = 0; for (int i = 0; i < num_cache && n < max_num; i++) { // first, check cache if (cache_ids[i] == id) { @@ -21,24 +46,35 @@ int TransportKeyStore::loadKeysFor(const char* name, uint16_t id, TransportKey k } if (n > 0) return n; // cache hit! - if (*name == '#') { // is a publicly-known hashtag region - SHA256 sha; - sha.update(name, strlen(name)); - sha.finalize(&keys[0], sizeof(keys[0].key)); - n = 1; - } else { - // TODO: retrieve from difficult-to-copy keystore - } + // TODO: retrieve from difficult-to-copy keystore // store in cache (if room) for (int i = 0; i < n; i++) { - if (num_cache < MAX_TKS_ENTRIES) { - cache_ids[num_cache] = id; - cache_keys[num_cache] = keys[i]; - num_cache++; - } else { - // TODO: evict oldest cache entry - } + putCache(id, keys[i]); } return n; } + +bool TransportKeyStore::saveKeysFor(uint16_t id, const TransportKey keys[], int num) { + invalidateCache(); + + // TODO: update hardware keystore + + return false; // failed +} + +bool TransportKeyStore::removeKeys(uint16_t id) { + invalidateCache(); + + // TODO: remove from hardware keystore + + return false; // failed +} + +bool TransportKeyStore::clear() { + invalidateCache(); + + // TODO: clear hardware keystore + + return false; // failed +} diff --git a/src/helpers/TransportKeyStore.h b/src/helpers/TransportKeyStore.h index f25ed53f..fc9d2532 100644 --- a/src/helpers/TransportKeyStore.h +++ b/src/helpers/TransportKeyStore.h @@ -17,7 +17,14 @@ class TransportKeyStore { TransportKey cache_keys[MAX_TKS_ENTRIES]; int num_cache; + void putCache(uint16_t id, const TransportKey& key); + void invalidateCache() { num_cache = 0; } + public: TransportKeyStore() { num_cache = 0; } - int loadKeysFor(const char* name, uint16_t id, TransportKey keys[], int max_num); + void getAutoKeyFor(uint16_t id, const char* name, TransportKey& dest); + int loadKeysFor(uint16_t id, TransportKey keys[], int max_num); + bool saveKeysFor(uint16_t id, const TransportKey keys[], int num); + bool removeKeys(uint16_t id); + bool clear(); }; From ecd30f4d364d190beac467224063d56ed681c5f2 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 3 Nov 2025 22:53:14 +1100 Subject: [PATCH 091/115] * new CLI commands: region, region load, region save, region get, region allow --- examples/simple_repeater/MyMesh.cpp | 83 +++++++++++++++++- examples/simple_repeater/MyMesh.h | 4 +- examples/simple_repeater/main.cpp | 4 +- src/helpers/RegionMap.cpp | 128 ++++++++++++++++++++++++++-- src/helpers/RegionMap.h | 22 +++-- 5 files changed, 219 insertions(+), 22 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 21f5a76b..2cd81ef6 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -610,7 +610,7 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store) + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store) #if defined(WITH_RS232_BRIDGE) , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) #endif @@ -624,6 +624,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc dirty_contacts_expiry = 0; set_radio_at = revert_radio_at = 0; _logging = false; + region_load_active = false; #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); @@ -846,9 +847,46 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } +static bool is_name_char(char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= 'z') || c == '-' || c == '.' || c == '_' || c == '#'; +} + void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { - while (*command == ' ') - command++; // skip leading spaces + if (region_load_active) { + if (*command == 0) { // empty line, signal to terminate 'load' operation + region_map = temp_map; // copy over the temp instance as new current map + region_load_active = false; + + sprintf(reply, "OK - loaded %d regions", region_map.getCount()); + } else { + char *np = command; + while (*np == ' ') np++; // skip indent + int indent = np - command; + + char *ep = np; + while (is_name_char(*ep)) ep++; + if (*ep) { *ep++ = 0; } // set null terminator for end of name + + while (*ep && *ep != 'F') ep++; // look for (optional flags) + + if (indent > 0 && indent < 8) { + auto parent = load_stack[indent - 1]; + if (parent) { + auto old = region_map.findByName(np); + auto nw = temp_map.putRegion(np, parent->id, old ? old->id : 0); // carry-over the current ID (if name already exists) + if (nw) { + nw->flags = old ? old->flags : (*ep == 'F' ? REGION_ALLOW_FLOOD : 0); // carry-over flags from curr + + load_stack[indent] = nw; // keep pointers to parent regions, to resolve parent_id's + } + } + } + reply[0] = 0; + } + return; + } + + while (*command == ' ') command++; // skip leading spaces if (strlen(command) > 4 && command[2] == '|') { // optional prefix (for companion radio CLI) memcpy(reply, command, 3); // reflect the prefix back @@ -890,6 +928,45 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; + } else if (memcmp(command, "region", 6) == 0) { + reply[0] = 0; + + const char* parts[4]; + int n = mesh::Utils::parseTextParts(command, parts, 4, ' '); + if (n == 1 && sender_timestamp == 0) { + region_map.exportTo(Serial); + } else if (n >= 2 && strcmp(parts[1], "load") == 0) { + temp_map.resetFrom(region_map); // rebuild regions in a temp instance + memset(load_stack, 0, sizeof(load_stack)); + load_stack[0] = &temp_map.getWildcard(); + region_load_active = true; + } else if (n >= 2 && strcmp(parts[1], "save") == 0) { + bool success = region_map.save(_fs); + strcpy(reply, success ? "OK" : "Err - save failed"); + } else if (n >= 3 && strcmp(parts[1], "allow") == 0) { + auto region = n >= 4 ? region_map.findByNamePrefix(parts[3]) : ®ion_map.getWildcard(); + if (region) { + strcpy(reply, "OK"); + if (strcmp(parts[2], "F") == 0) { + region->flags = REGION_ALLOW_FLOOD; + } else if (strcmp(parts[2], "0") == 0) { + region->flags = 0; + } else { + sprintf(reply, "Err - invalid flag: %s", parts[2]); + } + } else { + strcpy(reply, "Err - unknown region"); + } + } else if (n >= 3 && strcmp(parts[1], "get") == 0) { + auto region = region_map.findByNamePrefix(parts[2]); + if (region) { + sprintf(reply, " %s %s", region->name, region->flags == REGION_ALLOW_FLOOD ? "F" : ""); + } else { + strcpy(reply, "Err - unknown region"); + } + } else { + strcpy(reply, "Err - ??"); + } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7e34ef5f..9ab93747 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -89,7 +89,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t reply_data[MAX_PACKET_PAYLOAD]; ClientACL acl; TransportKeyStore key_store; - RegionMap region_map; + RegionMap region_map, temp_map; + RegionEntry* load_stack[8]; + bool region_load_active; unsigned long dirty_contacts_expiry; #if MAX_NEIGHBOURS NeighbourInfo neighbours[MAX_NEIGHBOURS]; diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 5843df74..7387e77e 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -91,14 +91,16 @@ void loop() { if (c != '\n') { command[len++] = c; command[len] = 0; + Serial.print(c); } - Serial.print(c); + if (c == '\r') break; } if (len == sizeof(command)-1) { // command buffer full command[sizeof(command)-1] = '\r'; } if (len > 0 && command[len - 1] == '\r') { // received complete line + Serial.print('\n'); command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index db9ea2d5..7b6456b4 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -2,25 +2,106 @@ #include #include -void RegionMap::load(FILESYSTEM* _fs) { - // TODO -} -void RegionMap::save(FILESYSTEM* _fs) { - // TODO +RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) { + next_id = 1; num_regions = 0; + wildcard.id = wildcard.parent = 0; + wildcard.flags = REGION_ALLOW_FLOOD; // default behaviour, allow flood + strcpy(wildcard.name, "(*)"); } -RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id) { +static File openWrite(FILESYSTEM* _fs, const char* filename) { + #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(filename); + return _fs->open(filename, FILE_O_WRITE); + #elif defined(RP2040_PLATFORM) + return _fs->open(filename, "w"); + #else + return _fs->open(filename, "w", true); + #endif +} + +bool RegionMap::load(FILESYSTEM* _fs) { + if (_fs->exists("/regions2")) { + #if defined(RP2040_PLATFORM) + File file = _fs->open("/regions2", "r"); + #else + File file = _fs->open("/regions2"); + #endif + + if (file) { + uint8_t pad[128]; + + num_regions = 0; next_id = 1; + + bool success = file.read(pad, 7) == 7; // reserved header + success = success && file.read((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); + success = success && file.read((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); + + if (success) { + while (num_regions < MAX_REGION_ENTRIES) { + auto r = ®ions[num_regions]; + + success = file.read((uint8_t *) &r->id, sizeof(r->id)) == sizeof(r->id); + success = success && file.read((uint8_t *) &r->parent, sizeof(r->parent)) == sizeof(r->parent); + success = success && file.read((uint8_t *) r->name, sizeof(r->name)) == sizeof(r->name); + success = success && file.read((uint8_t *) &r->flags, sizeof(r->flags)) == sizeof(r->flags); + success = success && file.read(pad, sizeof(pad)) == sizeof(pad); + + if (!success) break; // EOF + + if (r->id >= next_id) { // make sure next_id is valid + next_id = r->id + 1; + } + num_regions++; + } + } + file.close(); + return true; + } + } + return false; // failed +} + +bool RegionMap::save(FILESYSTEM* _fs) { + File file = openWrite(_fs, "/regions2"); + if (file) { + uint8_t pad[128]; + memset(pad, 0, sizeof(pad)); + + bool success = file.write(pad, 7) == 7; // reserved header + success = success && file.write((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); + success = success && file.write((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); + + if (success) { + for (int i = 0; i < num_regions; i++) { + auto r = ®ions[i]; + + success = file.write((uint8_t *) &r->id, sizeof(r->id)) == sizeof(r->id); + success = success && file.write((uint8_t *) &r->parent, sizeof(r->parent)) == sizeof(r->parent); + success = success && file.write((uint8_t *) r->name, sizeof(r->name)) == sizeof(r->name); + success = success && file.write((uint8_t *) &r->flags, sizeof(r->flags)) == sizeof(r->flags); + success = success && file.write(pad, sizeof(pad)) == sizeof(pad); + if (!success) break; // write failed + } + } + file.close(); + return true; + } + return false; // failed +} + +RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id, uint16_t id) { auto region = findByName(name); if (region) { if (region->id == parent_id) return NULL; // ERROR: invalid parent! region->parent = parent_id; // re-parent / move this region in the hierarchy } else { - if (num_regions >= MAX_REGION_ENTRIES) return NULL; // full! + if (id == 0 && num_regions >= MAX_REGION_ENTRIES) return NULL; // full! region = ®ions[num_regions++]; // alloc new RegionEntry region->flags = 0; - region->id = next_id++; + region->id = id == 0 ? next_id++ : id; StrHelper::strncpy(region->name, name, sizeof(region->name)); region->parent = parent_id; } @@ -58,6 +139,14 @@ RegionEntry* RegionMap::findByName(const char* name) { return NULL; // not found } +RegionEntry* RegionMap::findByNamePrefix(const char* prefix) { + for (int i = 0; i < num_regions; i++) { + auto region = ®ions[i]; + if (memcmp(prefix, region->name, strlen(prefix)) == 0) return region; + } + return NULL; // not found +} + RegionEntry* RegionMap::findById(uint16_t id) { if (id == 0) return &wildcard; // special root Region @@ -94,3 +183,26 @@ bool RegionMap::clear() { num_regions = 0; return true; // success } + +void RegionMap::printChildRegions(int indent, const RegionEntry* parent, Stream& out) const { + for (int i = 0; i < indent; i++) { + out.print(' '); + } + + if (parent->flags & REGION_ALLOW_FLOOD) { + out.printf("%s F\n", parent->name); + } else { + out.printf("%s\n", parent->name); + } + + for (int i = 0; i < num_regions; i++) { + auto r = ®ions[i]; + if (r->parent == parent->id) { + printChildRegions(indent + 1, r, out); + } + } +} + +void RegionMap::exportTo(Stream& out) const { + printChildRegions(0, &wildcard, out); // recursive +} diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h index 69c5220b..3858bfbd 100644 --- a/src/helpers/RegionMap.h +++ b/src/helpers/RegionMap.h @@ -24,20 +24,24 @@ class RegionMap { RegionEntry regions[MAX_REGION_ENTRIES]; RegionEntry wildcard; -public: - RegionMap(TransportKeyStore& store) : _store(&store) { - next_id = 1; num_regions = 0; - wildcard.id = wildcard.parent = 0; - wildcard.flags = REGION_ALLOW_FLOOD; // default behaviour, allow flood - } - void load(FILESYSTEM* _fs); - void save(FILESYSTEM* _fs); + void printChildRegions(int indent, const RegionEntry* parent, Stream& out) const; - RegionEntry* putRegion(const char* name, uint16_t parent_id); +public: + RegionMap(TransportKeyStore& store); + + bool load(FILESYSTEM* _fs); + bool save(FILESYSTEM* _fs); + + RegionEntry* putRegion(const char* name, uint16_t parent_id, uint16_t id = 0); RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask); RegionEntry& getWildcard() { return wildcard; } RegionEntry* findByName(const char* name); + RegionEntry* findByNamePrefix(const char* prefix); RegionEntry* findById(uint16_t id); bool removeRegion(const RegionEntry& region); bool clear(); + void resetFrom(const RegionMap& src) { num_regions = 0; next_id = src.next_id; } + int getCount() const { return num_regions; } + + void exportTo(Stream& out) const; }; From d9ff3a4d02d25005eac27c40bd58b6c43f29c9ed Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 4 Nov 2025 01:21:56 +1100 Subject: [PATCH 092/115] * Mesh: new sendFlood() overload with transport codes. * BaseChatMesh: sendFloodScoped(), for overriding with some outbound 'scope' / TransportKey * companion: new 'send_scope' variable. --- examples/companion_radio/MyMesh.cpp | 24 ++++++++++++++++++++++ examples/companion_radio/MyMesh.h | 6 ++++++ src/Mesh.cpp | 25 +++++++++++++++++++++++ src/Mesh.h | 6 ++++++ src/helpers/BaseChatMesh.cpp | 31 ++++++++++++++++++----------- src/helpers/BaseChatMesh.h | 3 +++ src/helpers/TransportKeyStore.cpp | 7 +++++++ src/helpers/TransportKeyStore.h | 1 + 8 files changed, 91 insertions(+), 12 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 02f1a21d..b8ddbfa7 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -378,6 +378,29 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #endif } +void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) { + // TODO: dynamic send_scope, depending on recipient and current 'home' Region + if (send_scope.isNull()) { + sendFlood(pkt, delay_millis); + } else { + uint16_t codes[2]; + codes[0] = send_scope.calcTransportCode(pkt); + codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region? + sendFlood(pkt, codes, delay_millis); + } +} +void MyMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis) { + // TODO: have per-channel send_scope + if (send_scope.isNull()) { + sendFlood(pkt, delay_millis); + } else { + uint16_t codes[2]; + codes[0] = send_scope.calcTransportCode(pkt); + codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region? + sendFlood(pkt, codes, delay_millis); + } +} + void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const char *text) { markConnectionActive(from); // in case this is from a server, and we have a connection @@ -663,6 +686,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe sign_data = NULL; dirty_contacts_expiry = 0; memset(advert_paths, 0, sizeof(advert_paths)); + memset(send_scope.key, 0, sizeof(send_scope.key)); // defaults memset(&_prefs, 0, sizeof(_prefs)); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index e6400871..bfb8bb18 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -68,6 +68,7 @@ #endif #include +#include /* -------------------------------------------------------------------------------------- */ @@ -107,6 +108,9 @@ protected: int calcRxDelay(float score, uint32_t air_time) const override; uint8_t getExtraAckTransmitCount() const override; + void sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis=0) override; + void sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis=0) override; + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; bool isAutoAddEnabled() const override; bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; @@ -191,6 +195,8 @@ private: uint32_t sign_data_len; unsigned long dirty_contacts_expiry; + TransportKey send_scope; + uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; uint8_t out_frame[MAX_FRAME_SIZE + 1]; CayenneLPP telemetry; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 53dc74f5..1ee6ce5c 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -610,6 +610,31 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis) { sendPacket(packet, pri, delay_millis); } +void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis) { + if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { + MESH_DEBUG_PRINTLN("%s Mesh::sendFlood(): TRACE type not suspported", getLogDateTime()); + return; + } + + packet->header &= ~PH_ROUTE_MASK; + packet->header |= ROUTE_TYPE_TRANSPORT_FLOOD; + packet->transport_codes[0] = transport_codes[0]; + packet->transport_codes[1] = transport_codes[1]; + packet->path_len = 0; + + _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + + uint8_t pri; + if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { + pri = 2; + } else if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT) { + pri = 3; // de-prioritie these + } else { + pri = 1; + } + sendPacket(packet, pri, delay_millis); +} + void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uint32_t delay_millis) { packet->header &= ~PH_ROUTE_MASK; packet->header |= ROUTE_TYPE_DIRECT; diff --git a/src/Mesh.h b/src/Mesh.h index cbf1c9cf..e96043e8 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -186,6 +186,12 @@ public: */ void sendFlood(Packet* packet, uint32_t delay_millis=0); + /** + * \brief send a locally-generated Packet with flood routing + * \param transport_codes array of 2 codes to attach to packet + */ + void sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); + /** * \brief send a locally-generated Packet with Direct routing */ diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 9b1eb1ce..b4072657 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -9,6 +9,13 @@ #define TXT_ACK_DELAY 200 #endif +void BaseChatMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) { + sendFlood(pkt, delay_millis); +} +void BaseChatMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis) { + sendFlood(pkt, delay_millis); +} + mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name) { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; @@ -34,7 +41,7 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { if (dest.out_path_len < 0) { mesh::Packet* ack = createAck(ack_hash); - if (ack) sendFlood(ack, TXT_ACK_DELAY); + if (ack) sendFloodScoped(dest, ack, TXT_ACK_DELAY); } else { uint32_t d = TXT_ACK_DELAY; if (getExtraAckTransmitCount() > 0) { @@ -175,7 +182,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); - if (path) sendFlood(path, TXT_ACK_DELAY); + if (path) sendFloodScoped(from, path, TXT_ACK_DELAY); } else { sendAckTo(from, ack_hash); } @@ -186,7 +193,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender if (packet->isRouteFlood()) { // let this sender know path TO here, so they can use sendDirect() (NOTE: no ACK as extra) mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, 0, NULL, 0); - if (path) sendFlood(path); + if (path) sendFloodScoped(from, path); } } else if (flags == TXT_TYPE_SIGNED_PLAIN) { if (timestamp > from.sync_since) { // make sure 'sync_since' is up-to-date @@ -202,7 +209,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); - if (path) sendFlood(path, TXT_ACK_DELAY); + if (path) sendFloodScoped(from, path, TXT_ACK_DELAY); } else { sendAckTo(from, ack_hash); } @@ -218,14 +225,14 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, temp_buf, reply_len); - if (path) sendFlood(path, SERVER_RESPONSE_DELAY); + if (path) sendFloodScoped(from, path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from.id, secret, temp_buf, reply_len); if (reply) { if (from.out_path_len >= 0) { // we have an out_path, so send DIRECT sendDirect(reply, from.out_path, from.out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply, SERVER_RESPONSE_DELAY); + sendFloodScoped(from, reply, SERVER_RESPONSE_DELAY); } } } @@ -346,7 +353,7 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp, int rc; if (recipient.out_path_len < 0) { - sendFlood(pkt); + sendFloodScoped(recipient, pkt); txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t)); rc = MSG_SEND_SENT_FLOOD; } else { @@ -372,7 +379,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); int rc; if (recipient.out_path_len < 0) { - sendFlood(pkt); + sendFloodScoped(recipient, pkt); txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t)); rc = MSG_SEND_SENT_FLOOD; } else { @@ -398,7 +405,7 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); if (pkt) { - sendFlood(pkt); + sendFloodScoped(channel, pkt); return true; } return false; @@ -460,7 +467,7 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); if (recipient.out_path_len < 0) { - sendFlood(pkt); + sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; } else { @@ -487,7 +494,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_ if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); if (recipient.out_path_len < 0) { - sendFlood(pkt); + sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; } else { @@ -514,7 +521,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t req_type, u if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); if (recipient.out_path_len < 0) { - sendFlood(pkt); + sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; } else { diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 9392001e..76b0dd1c 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -107,6 +107,9 @@ protected: virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0; virtual void handleReturnPathRetry(const ContactInfo& contact, const uint8_t* path, uint8_t path_len); + virtual void sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis=0); + virtual void sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis=0); + // storage concepts, for sub-classes to override/implement virtual int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { return 0; } // not implemented virtual bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { return false; } diff --git a/src/helpers/TransportKeyStore.cpp b/src/helpers/TransportKeyStore.cpp index 4f689a12..8b7c891b 100644 --- a/src/helpers/TransportKeyStore.cpp +++ b/src/helpers/TransportKeyStore.cpp @@ -12,6 +12,13 @@ uint16_t TransportKey::calcTransportCode(const mesh::Packet* packet) const { return code; } +bool TransportKey::isNull() const { + for (int i = 0; i < sizeof(key); i++) { + if (key[i]) return false; + } + return true; // key is all zeroes +} + void TransportKeyStore::putCache(uint16_t id, const TransportKey& key) { if (num_cache < MAX_TKS_ENTRIES) { cache_ids[num_cache] = id; diff --git a/src/helpers/TransportKeyStore.h b/src/helpers/TransportKeyStore.h index fc9d2532..e3ba1524 100644 --- a/src/helpers/TransportKeyStore.h +++ b/src/helpers/TransportKeyStore.h @@ -8,6 +8,7 @@ struct TransportKey { uint8_t key[16]; uint16_t calcTransportCode(const mesh::Packet* packet) const; + bool isNull() const; }; #define MAX_TKS_ENTRIES 16 From 5c80334dbda5a09fd44104a70168fe79a1bf0fea Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 3 Nov 2025 21:00:43 +0000 Subject: [PATCH 093/115] Fix manufacturer name on Seeed Xiao S3 WIO --- variants/xiao_s3_wio/XiaoS3WIOBoard.h | 13 +++++++++++++ variants/xiao_s3_wio/target.cpp | 3 ++- variants/xiao_s3_wio/target.h | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 variants/xiao_s3_wio/XiaoS3WIOBoard.h diff --git a/variants/xiao_s3_wio/XiaoS3WIOBoard.h b/variants/xiao_s3_wio/XiaoS3WIOBoard.h new file mode 100644 index 00000000..7ae06a35 --- /dev/null +++ b/variants/xiao_s3_wio/XiaoS3WIOBoard.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class XiaoS3WIOBoard : public ESP32Board { +public: + XiaoS3WIOBoard() { } + + const char* getManufacturerName() const override { + return "Xiao S3 WIO"; + } +}; diff --git a/variants/xiao_s3_wio/target.cpp b/variants/xiao_s3_wio/target.cpp index ed8584ff..f7de9244 100644 --- a/variants/xiao_s3_wio/target.cpp +++ b/variants/xiao_s3_wio/target.cpp @@ -1,7 +1,8 @@ #include #include "target.h" +#include "XiaoS3WIOBoard.h" -ESP32Board board; +XiaoS3WIOBoard board; #if defined(P_LORA_SCLK) static SPIClass spi; diff --git a/variants/xiao_s3_wio/target.h b/variants/xiao_s3_wio/target.h index f184c757..b84ab42b 100644 --- a/variants/xiao_s3_wio/target.h +++ b/variants/xiao_s3_wio/target.h @@ -11,8 +11,9 @@ #include #include #endif +#include "XiaoS3WIOBoard.h" -extern ESP32Board board; +extern XiaoS3WIOBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; From c3dbec41bac0730b778efd8202cd2d29466c0b9b Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 3 Nov 2025 21:02:08 +0000 Subject: [PATCH 094/115] Fix manufacturer name on Seeed Xiao S3 WIO --- variants/xiao_s3_wio/target.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/variants/xiao_s3_wio/target.cpp b/variants/xiao_s3_wio/target.cpp index f7de9244..41f25da6 100644 --- a/variants/xiao_s3_wio/target.cpp +++ b/variants/xiao_s3_wio/target.cpp @@ -1,6 +1,5 @@ #include #include "target.h" -#include "XiaoS3WIOBoard.h" XiaoS3WIOBoard board; From 04c0c40b391e5e9d78ce83dba5e539b30e4c1443 Mon Sep 17 00:00:00 2001 From: recrof Date: Tue, 4 Nov 2025 23:58:32 +0100 Subject: [PATCH 095/115] set max contacts to 350 and channels to 40 for esp32c3, s3 and c6 --- variants/ebyte_eora_s3/platformio.ini | 10 ++++---- variants/generic_espnow/platformio.ini | 4 ++-- variants/heltec_ct62/platformio.ini | 8 +++---- variants/heltec_e213/platformio.ini | 8 +++---- variants/heltec_e290/platformio.ini | 8 +++---- variants/heltec_t190/platformio.ini | 8 +++---- variants/heltec_tracker/platformio.ini | 4 ++-- variants/heltec_tracker_v2/platformio.ini | 14 +++++------ variants/heltec_v3/platformio.ini | 24 +++++++++---------- variants/heltec_v4/platformio.ini | 14 +++++------ variants/heltec_wireless_paper/platformio.ini | 4 ++-- variants/lilygo_t3s3/platformio.ini | 10 ++++---- variants/lilygo_t3s3_sx1276/platformio.ini | 10 ++++---- .../platformio.ini | 4 ++-- variants/lilygo_tlora_c6/platformio.ini | 4 ++-- variants/meshadventurer/platformio.ini | 8 +++---- .../sensecap_indicator-espnow/platformio.ini | 4 ++-- variants/station_g2/platformio.ini | 12 +++++----- variants/thinknode_m2/platformio.ini | 12 +++++----- variants/xiao_c3/platformio.ini | 12 +++++----- variants/xiao_c6/platformio.ini | 12 +++++----- variants/xiao_s3_wio/platformio.ini | 16 ++++++------- 22 files changed, 105 insertions(+), 105 deletions(-) diff --git a/variants/ebyte_eora_s3/platformio.ini b/variants/ebyte_eora_s3/platformio.ini index df622a34..bdf6bba3 100644 --- a/variants/ebyte_eora_s3/platformio.ini +++ b/variants/ebyte_eora_s3/platformio.ini @@ -64,7 +64,7 @@ lib_deps = extends = Ebyte_EoRa-S3 build_flags = ${Ebyte_EoRa-S3.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -99,8 +99,8 @@ build_flags = ${Ebyte_EoRa-S3.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Ebyte_EoRa-S3.build_src_filter} @@ -118,8 +118,8 @@ build_flags = ${Ebyte_EoRa-S3.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/generic_espnow/platformio.ini b/variants/generic_espnow/platformio.ini index c40833dc..cdeed076 100644 --- a/variants/generic_espnow/platformio.ini +++ b/variants/generic_espnow/platformio.ini @@ -26,7 +26,7 @@ build_src_filter = ${esp32_base.build_src_filter} extends = Generic_ESPNOW build_flags = ${Generic_ESPNOW.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 build_src_filter = ${Generic_ESPNOW.build_src_filter} +<../examples/simple_secure_chat/main.cpp> @@ -54,7 +54,7 @@ lib_deps = extends = Generic_ESPNOW build_flags = ${Generic_ESPNOW.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=8 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 1b6121e4..1f2e330a 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -97,8 +97,8 @@ build_flags = ${Heltec_ct62.build_flags} ; -D ARDUINO_USB_MODE=1 ; -D ARDUINO_USB_CDC_ON_BOOT=1 - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -115,8 +115,8 @@ build_flags = ${Heltec_ct62.build_flags} ; -D ARDUINO_USB_MODE=1 ; -D ARDUINO_USB_CDC_ON_BOOT=1 - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 -D BLE_PIN_CODE=123456 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index 93bdc000..caba3a30 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -45,8 +45,8 @@ extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E213Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 @@ -65,8 +65,8 @@ extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E213Display -D OFFLINE_QUEUE_SIZE=256 build_src_filter = ${Heltec_E213_base.build_src_filter} diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 201b3631..0c07c592 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -39,8 +39,8 @@ extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E290Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 @@ -59,8 +59,8 @@ extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E290Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index 01e9dfc9..8d21c523 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -52,8 +52,8 @@ extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -70,8 +70,8 @@ extends = Heltec_T190_base build_flags = ${Heltec_T190_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 build_src_filter = ${Heltec_T190_base.build_src_filter} + diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 19ab49a8..4f48ac21 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -43,8 +43,8 @@ build_flags = -I examples/companion_radio/ui-new -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; HWT will use display for pin -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index c4a79d9e..61ccd4f4 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -120,7 +120,7 @@ lib_deps = extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -135,8 +135,8 @@ extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=ST7735Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 @@ -154,8 +154,8 @@ extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=ST7735Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D AUTO_SHUTDOWN_MILLIVOLTS=3400 @@ -179,8 +179,8 @@ extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=ST7735Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index dcf566b3..36c6386f 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -125,7 +125,7 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -140,8 +140,8 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 @@ -159,8 +159,8 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D AUTO_SHUTDOWN_MILLIVOLTS=3400 @@ -183,8 +183,8 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' @@ -304,8 +304,8 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -323,7 +323,7 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D MAX_CONTACTS=140 - -D MAX_GROUP_CHANNELS=8 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -336,8 +336,8 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' -D WIFI_PWD='"mypwd"' diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 2c06b978..c26a5bc6 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -112,7 +112,7 @@ lib_deps = extends = Heltec_lora32_v4 build_flags = ${Heltec_lora32_v4.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -127,8 +127,8 @@ extends = Heltec_lora32_v4 build_flags = ${Heltec_lora32_v4.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 @@ -146,8 +146,8 @@ extends = Heltec_lora32_v4 build_flags = ${Heltec_lora32_v4.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D AUTO_SHUTDOWN_MILLIVOLTS=3400 @@ -170,8 +170,8 @@ extends = Heltec_lora32_v4 build_flags = ${Heltec_lora32_v4.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index 20bf4c64..c9ad758b 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -38,8 +38,8 @@ extends = Heltec_Wireless_Paper_base build_flags = ${Heltec_Wireless_Paper_base.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E213Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index a97a692a..0f01c9b7 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -102,7 +102,7 @@ lib_deps = extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -137,8 +137,8 @@ build_flags = ${LilyGo_T3S3_sx1262.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} @@ -156,8 +156,8 @@ build_flags = ${LilyGo_T3S3_sx1262.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index eee8bdbd..f544be11 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -100,7 +100,7 @@ lib_deps = extends = LilyGo_T3S3_sx1276 build_flags = ${LilyGo_T3S3_sx1276.build_flags} - -D MAX_CONTACTS=300 + -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -136,8 +136,8 @@ build_flags = ${LilyGo_T3S3_sx1276.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} @@ -155,8 +155,8 @@ build_flags = ${LilyGo_T3S3_sx1276.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index ef31e6e1..2d2a095a 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -116,8 +116,8 @@ extends = T_Beam_S3_Supreme_SX1262 build_flags = ${T_Beam_S3_Supreme_SX1262.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini index 88a811b5..b29cd036 100644 --- a/variants/lilygo_tlora_c6/platformio.ini +++ b/variants/lilygo_tlora_c6/platformio.ini @@ -67,8 +67,8 @@ lib_deps = [env:LilyGo_Tlora_C6_companion_radio_ble_] extends = tlora_c6 build_flags = ${tlora_c6.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini index c52f4b7b..18b64ac3 100644 --- a/variants/meshadventurer/platformio.ini +++ b/variants/meshadventurer/platformio.ini @@ -190,7 +190,7 @@ build_flags = -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -212,7 +212,7 @@ build_flags = -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -275,7 +275,7 @@ build_flags = -D WRAPPER_CLASS=CustomSX1268Wrapper -D LORA_TX_POWER=22 -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = @@ -297,7 +297,7 @@ build_flags = -D WRAPPER_CLASS=CustomSX1268Wrapper -D LORA_TX_POWER=22 -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/sensecap_indicator-espnow/platformio.ini b/variants/sensecap_indicator-espnow/platformio.ini index 064f3ae8..e643d033 100644 --- a/variants/sensecap_indicator-espnow/platformio.ini +++ b/variants/sensecap_indicator-espnow/platformio.ini @@ -37,8 +37,8 @@ extends =SenseCapIndicator-ESPNow build_flags = ${SenseCapIndicator-ESPNow.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 ; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1 diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index bda8b7ae..9ee8f829 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -184,8 +184,8 @@ extends = Station_G2 build_flags = ${Station_G2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} @@ -201,8 +201,8 @@ extends = Station_G2 build_flags = ${Station_G2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -221,8 +221,8 @@ extends = Station_G2 build_flags = ${Station_G2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' -D WIFI_PWD='"mypwd"' diff --git a/variants/thinknode_m2/platformio.ini b/variants/thinknode_m2/platformio.ini index 0238947f..fb691d92 100644 --- a/variants/thinknode_m2/platformio.ini +++ b/variants/thinknode_m2/platformio.ini @@ -118,8 +118,8 @@ lib_deps = extends = ThinkNode_M2 build_flags = ${ThinkNode_M2.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ThinkNode_M2.build_src_filter} @@ -133,8 +133,8 @@ extends = ThinkNode_M2 build_flags = ${ThinkNode_M2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 @@ -155,8 +155,8 @@ extends = ThinkNode_M2 build_flags = ${ThinkNode_M2.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D SERIAL_TX=D6 -D SERIAL_RX=D7 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 683199d9..76b72174 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -73,8 +73,8 @@ build_src_filter = ${Xiao_esp32_C3.build_src_filter} + build_flags = ${Xiao_esp32_C3.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 @@ -92,8 +92,8 @@ build_src_filter = ${Xiao_esp32_C3.build_src_filter} + build_flags = ${Xiao_esp32_C3.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 @@ -110,8 +110,8 @@ build_src_filter = ${Xiao_esp32_C3.build_src_filter} + build_flags = ${Xiao_esp32_C3.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 9c971083..8f02dc87 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -50,8 +50,8 @@ lib_deps = [env:Xiao_C6_companion_radio_ble_] extends = Xiao_C6 build_flags = ${Xiao_C6.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -107,8 +107,8 @@ lib_deps = [env:Meshimi_companion_radio_ble_] extends = Meshimi build_flags = ${Meshimi.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -167,8 +167,8 @@ lib_deps = [env:WHY2025_badge_companion_radio_ble_] extends = WHY2025_badge build_flags = ${WHY2025_badge.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index e5cc4c8c..95a54012 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -107,8 +107,8 @@ lib_deps = extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_S3_WIO.build_src_filter} @@ -122,8 +122,8 @@ extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 @@ -144,8 +144,8 @@ extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D DISPLAY_CLASS=SSD1306Display -D OFFLINE_QUEUE_SIZE=256 @@ -168,8 +168,8 @@ extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=300 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display -D SERIAL_TX=D6 -D SERIAL_RX=D7 From 9ebeb477aa3313123323a967c8981a6ab96ea82f Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 5 Nov 2025 14:34:44 +1100 Subject: [PATCH 096/115] * RegionMap: inverted 'flags' to _deny_ bits * Mesh: new filterRecvFloodPacket() for overriding * repeater CLI: 'allow' -> 'allowf' or 'denyf' --- examples/companion_radio/MyMesh.cpp | 6 +++++ examples/companion_radio/MyMesh.h | 1 + examples/simple_repeater/MyMesh.cpp | 38 +++++++++++++++-------------- examples/simple_repeater/MyMesh.h | 2 +- src/Mesh.cpp | 2 ++ src/Mesh.h | 5 ++++ src/helpers/RegionMap.cpp | 12 ++++----- src/helpers/RegionMap.h | 3 ++- 8 files changed, 43 insertions(+), 26 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index b8ddbfa7..3c882d18 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -378,6 +378,12 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #endif } +bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) { + // REVISIT: try to determine which Region (from transport_codes[1]) that Sender is indicating for replies/responses + // if unknown, fallback to finding Region from transport_codes[0], the 'scope' used by Sender + return false; +} + void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) { // TODO: dynamic send_scope, depending on recipient and current 'home' Region if (send_scope.isNull()) { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index bfb8bb18..fb0fb479 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -107,6 +107,7 @@ protected: int getInterferenceThreshold() const override; int calcRxDelay(float score, uint32_t air_time) const override; uint8_t getExtraAckTransmitCount() const override; + bool filterRecvFloodPacket(mesh::Packet* packet) override; void sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis=0) override; void sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis=0) override; diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 2cd81ef6..dce0f18b 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -404,21 +404,21 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { return getRNG()->nextInt(0, 5*t + 1); } -mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) { +bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { - auto region = region_map.findMatch(pkt, REGION_ALLOW_FLOOD); + auto region = region_map.findMatch(pkt, REGION_DENY_FLOOD); if (region == NULL) { MESH_DEBUG_PRINTLN("onRecvPacket: unknown transport code for FLOOD packet"); - return ACTION_RELEASE; + return true; } } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { - if ((region_map.getWildcard().flags & REGION_ALLOW_FLOOD) == 0) { + if (region_map.getWildcard().flags & REGION_DENY_FLOOD) { MESH_DEBUG_PRINTLN("onRecvPacket: wildcard FLOOD packet not allowed"); - return ACTION_RELEASE; + return true; } } // otherwise do normal processing - return mesh::Mesh::onRecvPacket(pkt); + return false; } void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, @@ -867,7 +867,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply while (is_name_char(*ep)) ep++; if (*ep) { *ep++ = 0; } // set null terminator for end of name - while (*ep && *ep != 'F') ep++; // look for (optional flags) + while (*ep && *ep != 'F') ep++; // look for (optional) flags if (indent > 0 && indent < 8) { auto parent = load_stack[indent - 1]; @@ -875,7 +875,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply auto old = region_map.findByName(np); auto nw = temp_map.putRegion(np, parent->id, old ? old->id : 0); // carry-over the current ID (if name already exists) if (nw) { - nw->flags = old ? old->flags : (*ep == 'F' ? REGION_ALLOW_FLOOD : 0); // carry-over flags from curr + nw->flags = old ? old->flags : (*ep == 'F' ? 0 : REGION_DENY_FLOOD); // carry-over flags from curr load_stack[indent] = nw; // keep pointers to parent regions, to resolve parent_id's } @@ -943,24 +943,26 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else if (n >= 2 && strcmp(parts[1], "save") == 0) { bool success = region_map.save(_fs); strcpy(reply, success ? "OK" : "Err - save failed"); - } else if (n >= 3 && strcmp(parts[1], "allow") == 0) { - auto region = n >= 4 ? region_map.findByNamePrefix(parts[3]) : ®ion_map.getWildcard(); + } else if (n >= 2 && strcmp(parts[1], "allowf") == 0) { + auto region = n >= 3 ? region_map.findByNamePrefix(parts[2]) : ®ion_map.getWildcard(); if (region) { + region->flags &= ~REGION_DENY_FLOOD; + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - unknown region"); + } + } else if (n >= 2 && strcmp(parts[1], "denyf") == 0) { + auto region = n >= 3 ? region_map.findByNamePrefix(parts[2]) : ®ion_map.getWildcard(); + if (region) { + region->flags |= REGION_DENY_FLOOD; strcpy(reply, "OK"); - if (strcmp(parts[2], "F") == 0) { - region->flags = REGION_ALLOW_FLOOD; - } else if (strcmp(parts[2], "0") == 0) { - region->flags = 0; - } else { - sprintf(reply, "Err - invalid flag: %s", parts[2]); - } } else { strcpy(reply, "Err - unknown region"); } } else if (n >= 3 && strcmp(parts[1], "get") == 0) { auto region = region_map.findByNamePrefix(parts[2]); if (region) { - sprintf(reply, " %s %s", region->name, region->flags == REGION_ALLOW_FLOOD ? "F" : ""); + sprintf(reply, " %s %s", region->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); } else { strcpy(reply, "Err - unknown region"); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 9ab93747..83331541 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -149,7 +149,7 @@ protected: } #endif - mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override; + bool filterRecvFloodPacket(mesh::Packet* pkt) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 1ee6ce5c..71b8eaee 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -90,6 +90,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard. } + if (pkt->isRouteFlood() && filterRecvFloodPacket(pkt)) return ACTION_RELEASE; + DispatcherAction action = ACTION_RELEASE; switch (pkt->getPayloadType()) { diff --git a/src/Mesh.h b/src/Mesh.h index e96043e8..70fa80f3 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -43,6 +43,11 @@ protected: */ DispatcherAction routeRecvPacket(Packet* packet); + /** + * \returns true, if given packet should be NOT be processed. + */ + virtual bool filterRecvFloodPacket(Packet* packet) { return false; } + /** * \brief Check whether this packet should be forwarded (re-transmitted) or not. * Is sub-classes responsibility to make sure given packet is only transmitted ONCE (by this node) diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 7b6456b4..074084ec 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -5,7 +5,7 @@ RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) { next_id = 1; num_regions = 0; wildcard.id = wildcard.parent = 0; - wildcard.flags = REGION_ALLOW_FLOOD; // default behaviour, allow flood + wildcard.flags = 0; // default behaviour, allow flood and direct strcpy(wildcard.name, "(*)"); } @@ -100,7 +100,7 @@ RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id, uint16_t if (id == 0 && num_regions >= MAX_REGION_ENTRIES) return NULL; // full! region = ®ions[num_regions++]; // alloc new RegionEntry - region->flags = 0; + region->flags = REGION_DENY_FLOOD; // DENY by default region->id = id == 0 ? next_id++ : id; StrHelper::strncpy(region->name, name, sizeof(region->name)); region->parent = parent_id; @@ -111,7 +111,7 @@ RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id, uint16_t RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; - if ((region->flags & mask) == mask) { // does region allow this? (per 'mask' param) + if ((region->flags & mask) == 0) { // does region allow this? (per 'mask' param) TransportKey keys[4]; int num; if (region->name[0] == '#') { // auto hashtag region @@ -189,10 +189,10 @@ void RegionMap::printChildRegions(int indent, const RegionEntry* parent, Stream& out.print(' '); } - if (parent->flags & REGION_ALLOW_FLOOD) { - out.printf("%s F\n", parent->name); - } else { + if (parent->flags & REGION_DENY_FLOOD) { out.printf("%s\n", parent->name); + } else { + out.printf("%s F\n", parent->name); } for (int i = 0; i < num_regions; i++) { diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h index 3858bfbd..5ad9df29 100644 --- a/src/helpers/RegionMap.h +++ b/src/helpers/RegionMap.h @@ -8,7 +8,8 @@ #define MAX_REGION_ENTRIES 32 #endif -#define REGION_ALLOW_FLOOD 0x01 +#define REGION_DENY_FLOOD 0x01 +#define REGION_DENY_DIRECT 0x02 // reserved for future struct RegionEntry { uint16_t id; From 937865c8fd8d3e82cf5078bc77226d725c556344 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 5 Nov 2025 14:56:18 +1100 Subject: [PATCH 097/115] * companion: new CMD_SET_FLOOD_SCOPE (54) --- examples/companion_radio/MyMesh.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 3c882d18..904cd871 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -50,6 +50,7 @@ #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 +#define CMD_SET_FLOOD_SCOPE 54 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -1515,6 +1516,13 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_FILE_IO_ERROR); } + } else if (cmd_frame[0] == CMD_SET_FLOOD_SCOPE && len >= 2 && cmd_frame[1] == 0) { + if (len >= 2 + 16) { + memcpy(send_scope.key, &cmd_frame[2], sizeof(send_scope.key)); // set curr scope TransportKey + } else { + memset(send_scope.key, 0, sizeof(send_scope.key)); // set scope to null + } + writeOKFrame(); } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); From 3ef53e64a1fd0f7b654e949f3caf16d3a760c1ba Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 5 Nov 2025 15:34:23 +1100 Subject: [PATCH 098/115] * is_name_char() bug fix --- examples/simple_repeater/MyMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index dce0f18b..0bfb7c89 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -848,7 +848,7 @@ void MyMesh::clearStats() { } static bool is_name_char(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= 'z') || c == '-' || c == '.' || c == '_' || c == '#'; + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == '#'; } void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { From 82b4c1e6b0771031d53c6c301567cb021577a9cd Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 00:56:54 +1100 Subject: [PATCH 099/115] * new PAYLOAD_TYPE_CONTROL (11) * repeater: onControlDataRecv(), now responds to new CTL_TYPE_NODE_DISCOVER_REQ (zero hop only) * node prefs: new discovery_mod_timestamp (will be set to affect when node should respond to DISCOVERY_REQ's ) --- examples/simple_repeater/MyMesh.cpp | 32 +++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 24 ++++++++++++++++++++++ src/Mesh.h | 6 ++++++ src/Packet.h | 1 + src/helpers/CommonCLI.cpp | 6 ++++-- src/helpers/CommonCLI.h | 1 + 7 files changed, 69 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 0bfb7c89..74fca86c 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -607,6 +607,38 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t return false; } +#define CTL_TYPE_NODE_DISCOVER_REQ 0x80 +#define CTL_TYPE_NODE_DISCOVER_RESP 0x90 + +void MyMesh::onControlDataRecv(mesh::Packet* packet) { + uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits + if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6) { + // TODO: apply rate limiting to these! + int i = 1; + uint8_t filter = packet->payload[i++]; + uint32_t tag; + memcpy(&tag, &packet->payload[i], 4); i += 4; + uint32_t since; + if (packet->payload_len >= i+4) { // optional since field + memcpy(&since, &packet->payload[i], 4); i += 4; + } else { + since = 0; + } + + if ((filter & (1 << ADV_TYPE_REPEATER)) != 0 && _prefs.discovery_mod_timestamp >= since) { + uint8_t data[6 + PUB_KEY_SIZE]; + data[0] = CTL_TYPE_NODE_DISCOVER_RESP | ADV_TYPE_REPEATER; // low 4-bits for node type + data[1] = packet->_snr; // let sender know the inbound SNR ( x 4) + memcpy(&data[2], &tag, 4); // include tag from request, for client to match to + memcpy(&data[6], self_id.pub_key, PUB_KEY_SIZE); + auto resp = createControlData(data, sizeof(data)); + if (resp) { + sendZeroHop(resp, getRetransmitDelay(resp)); // apply random delay, as multiple nodes can respond to this + } + } + } +} + MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 83331541..fd0b4d6a 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -157,6 +157,7 @@ protected: void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len); void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; + void onControlDataRecv(mesh::Packet* packet) override; public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 71b8eaee..f1271574 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -68,6 +68,14 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return ACTION_RELEASE; } + if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_CONTROL && (pkt->payload[0] & 0x80) != 0) { + if (pkt->path_len == 0) { + onControlDataRecv(pkt); + } + // just zero-hop control packets allowed (for this subset of payloads) + return ACTION_RELEASE; + } + if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) { if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { @@ -589,6 +597,22 @@ Packet* Mesh::createTrace(uint32_t tag, uint32_t auth_code, uint8_t flags) { return packet; } +Packet* Mesh::createControlData(const uint8_t* data, size_t len) { + if (len > sizeof(Packet::payload)) return NULL; // invalid arg + + Packet* packet = obtainNewPacket(); + if (packet == NULL) { + MESH_DEBUG_PRINTLN("%s Mesh::createControlData(): error, packet pool empty", getLogDateTime()); + return NULL; + } + packet->header = (PAYLOAD_TYPE_CONTROL << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later + + memcpy(packet->payload, data, len); + packet->payload_len = len; + + return packet; +} + void Mesh::sendFlood(Packet* packet, uint32_t delay_millis) { if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { MESH_DEBUG_PRINTLN("%s Mesh::sendFlood(): TRACE type not suspported", getLogDateTime()); diff --git a/src/Mesh.h b/src/Mesh.h index 70fa80f3..bc1ac54d 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -133,6 +133,11 @@ protected: */ virtual void onPathRecv(Packet* packet, Identity& sender, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { } + /** + * \brief A control packet has been received. + */ + virtual void onControlDataRecv(Packet* packet) { } + /** * \brief A packet with PAYLOAD_TYPE_RAW_CUSTOM has been received. */ @@ -185,6 +190,7 @@ public: Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createRawData(const uint8_t* data, size_t len); Packet* createTrace(uint32_t tag, uint32_t auth_code, uint8_t flags = 0); + Packet* createControlData(const uint8_t* data, size_t len); /** * \brief send a locally-generated Packet with flood routing diff --git a/src/Packet.h b/src/Packet.h index e52ab526..42d73f41 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -27,6 +27,7 @@ namespace mesh { #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) #define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop #define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets +#define PAYLOAD_TYPE_CONTROL 0x0B // a control/discovery packet //... #define PAYLOAD_TYPE_RAW_CUSTOM 0x0F // custom packet as raw bytes, for applications with custom encryption, payloads, etc diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 88327aa8..caed368b 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -69,7 +69,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161 - // 162 + file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 + // 166 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -146,7 +147,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161 - // 162 + file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 + // 166 file.close(); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3cfca46c..a665e014 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -46,6 +46,7 @@ struct NodePrefs { // persisted to file uint8_t gps_enabled; uint32_t gps_interval; // in seconds uint8_t advert_loc_policy; + uint32_t discovery_mod_timestamp; }; class CommonCLICallbacks { From 7419ed71f7a57f7bf6f2ec157d5222fa1fa2283c Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 12:27:25 +1100 Subject: [PATCH 100/115] * region filtering now applied in allowPacketForward() --- examples/simple_repeater/MyMesh.cpp | 22 ++++++++++++++-------- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.h | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 74fca86c..2fb01029 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -306,6 +306,10 @@ File MyMesh::openAppend(const char *fname) { bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; + if (packet->isRouteFlood() && recv_pkt_region == NULL) { + MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); + return false; + } return true; } @@ -405,19 +409,19 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { } bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { + // just try to determine region for packet (apply later in allowPacketForward()) if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { - auto region = region_map.findMatch(pkt, REGION_DENY_FLOOD); - if (region == NULL) { - MESH_DEBUG_PRINTLN("onRecvPacket: unknown transport code for FLOOD packet"); - return true; - } + recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { if (region_map.getWildcard().flags & REGION_DENY_FLOOD) { - MESH_DEBUG_PRINTLN("onRecvPacket: wildcard FLOOD packet not allowed"); - return true; + recv_pkt_region = NULL; + } else { + recv_pkt_region = ®ion_map.getWildcard(); } + } else { + recv_pkt_region = NULL; } - // otherwise do normal processing + // do normal processing return false; } @@ -973,6 +977,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply load_stack[0] = &temp_map.getWildcard(); region_load_active = true; } else if (n >= 2 && strcmp(parts[1], "save") == 0) { + _prefs.discovery_mod_timestamp = rtc_clock.getCurrentTime(); // this node is now 'modified' (for discovery info) + savePrefs(); bool success = region_map.save(_fs); strcpy(reply, success ? "OK" : "Err - save failed"); } else if (n >= 2 && strcmp(parts[1], "allowf") == 0) { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index fd0b4d6a..45001597 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -91,6 +91,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { TransportKeyStore key_store; RegionMap region_map, temp_map; RegionEntry* load_stack[8]; + RegionEntry* recv_pkt_region; bool region_load_active; unsigned long dirty_contacts_expiry; #if MAX_NEIGHBOURS diff --git a/src/Mesh.h b/src/Mesh.h index bc1ac54d..00f7ed00 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -44,6 +44,7 @@ protected: DispatcherAction routeRecvPacket(Packet* packet); /** + * \brief Called _before_ the packet is dispatched to the on..Recv() methods. * \returns true, if given packet should be NOT be processed. */ virtual bool filterRecvFloodPacket(Packet* packet) { return false; } From cf547da85735f86df5836e6803708845a7677ac8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 17:28:45 +1100 Subject: [PATCH 101/115] * RegionMap: get/set Home Region * repeater: admin CLI, changed "allowf *", "denyf *", added "home" --- examples/simple_repeater/MyMesh.cpp | 21 ++++++++++++++----- src/helpers/RegionMap.cpp | 32 +++++++++++++++++++++-------- src/helpers/RegionMap.h | 4 +++- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 2fb01029..7489d611 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -637,7 +637,7 @@ void MyMesh::onControlDataRecv(mesh::Packet* packet) { memcpy(&data[6], self_id.pub_key, PUB_KEY_SIZE); auto resp = createControlData(data, sizeof(data)); if (resp) { - sendZeroHop(resp, getRetransmitDelay(resp)); // apply random delay, as multiple nodes can respond to this + sendZeroHop(resp, getRetransmitDelay(resp)*4); // apply random delay (widened x4), as multiple nodes can respond to this } } } @@ -981,16 +981,16 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply savePrefs(); bool success = region_map.save(_fs); strcpy(reply, success ? "OK" : "Err - save failed"); - } else if (n >= 2 && strcmp(parts[1], "allowf") == 0) { - auto region = n >= 3 ? region_map.findByNamePrefix(parts[2]) : ®ion_map.getWildcard(); + } else if (n >= 3 && strcmp(parts[1], "allowf") == 0) { + auto region = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); if (region) { region->flags &= ~REGION_DENY_FLOOD; strcpy(reply, "OK"); } else { strcpy(reply, "Err - unknown region"); } - } else if (n >= 2 && strcmp(parts[1], "denyf") == 0) { - auto region = n >= 3 ? region_map.findByNamePrefix(parts[2]) : ®ion_map.getWildcard(); + } else if (n >= 3 && strcmp(parts[1], "denyf") == 0) { + auto region = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); if (region) { region->flags |= REGION_DENY_FLOOD; strcpy(reply, "OK"); @@ -1004,6 +1004,17 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else { strcpy(reply, "Err - unknown region"); } + } else if (n >= 3 && strcmp(parts[1], "home") == 0) { + auto home = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); + if (home) { + region_map.setHomeRegion(home); + sprintf(reply, " home is now %s", home->name); + } else { + strcpy(reply, "Err - unknown region"); + } + } else if (n == 2 && strcmp(parts[1], "home") == 0) { + auto home = region_map.getHomeRegion(); + sprintf(reply, " home is %s", home ? home->name : "*"); } else { strcpy(reply, "Err - ??"); } diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 074084ec..c6221db0 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -3,10 +3,10 @@ #include RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) { - next_id = 1; num_regions = 0; + next_id = 1; num_regions = 0; home_id = 0; wildcard.id = wildcard.parent = 0; wildcard.flags = 0; // default behaviour, allow flood and direct - strcpy(wildcard.name, "(*)"); + strcpy(wildcard.name, "*"); } static File openWrite(FILESYSTEM* _fs, const char* filename) { @@ -31,9 +31,10 @@ bool RegionMap::load(FILESYSTEM* _fs) { if (file) { uint8_t pad[128]; - num_regions = 0; next_id = 1; + num_regions = 0; next_id = 1; home_id = 0; - bool success = file.read(pad, 7) == 7; // reserved header + bool success = file.read(pad, 5) == 5; // reserved header + success = success && file.read((uint8_t *) &home_id, sizeof(home_id)) == sizeof(home_id); success = success && file.read((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); success = success && file.read((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); @@ -68,7 +69,8 @@ bool RegionMap::save(FILESYSTEM* _fs) { uint8_t pad[128]; memset(pad, 0, sizeof(pad)); - bool success = file.write(pad, 7) == 7; // reserved header + bool success = file.write(pad, 5) == 5; // reserved header + success = success && file.write((uint8_t *) &home_id, sizeof(home_id)) == sizeof(home_id); success = success && file.write((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); success = success && file.write((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); @@ -140,11 +142,15 @@ RegionEntry* RegionMap::findByName(const char* name) { } RegionEntry* RegionMap::findByNamePrefix(const char* prefix) { + RegionEntry* partial = NULL; for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; - if (memcmp(prefix, region->name, strlen(prefix)) == 0) return region; + if (strcmp(prefix, region->name) == 0) return region; // is a complete match, preference this one + if (memcmp(prefix, region->name, strlen(prefix)) == 0) { + partial = region; + } } - return NULL; // not found + return partial; } RegionEntry* RegionMap::findById(uint16_t id) { @@ -157,6 +163,14 @@ RegionEntry* RegionMap::findById(uint16_t id) { return NULL; // not found } +RegionEntry* RegionMap::getHomeRegion() { + return findById(home_id); +} + +void RegionMap::setHomeRegion(const RegionEntry* home) { + home_id = home ? home->id : 0; +} + bool RegionMap::removeRegion(const RegionEntry& region) { if (region.id == 0) return false; // failed (cannot remove the wildcard Region) @@ -190,9 +204,9 @@ void RegionMap::printChildRegions(int indent, const RegionEntry* parent, Stream& } if (parent->flags & REGION_DENY_FLOOD) { - out.printf("%s\n", parent->name); + out.printf("%s%s\n", parent->name, parent->id == home_id ? "^" : ""); } else { - out.printf("%s F\n", parent->name); + out.printf("%s%s F\n", parent->name, parent->id == home_id ? "^" : ""); } for (int i = 0; i < num_regions; i++) { diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h index 5ad9df29..c3f897c5 100644 --- a/src/helpers/RegionMap.h +++ b/src/helpers/RegionMap.h @@ -20,7 +20,7 @@ struct RegionEntry { class RegionMap { TransportKeyStore* _store; - uint16_t next_id; + uint16_t next_id, home_id; uint16_t num_regions; RegionEntry regions[MAX_REGION_ENTRIES]; RegionEntry wildcard; @@ -39,6 +39,8 @@ public: RegionEntry* findByName(const char* name); RegionEntry* findByNamePrefix(const char* prefix); RegionEntry* findById(uint16_t id); + RegionEntry* getHomeRegion(); // NOTE: can be NULL + void setHomeRegion(const RegionEntry* home); bool removeRegion(const RegionEntry& region); bool clear(); void resetFrom(const RegionMap& src) { num_regions = 0; next_id = src.next_id; } From 09eab330a2f3f22008948aa244a81e29462a985d Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 20:15:01 +1100 Subject: [PATCH 102/115] * repeater: onAnonDataRecv(), now rejecting non-ASCII password (preparing for future request codes) * repeater: DISCOVER requests now with a simple RateLimiter (max 4, every 2 minutes) --- examples/simple_repeater/MyMesh.cpp | 15 +++++++++++---- examples/simple_repeater/MyMesh.h | 2 ++ examples/simple_repeater/RateLimiter.h | 23 +++++++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 examples/simple_repeater/RateLimiter.h diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 7489d611..9d313d21 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -433,7 +433,14 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m memcpy(×tamp, data, 4); data[len] = 0; // ensure null terminator - uint8_t reply_len = handleLoginReq(sender, secret, timestamp, &data[4]); + uint8_t reply_len; + if (data[0] == 0 || data[0] >= ' ') { // is password, ie. a login request + reply_len = handleLoginReq(sender, secret, timestamp, &data[4]); + //} else if (data[0] == ANON_REQ_TYPE_*) { // future type codes + // TODO + } else { + reply_len = 0; // unknown request type + } if (reply_len == 0) return; // invalid request @@ -616,8 +623,7 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t void MyMesh::onControlDataRecv(mesh::Packet* packet) { uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits - if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6) { - // TODO: apply rate limiting to these! + if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6 && discover_limiter.allow(rtc_clock.getCurrentTime())) { int i = 1; uint8_t filter = packet->payload[i++]; uint32_t tag; @@ -646,7 +652,8 @@ void MyMesh::onControlDataRecv(mesh::Packet* packet) { MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store) + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store), + discover_limiter(4, 120) // max 4 every 2 minutes #if defined(WITH_RS232_BRIDGE) , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) #endif diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 45001597..86fac3f4 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -33,6 +33,7 @@ #include #include #include +#include "RateLimiter.h" #ifdef WITH_BRIDGE extern AbstractBridge* bridge; @@ -92,6 +93,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { RegionMap region_map, temp_map; RegionEntry* load_stack[8]; RegionEntry* recv_pkt_region; + RateLimiter discover_limiter; bool region_load_active; unsigned long dirty_contacts_expiry; #if MAX_NEIGHBOURS diff --git a/examples/simple_repeater/RateLimiter.h b/examples/simple_repeater/RateLimiter.h new file mode 100644 index 00000000..a6633c0a --- /dev/null +++ b/examples/simple_repeater/RateLimiter.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class RateLimiter { + uint32_t _start_timestamp; + uint32_t _secs; + uint16_t _maximum, _count; + +public: + RateLimiter(uint16_t maximum, uint32_t secs): _maximum(maximum), _secs(secs), _start_timestamp(0), _count(0) { } + + bool allow(uint32_t now) { + if (now < _start_timestamp + _secs) { + _count++; + if (_count > _maximum) return false; // deny + } else { // time window now expired + _start_timestamp = now; + _count = 1; + } + return true; + } +}; \ No newline at end of file From 256848208d6303b2f11f16d5026d027a3be7c0e8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 20:22:40 +1100 Subject: [PATCH 103/115] * repeater: onAnonDataRecv(), future code check bug fix (offset 4) * sensor: onAnonDataRecv(), future request code provision --- examples/simple_repeater/MyMesh.cpp | 4 ++-- examples/simple_sensor/SensorMesh.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 9d313d21..a996cbf6 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -434,9 +434,9 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m data[len] = 0; // ensure null terminator uint8_t reply_len; - if (data[0] == 0 || data[0] >= ' ') { // is password, ie. a login request + if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request reply_len = handleLoginReq(sender, secret, timestamp, &data[4]); - //} else if (data[0] == ANON_REQ_TYPE_*) { // future type codes + //} else if (data[4] == ANON_REQ_TYPE_*) { // future type codes // TODO } else { reply_len = 0; // unknown request type diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 6564c4ef..58bce766 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -449,7 +449,14 @@ void SensorMesh::onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, con memcpy(×tamp, data, 4); data[len] = 0; // ensure null terminator - uint8_t reply_len = handleLoginReq(sender, secret, timestamp, &data[4]); + uint8_t reply_len; + if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request + reply_len = handleLoginReq(sender, secret, timestamp, &data[4]); + //} else if (data[4] == ANON_REQ_TYPE_*) { // future type codes + // TODO + } else { + reply_len = 0; // unknown request type + } if (reply_len == 0) return; // invalid request From ddac13ae80c74960a0879975741236f9021dc2cd Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 21:40:52 +1100 Subject: [PATCH 104/115] * repeater: CLI, added "region put" and "region remove" commands --- examples/simple_repeater/MyMesh.cpp | 29 ++++++++++++++++++++++++----- src/helpers/RegionMap.cpp | 13 ++++++++++++- src/helpers/RegionMap.h | 2 ++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index a996cbf6..b06ae42a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -890,10 +890,6 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } -static bool is_name_char(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == '#'; -} - void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { if (region_load_active) { if (*command == 0) { // empty line, signal to terminate 'load' operation @@ -907,7 +903,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply int indent = np - command; char *ep = np; - while (is_name_char(*ep)) ep++; + while (RegionMap::is_name_char(*ep)) ep++; if (*ep) { *ep++ = 0; } // set null terminator for end of name while (*ep && *ep != 'F') ep++; // look for (optional) flags @@ -1022,6 +1018,29 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else if (n == 2 && strcmp(parts[1], "home") == 0) { auto home = region_map.getHomeRegion(); sprintf(reply, " home is %s", home ? home->name : "*"); + } else if (n >= 3 && strcmp(parts[1], "put") == 0) { + auto parent = n >= 4 ? region_map.findByNamePrefix(parts[3]) : ®ion_map.getWildcard(); + if (parent == NULL) { + strcpy(reply, "Err - unknown parent"); + } else { + auto region = region_map.putRegion(parts[2], parent->id); + if (region == NULL) { + strcpy(reply, "Err - unable to put"); + } else { + strcpy(reply, "OK"); + } + } + } else if (n >= 3 && strcmp(parts[1], "remove") == 0) { + auto region = region_map.findByName(parts[2]); + if (region) { + if (region_map.removeRegion(*region)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - not empty"); + } + } else { + strcpy(reply, "Err - not found"); + } } else { strcpy(reply, "Err - ??"); } diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index c6221db0..7d1c08e6 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -9,6 +9,10 @@ RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) { strcpy(wildcard.name, "*"); } +bool RegionMap::is_name_char(char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == '#'; +} + static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); @@ -93,6 +97,12 @@ bool RegionMap::save(FILESYSTEM* _fs) { } RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id, uint16_t id) { + const char* sp = name; // check for illegal name chars + while (*sp) { + if (!is_name_char(*sp)) return NULL; // error + sp++; + } + auto region = findByName(name); if (region) { if (region->id == parent_id) return NULL; // ERROR: invalid parent! @@ -187,8 +197,9 @@ bool RegionMap::removeRegion(const RegionEntry& region) { if (i >= num_regions) return false; // failed (not found) num_regions--; // remove from regions array - while (i + 1 < num_regions) { + while (i < num_regions) { regions[i] = regions[i + 1]; + i++; } return true; // success } diff --git a/src/helpers/RegionMap.h b/src/helpers/RegionMap.h index c3f897c5..50513be1 100644 --- a/src/helpers/RegionMap.h +++ b/src/helpers/RegionMap.h @@ -30,6 +30,8 @@ class RegionMap { public: RegionMap(TransportKeyStore& store); + static bool is_name_char(char c); + bool load(FILESYSTEM* _fs); bool save(FILESYSTEM* _fs); From 4a5404d997ce89dae793e47b498f076b9a244d76 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 22:10:20 +1100 Subject: [PATCH 105/115] * companion: added CMD_SEND_CONTROL_DATA, and PUSH_CODE_CONTROL_DATA --- examples/companion_radio/MyMesh.cpp | 30 +++++++++++++++++++++++++++++ examples/companion_radio/MyMesh.h | 1 + 2 files changed, 31 insertions(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 904cd871..16be3e9c 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -51,6 +51,7 @@ #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 #define CMD_SET_FLOOD_SCOPE 54 +#define CMD_SEND_CONTROL_DATA 55 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -100,6 +101,7 @@ #define PUSH_CODE_TELEMETRY_RESPONSE 0x8B #define PUSH_CODE_BINARY_RESPONSE 0x8C #define PUSH_CODE_PATH_DISCOVERY_RESPONSE 0x8D +#define PUSH_CODE_CONTROL_DATA 0x8E #define ERR_CODE_UNSUPPORTED_CMD 1 #define ERR_CODE_NOT_FOUND 2 @@ -626,6 +628,26 @@ bool MyMesh::onContactPathRecv(ContactInfo& contact, uint8_t* in_path, uint8_t i return BaseChatMesh::onContactPathRecv(contact, in_path, in_path_len, out_path, out_path_len, extra_type, extra, extra_len); } +void MyMesh::onControlDataRecv(mesh::Packet *packet) { + if (packet->payload_len + 4 > sizeof(out_frame)) { + MESH_DEBUG_PRINTLN("onControlDataRecv(), payload_len too long: %d", packet->payload_len); + return; + } + int i = 0; + out_frame[i++] = PUSH_CODE_CONTROL_DATA; + out_frame[i++] = (int8_t)(_radio->getLastSNR() * 4); + out_frame[i++] = (int8_t)(_radio->getLastRSSI()); + out_frame[i++] = packet->path_len; + memcpy(&out_frame[i], packet->payload, packet->payload_len); + i += packet->payload_len; + + if (_serial->isConnected()) { + _serial->writeFrame(out_frame, i); + } else { + MESH_DEBUG_PRINTLN("onControlDataRecv(), data received while app offline"); + } +} + void MyMesh::onRawDataRecv(mesh::Packet *packet) { if (packet->payload_len + 4 > sizeof(out_frame)) { MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len); @@ -1523,6 +1545,14 @@ void MyMesh::handleCmdFrame(size_t len) { memset(send_scope.key, 0, sizeof(send_scope.key)); // set scope to null } writeOKFrame(); + } else if (cmd_frame[0] == CMD_SEND_CONTROL_DATA && len >= 2 && (cmd_frame[1] & 0x80) != 0) { + auto resp = createControlData(&cmd_frame[1], len - 1); + if (resp) { + sendZeroHop(resp); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_TABLE_FULL); + } } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index fb0fb479..927f22e4 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -133,6 +133,7 @@ protected: uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) override; void onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) override; + void onControlDataRecv(mesh::Packet *packet) override; void onRawDataRecv(mesh::Packet *packet) override; void onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t *path_snrs, const uint8_t *path_hashes, uint8_t path_len) override; From 2e63499ae594e14842a3be35f5678389b748ad0a Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 6 Nov 2025 22:51:17 +1100 Subject: [PATCH 106/115] * companion: protocol ver bumped to 8. --- examples/companion_radio/MyMesh.cpp | 6 +++--- examples/companion_radio/MyMesh.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 16be3e9c..598d535f 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -50,8 +50,8 @@ #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 -#define CMD_SET_FLOOD_SCOPE 54 -#define CMD_SEND_CONTROL_DATA 55 +#define CMD_SET_FLOOD_SCOPE 54 // v8+ +#define CMD_SEND_CONTROL_DATA 55 // v8+ #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -101,7 +101,7 @@ #define PUSH_CODE_TELEMETRY_RESPONSE 0x8B #define PUSH_CODE_BINARY_RESPONSE 0x8C #define PUSH_CODE_PATH_DISCOVERY_RESPONSE 0x8D -#define PUSH_CODE_CONTROL_DATA 0x8E +#define PUSH_CODE_CONTROL_DATA 0x8E // v8+ #define ERR_CODE_UNSUPPORTED_CMD 1 #define ERR_CODE_NOT_FOUND 2 diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 927f22e4..f2b56e5e 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -5,7 +5,7 @@ #include "AbstractUITask.h" /*------------ Frame Protocol --------------*/ -#define FIRMWARE_VER_CODE 7 +#define FIRMWARE_VER_CODE 8 #ifndef FIRMWARE_BUILD_DATE #define FIRMWARE_BUILD_DATE "2 Oct 2025" From 06825030e5d48098857a63a26165cb88d0221218 Mon Sep 17 00:00:00 2001 From: Florent Date: Thu, 6 Nov 2025 22:36:37 +0100 Subject: [PATCH 107/115] sensor: copy control data code from repeater --- examples/simple_sensor/SensorMesh.cpp | 32 +++++++++++++++++++++++++++ examples/simple_sensor/SensorMesh.h | 1 + 2 files changed, 33 insertions(+) diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 58bce766..2ab2081f 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -617,6 +617,38 @@ bool SensorMesh::handleIncomingMsg(ClientInfo& from, uint32_t timestamp, uint8_t return false; } +#define CTL_TYPE_NODE_DISCOVER_REQ 0x80 +#define CTL_TYPE_NODE_DISCOVER_RESP 0x90 + +void SensorMesh::onControlDataRecv(mesh::Packet* packet) { + uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits + if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6) { + // TODO: apply rate limiting to these! + int i = 1; + uint8_t filter = packet->payload[i++]; + uint32_t tag; + memcpy(&tag, &packet->payload[i], 4); i += 4; + uint32_t since; + if (packet->payload_len >= i+4) { // optional since field + memcpy(&since, &packet->payload[i], 4); i += 4; + } else { + since = 0; + } + + if ((filter & (1 << ADV_TYPE_SENSOR)) != 0 && _prefs.discovery_mod_timestamp >= since) { + uint8_t data[6 + PUB_KEY_SIZE]; + data[0] = CTL_TYPE_NODE_DISCOVER_RESP | ADV_TYPE_SENSOR; // low 4-bits for node type + data[1] = packet->_snr; // let sender know the inbound SNR ( x 4) + memcpy(&data[2], &tag, 4); // include tag from request, for client to match to + memcpy(&data[6], self_id.pub_key, PUB_KEY_SIZE); + auto resp = createControlData(data, sizeof(data)); + if (resp) { + sendZeroHop(resp, getRetransmitDelay(resp)); // apply random delay, as multiple nodes can respond to this + } + } + } +} + bool SensorMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { int i = matching_peer_indexes[sender_idx]; if (i < 0 || i >= acl.getNumClients()) { diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index ba55bc70..0fb00dc1 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -125,6 +125,7 @@ protected: void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override; void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; + void onControlDataRecv(mesh::Packet* packet) override; void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override; virtual bool handleIncomingMsg(ClientInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len); void sendAckTo(const ClientInfo& dest, uint32_t ack_hash); From 963290ea159c00edf4283bafd71464b27c8d95dc Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 7 Nov 2025 14:42:06 +1100 Subject: [PATCH 108/115] * repeater: various "region" CLI changes * transport codes 0000 and FFFF reserved --- examples/simple_repeater/MyMesh.cpp | 17 +++++++++++------ src/helpers/RegionMap.cpp | 4 ++++ src/helpers/TransportKeyStore.cpp | 5 +++++ src/helpers/TxtDataHelpers.cpp | 7 +++++++ src/helpers/TxtDataHelpers.h | 1 + 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index b06ae42a..cae05b20 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -892,7 +892,7 @@ void MyMesh::clearStats() { void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { if (region_load_active) { - if (*command == 0) { // empty line, signal to terminate 'load' operation + if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map region_load_active = false; @@ -908,7 +908,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply while (*ep && *ep != 'F') ep++; // look for (optional) flags - if (indent > 0 && indent < 8) { + if (indent > 0 && indent < 8 && strlen(np) > 0) { auto parent = load_stack[indent - 1]; if (parent) { auto old = region_map.findByName(np); @@ -985,7 +985,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply bool success = region_map.save(_fs); strcpy(reply, success ? "OK" : "Err - save failed"); } else if (n >= 3 && strcmp(parts[1], "allowf") == 0) { - auto region = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); + auto region = region_map.findByNamePrefix(parts[2]); if (region) { region->flags &= ~REGION_DENY_FLOOD; strcpy(reply, "OK"); @@ -993,7 +993,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply strcpy(reply, "Err - unknown region"); } } else if (n >= 3 && strcmp(parts[1], "denyf") == 0) { - auto region = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); + auto region = region_map.findByNamePrefix(parts[2]); if (region) { region->flags |= REGION_DENY_FLOOD; strcpy(reply, "OK"); @@ -1003,12 +1003,17 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else if (n >= 3 && strcmp(parts[1], "get") == 0) { auto region = region_map.findByNamePrefix(parts[2]); if (region) { - sprintf(reply, " %s %s", region->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); + auto parent = region_map.findById(region->parent); + if (parent && parent->id != 0) { + sprintf(reply, " %s (%s) %s", region->name, parent->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); + } else { + sprintf(reply, " %s %s", region->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); + } } else { strcpy(reply, "Err - unknown region"); } } else if (n >= 3 && strcmp(parts[1], "home") == 0) { - auto home = strcmp(parts[2], "*") == 0 ? ®ion_map.getWildcard() : region_map.findByNamePrefix(parts[2]); + auto home = region_map.findByNamePrefix(parts[2]); if (home) { region_map.setHomeRegion(home); sprintf(reply, " home is now %s", home->name); diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 7d1c08e6..36844615 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -144,6 +144,8 @@ RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { } RegionEntry* RegionMap::findByName(const char* name) { + if (strcmp(name, "*") == 0) return &wildcard; + for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; if (strcmp(name, region->name) == 0) return region; @@ -152,6 +154,8 @@ RegionEntry* RegionMap::findByName(const char* name) { } RegionEntry* RegionMap::findByNamePrefix(const char* prefix) { + if (strcmp(prefix, "*") == 0) return &wildcard; + RegionEntry* partial = NULL; for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; diff --git a/src/helpers/TransportKeyStore.cpp b/src/helpers/TransportKeyStore.cpp index 8b7c891b..f34610b6 100644 --- a/src/helpers/TransportKeyStore.cpp +++ b/src/helpers/TransportKeyStore.cpp @@ -9,6 +9,11 @@ uint16_t TransportKey::calcTransportCode(const mesh::Packet* packet) const { sha.update(&type, 1); sha.update(packet->payload, packet->payload_len); sha.finalizeHMAC(key, sizeof(key), &code, 2); + if (code == 0) { // reserve codes 0000 and FFFF + code++; + } else if (code == 0xFFFF) { + code--; + } return code; } diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp index 0044fd28..224eb873 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -19,6 +19,13 @@ void StrHelper::strzcpy(char* dest, const char* src, size_t buf_sz) { } } +bool StrHelper::isBlank(const char* str) { + while (*str) { + if (*str++ != ' ') return false; + } + return true; +} + #include union int32_Float_t diff --git a/src/helpers/TxtDataHelpers.h b/src/helpers/TxtDataHelpers.h index 3154766c..89789990 100644 --- a/src/helpers/TxtDataHelpers.h +++ b/src/helpers/TxtDataHelpers.h @@ -12,4 +12,5 @@ public: static void strncpy(char* dest, const char* src, size_t buf_sz); static void strzcpy(char* dest, const char* src, size_t buf_sz); // pads with trailing nulls static const char* ftoa(float f); + static bool isBlank(const char* str); }; From 62d7ce110b912b55a5e1fd5107ecfabaac7cb694 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 7 Nov 2025 15:49:01 +1100 Subject: [PATCH 109/115] * packet format docs updated --- docs/packet_structure.md | 4 +++ docs/payloads.md | 56 +++++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/docs/packet_structure.md b/docs/packet_structure.md index aa260855..92c410be 100644 --- a/docs/packet_structure.md +++ b/docs/packet_structure.md @@ -44,6 +44,10 @@ bit 0 means the lowest bit (1s place) | `0x08` | `PAYLOAD_TYPE_PATH` | Returned path. | | `0x09` | `PAYLOAD_TYPE_TRACE` | trace a path, collecting SNI for each hop. | | `0x0A` | `PAYLOAD_TYPE_MULTIPART` | packet is part of a sequence of packets. | +| `0x0B` | `PAYLOAD_TYPE_CONTROL` | control packet data (unencrypted) | +| `0x0C` | . | reserved | +| `0x0D` | . | reserved | +| `0x0E` | . | reserved | | `0x0F` | `PAYLOAD_TYPE_RAW_CUSTOM` | Custom packet (raw bytes, custom encryption). | ## Payload Version Values diff --git a/docs/payloads.md b/docs/payloads.md index 4d00f930..f66088f7 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -11,6 +11,7 @@ Inside of each [meshcore packet](./packet_structure.md) is a payload, identified * Group text message (unverified). * Group datagram (unverified). * Multi-part packet +* Control data packet * Custom packet (raw bytes, custom encryption). This document defines the structure of each of these payload types. @@ -57,7 +58,7 @@ Appdata Flags # Acknowledgement -An acknowledgement that a message was received. Note that for returned path messages, an acknowledgement will be sent in the "extra" payload (see [Returned Path](#returned-path)) and not as a discrete ackowledgement. CLI commands do not require an acknowledgement, neither discrete nor extra. +An acknowledgement that a message was received. Note that for returned path messages, an acknowledgement can be sent in the "extra" payload (see [Returned Path](#returned-path)) instead of as a separate ackowledgement packet. CLI commands do not cause acknowledgement responses, neither discrete nor extra. | Field | Size (bytes) | Description | |----------|--------------|------------------------------------------------------------| @@ -140,13 +141,13 @@ Request data about sensors on the node, including battery level. ## Plain text message -| Field | Size (bytes) | Description | -|-----------------|-----------------|--------------------------------------------------------------| -| timestamp | 4 | send time (unix timestamp) | -| flags + attempt | 1 | upper six bits are flags (see below), lower two bits are attempt number (0..3) | -| message | rest of payload | the message content, see next table | +| Field | Size (bytes) | Description | +|--------------------|-----------------|--------------------------------------------------------------| +| timestamp | 4 | send time (unix timestamp) | +| txt_type + attempt | 1 | upper six bits are txt_type (see below), lower two bits are attempt number (0..3) | +| message | rest of payload | the message content, see next table | -Flags +txt_type | Value | Description | Message content | |--------|---------------------------|------------------------------------------------------------| @@ -163,13 +164,20 @@ Flags | cipher MAC | 2 | MAC for encrypted data in next field | | ciphertext | rest of payload | encrypted message, see below for details | -Plaintext message +## Room server login | Field | Size (bytes) | Description | |----------------|-----------------|-------------------------------------------------------------------------------| -| timestamp | 4 | send time (unix timestamp) | -| sync timestamp | 4 | NOTE: room server only! - sender's "sync messages SINCE x" timestamp | -| password | rest of message | password for repeater/room | +| timestamp | 4 | sender time (unix timestamp) | +| sync timestamp | 4 | sender's "sync messages SINCE x" timestamp | +| password | rest of message | password for room | + +## Repeater/Sensor login + +| Field | Size (bytes) | Description | +|----------------|-----------------|-------------------------------------------------------------------------------| +| timestamp | 4 | sender time (unix timestamp) | +| password | rest of message | password for repeater/sensor | # Group text message / datagram @@ -182,7 +190,31 @@ Plaintext message The plaintext contained in the ciphertext matches the format described in [plain text message](#plain-text-message). Specifically, it consists of a four byte timestamp, a flags byte, and the message. The flags byte will generally be `0x00` because it is a "plain text message". The message will be of the form `: ` (eg., `user123: I'm on my way`). -TODO: describe what datagram looks like +# Control data + +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| flags | 1 | upper 4 bits is sub_type | +| data | rest of payload | typically unencrypted data | + +## DISCOVER_REQ (sub_type) + +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| flags | 1 | 0x8 (upper 4 bits) | +| type_filter | 1 | bit for each ADV_TYPE_* | +| tag | 4 | randomly generate by sender | +| since | 4 | (optional) epoch timestamp (0 by default) | + +## DISCOVER_RESP (sub_type) + +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| flags | 1 | 0x9 (upper 4 bits), node_type (lower 4) | +| snr | 1 | signed, SNR*4 | +| tag | 4 | reflected back from DISCOVER_REQ | +| pubkey | 32 | node's ID | + # Custom packet From 1520f4d28e3b0e512155567bdcfd12ac54922c94 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 7 Nov 2025 19:31:09 +1100 Subject: [PATCH 110/115] * repeater, DISCOVER_REQ, flags lowest bit now for 'prefix_only' responses --- docs/payloads.md | 14 +++++++------- examples/simple_repeater/MyMesh.cpp | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/payloads.md b/docs/payloads.md index f66088f7..5a41e69c 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -199,12 +199,12 @@ The plaintext contained in the ciphertext matches the format described in [plain ## DISCOVER_REQ (sub_type) -| Field | Size (bytes) | Description | -|--------------|-----------------|--------------------------------------------| -| flags | 1 | 0x8 (upper 4 bits) | -| type_filter | 1 | bit for each ADV_TYPE_* | -| tag | 4 | randomly generate by sender | -| since | 4 | (optional) epoch timestamp (0 by default) | +| Field | Size (bytes) | Description | +|--------------|-----------------|----------------------------------------------| +| flags | 1 | 0x8 (upper 4 bits), prefix_only (lowest bit) | +| type_filter | 1 | bit for each ADV_TYPE_* | +| tag | 4 | randomly generate by sender | +| since | 4 | (optional) epoch timestamp (0 by default) | ## DISCOVER_RESP (sub_type) @@ -213,7 +213,7 @@ The plaintext contained in the ciphertext matches the format described in [plain | flags | 1 | 0x9 (upper 4 bits), node_type (lower 4) | | snr | 1 | signed, SNR*4 | | tag | 4 | reflected back from DISCOVER_REQ | -| pubkey | 32 | node's ID | +| pubkey | 8 or 32 | node's ID (or prefix) | # Custom packet diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index cae05b20..622b73a6 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -636,12 +636,13 @@ void MyMesh::onControlDataRecv(mesh::Packet* packet) { } if ((filter & (1 << ADV_TYPE_REPEATER)) != 0 && _prefs.discovery_mod_timestamp >= since) { + bool prefix_only = packet->payload[0] & 1; uint8_t data[6 + PUB_KEY_SIZE]; data[0] = CTL_TYPE_NODE_DISCOVER_RESP | ADV_TYPE_REPEATER; // low 4-bits for node type data[1] = packet->_snr; // let sender know the inbound SNR ( x 4) memcpy(&data[2], &tag, 4); // include tag from request, for client to match to memcpy(&data[6], self_id.pub_key, PUB_KEY_SIZE); - auto resp = createControlData(data, sizeof(data)); + auto resp = createControlData(data, prefix_only ? 6 + 8 : 6 + PUB_KEY_SIZE); if (resp) { sendZeroHop(resp, getRetransmitDelay(resp)*4); // apply random delay (widened x4), as multiple nodes can respond to this } From b31d3e7b5f90c7b6f562079df02e4a88cf93c97c Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 9 Nov 2025 16:17:49 +1100 Subject: [PATCH 111/115] * added StrHelper::fromHex() --- src/helpers/TxtDataHelpers.cpp | 20 ++++++++++++++++++++ src/helpers/TxtDataHelpers.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp index 224eb873..09e86c67 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -139,3 +139,23 @@ const char* StrHelper::ftoa(float f) { } return tmp; } + +uint32_t StrHelper::fromHex(const char* src) { + uint32_t n = 0; + while (*src) { + if (*src >= '0' && *src <= '9') { + n <<= 4; + n |= (*src - '0'); + } else if (*src >= 'A' && *src <= 'F') { + n <<= 4; + n |= (*src - 'A' + 10); + } else if (*src >= 'a' && *src <= 'f') { + n <<= 4; + n |= (*src - 'a' + 10); + } else { + break; // non-hex char encountered, stop parsing + } + src++; + } + return n; +} diff --git a/src/helpers/TxtDataHelpers.h b/src/helpers/TxtDataHelpers.h index 89789990..387e09b9 100644 --- a/src/helpers/TxtDataHelpers.h +++ b/src/helpers/TxtDataHelpers.h @@ -13,4 +13,5 @@ public: static void strzcpy(char* dest, const char* src, size_t buf_sz); // pads with trailing nulls static const char* ftoa(float f); static bool isBlank(const char* str); + static uint32_t fromHex(const char* src); }; From ab0721d6dfaa66aa1271196ee7b7abe64c880686 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 9 Nov 2025 16:36:23 +1100 Subject: [PATCH 112/115] * fix: repeater and room server telemetry requests now return all telemetry for _READ & _WRITE ACL permissions. --- examples/simple_repeater/MyMesh.cpp | 5 ++++- examples/simple_room_server/MyMesh.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 622b73a6..4136818c 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -170,7 +170,10 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific - sensors.querySensors((sender->isAdmin() ? 0xFF : 0x00) & perm_mask, telemetry); + if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { + perm_mask = 0x00; // just base telemetry allowed + } + sensors.querySensors(perm_mask, telemetry); uint8_t tlen = telemetry.getSize(); memcpy(&reply_data[4], telemetry.getBuffer(), tlen); diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 4d953c9c..de06b4c6 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -165,7 +165,10 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific - sensors.querySensors((sender->isAdmin() ? 0xFF : 0x00) & perm_mask, telemetry); + if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { + perm_mask = 0x00; // just base telemetry allowed + } + sensors.querySensors(perm_mask, telemetry); uint8_t tlen = telemetry.getSize(); memcpy(&reply_data[4], telemetry.getBuffer(), tlen); From b59d1999e6f6d79aa673e136cc52319229444bc0 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 11 Nov 2025 20:08:05 +1100 Subject: [PATCH 113/115] * Sensor: DISCOVER_REQ, prefix_only support --- examples/simple_sensor/SensorMesh.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 2ab2081f..20d9921b 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -636,14 +636,15 @@ void SensorMesh::onControlDataRecv(mesh::Packet* packet) { } if ((filter & (1 << ADV_TYPE_SENSOR)) != 0 && _prefs.discovery_mod_timestamp >= since) { + bool prefix_only = packet->payload[0] & 1; uint8_t data[6 + PUB_KEY_SIZE]; data[0] = CTL_TYPE_NODE_DISCOVER_RESP | ADV_TYPE_SENSOR; // low 4-bits for node type data[1] = packet->_snr; // let sender know the inbound SNR ( x 4) memcpy(&data[2], &tag, 4); // include tag from request, for client to match to memcpy(&data[6], self_id.pub_key, PUB_KEY_SIZE); - auto resp = createControlData(data, sizeof(data)); + auto resp = createControlData(data, prefix_only ? 6 + 8 : 6 + PUB_KEY_SIZE); if (resp) { - sendZeroHop(resp, getRetransmitDelay(resp)); // apply random delay, as multiple nodes can respond to this + sendZeroHop(resp, getRetransmitDelay(resp)*4); // apply random delay (widened x4), as multiple nodes can respond to this } } } From 750e955f193780b8eb1065b21691eb7e704b4377 Mon Sep 17 00:00:00 2001 From: fdlamotte Date: Thu, 13 Nov 2025 10:39:20 +0100 Subject: [PATCH 114/115] Update library.json to latest libs and version --- library.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index 572c55b0..aa37cb6e 100644 --- a/library.json +++ b/library.json @@ -1,14 +1,14 @@ { "name": "MeshCore", - "version" : "1.8.0", + "version" : "1.10.0", "dependencies": { "SPI": "*", "Wire": "*", - "jgromes/RadioLib": "^7.1.2", + "jgromes/RadioLib": "^7.3.0", "rweather/Crypto": "^0.4.0", "adafruit/RTClib": "^2.1.3", "melopero/Melopero RV3028": "^1.1.0", - "electroniccats/CayenneLPP": "1.4.0" + "electroniccats/CayenneLPP": "1.6.1" }, "build": { "extraScript": "build_as_lib.py" From 91e9fcea4b76307d0e109730e848ddb819df9360 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 13 Nov 2025 20:45:38 +1100 Subject: [PATCH 115/115] * ver 1.10.0 --- examples/companion_radio/MyMesh.h | 4 ++-- examples/simple_repeater/MyMesh.h | 4 ++-- examples/simple_room_server/MyMesh.h | 4 ++-- examples/simple_sensor/SensorMesh.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index f2b56e5e..927ec65e 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -8,11 +8,11 @@ #define FIRMWARE_VER_CODE 8 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "2 Oct 2025" +#define FIRMWARE_BUILD_DATE "13 Nov 2025" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.9.1" +#define FIRMWARE_VERSION "v1.10.0" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 00720bf7..d8a20486 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -68,11 +68,11 @@ struct NeighbourInfo { }; #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "2 Oct 2025" + #define FIRMWARE_BUILD_DATE "13 Nov 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.9.1" + #define FIRMWARE_VERSION "v1.10.0" #endif #define FIRMWARE_ROLE "repeater" diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index f0dec42b..8641caaf 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -26,11 +26,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "2 Oct 2025" + #define FIRMWARE_BUILD_DATE "13 Nov 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.9.1" + #define FIRMWARE_VERSION "v1.10.0" #endif #ifndef LORA_FREQ diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index cc14f596..00d9c698 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -33,11 +33,11 @@ #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "2 Oct 2025" + #define FIRMWARE_BUILD_DATE "13 Nov 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.9.1" + #define FIRMWARE_VERSION "v1.10.0" #endif #define FIRMWARE_ROLE "sensor"