From 0263b6632c2f0954e3f529070084e3d949173611 Mon Sep 17 00:00:00 2001 From: hank Date: Mon, 5 May 2025 23:03:14 -0700 Subject: [PATCH 001/103] Adding support for TBeam 1.1 --- .vscode/settings.json | 5 + src/helpers/TBeamBoard.h | 17 +-- variants/lilygo_tbeam/target.cpp | 185 +++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..4a67f19a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.tpp": "cpp" + } +} \ No newline at end of file diff --git a/src/helpers/TBeamBoard.h b/src/helpers/TBeamBoard.h index fc52e712..8eba6933 100644 --- a/src/helpers/TBeamBoard.h +++ b/src/helpers/TBeamBoard.h @@ -7,6 +7,9 @@ // Defined using AXP2102 #define XPOWERS_CHIP_AXP2101 +#define PIN_BOARD_SDA1 42 //SDA for PMU and PFC8563 (RTC) +#define PIN_BOARD_SCL1 41 //SCL for PMU and PFC8563 (RTC) +#define PIN_PMU_IRQ 40 //IRQ pin for PMU // LoRa radio module pins for TBeam #define P_LORA_DIO_0 26 @@ -28,15 +31,13 @@ #include class TBeamBoard : public ESP32Board { - XPowersAXP2101 power; - + XPowersLibInterface *PMU = NULL; public: + bool power_init(); + void printPMU(); + void begin() { ESP32Board::begin(); - - power.setALDO2Voltage(3300); - power.enableALDO2(); - pinMode(38, INPUT_PULLUP); esp_reset_reason_t reason = esp_reset_reason(); @@ -49,6 +50,7 @@ public: rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); } + power_init(); } void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { @@ -75,7 +77,8 @@ public: } uint16_t getBattMilliVolts() override { - return power.getBattVoltage(); + if(PMU) return PMU->getBattVoltage(); + else return 0; } const char* getManufacturerName() const override { diff --git a/variants/lilygo_tbeam/target.cpp b/variants/lilygo_tbeam/target.cpp index 24e4a342..71c12894 100644 --- a/variants/lilygo_tbeam/target.cpp +++ b/variants/lilygo_tbeam/target.cpp @@ -3,6 +3,31 @@ TBeamBoard board; +// Using PMU AXP2102 +#define PMU_WIRE_PORT Wire + +bool pmuIntFlag = false; + +void TBeamBoard::printPMU() +{ + Serial.print("isCharging:"); Serial.println(PMU->isCharging() ? "YES" : "NO"); + Serial.print("isDischarge:"); Serial.println(PMU->isDischarge() ? "YES" : "NO"); + Serial.print("isVbusIn:"); Serial.println(PMU->isVbusIn() ? "YES" : "NO"); + Serial.print("getBattVoltage:"); Serial.print(PMU->getBattVoltage()); Serial.println("mV"); + Serial.print("getVbusVoltage:"); Serial.print(PMU->getVbusVoltage()); Serial.println("mV"); + Serial.print("getSystemVoltage:"); Serial.print(PMU->getSystemVoltage()); Serial.println("mV"); + + // The battery percentage may be inaccurate at first use, the PMU will automatically + // learn the battery curve and will automatically calibrate the battery percentage + // after a charge and discharge cycle + if (PMU->isBatteryConnect()) { + Serial.print("getBatteryPercent:"); Serial.print(PMU->getBatteryPercent()); Serial.println("%"); + } + + Serial.println(); +} + + #if defined(P_LORA_SCLK) static SPIClass spi; RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); @@ -15,10 +40,168 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); +static void setPMUIntFlag(){ + pmuIntFlag = true; +} + #ifndef LORA_CR #define LORA_CR 5 #endif +bool TBeamBoard::power_init() +{ + if (!PMU) + { + PMU = new XPowersAXP2101(PMU_WIRE_PORT); + if (!PMU->init()) + { + Serial.println("Warning: Failed to find AXP2101 power management"); + delete PMU; + PMU = NULL; + } + else + { + Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU"); + } + } + if (!PMU) + { + PMU = new XPowersAXP192(PMU_WIRE_PORT); + if (!PMU->init()) + { + Serial.println("Warning: Failed to find AXP192 power management"); + delete PMU; + PMU = NULL; + } + else + { + Serial.println("AXP192 PMU init succeeded, using AXP192 PMU"); + } + } + + if (!PMU) + { + return false; + } + + Serial.printf("PMU ID:0x%x\n", PMU->getChipID()); + printPMU(); + if (PMU->getChipModel() == XPOWERS_AXP192) + { + + // lora radio power channel + PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300); + PMU->enablePowerOutput(XPOWERS_LDO2); + + // oled module power channel, + // disable it will cause abnormal communication between boot and AXP power supply, + // do not turn it off + PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); + // enable oled power + PMU->enablePowerOutput(XPOWERS_DCDC1); + + // gnss module power channel + PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300); + // power->enablePowerOutput(XPOWERS_LDO3); + + // protected oled power source + PMU->setProtectedChannel(XPOWERS_DCDC1); + // protected esp32 power source + PMU->setProtectedChannel(XPOWERS_DCDC3); + + // disable not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); + + // disable all axp chip interrupt + PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ); + + // + /* Set the constant current charging current of AXP192 + opt: + XPOWERS_AXP192_CHG_CUR_100MA, + XPOWERS_AXP192_CHG_CUR_190MA, + XPOWERS_AXP192_CHG_CUR_280MA, + XPOWERS_AXP192_CHG_CUR_360MA, + XPOWERS_AXP192_CHG_CUR_450MA, + XPOWERS_AXP192_CHG_CUR_550MA, + XPOWERS_AXP192_CHG_CUR_630MA, + XPOWERS_AXP192_CHG_CUR_700MA, + XPOWERS_AXP192_CHG_CUR_780MA, + XPOWERS_AXP192_CHG_CUR_880MA, + XPOWERS_AXP192_CHG_CUR_960MA, + XPOWERS_AXP192_CHG_CUR_1000MA, + XPOWERS_AXP192_CHG_CUR_1080MA, + XPOWERS_AXP192_CHG_CUR_1160MA, + XPOWERS_AXP192_CHG_CUR_1240MA, + XPOWERS_AXP192_CHG_CUR_1320MA, + */ + PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_550MA); + } + else if (PMU->getChipModel() == XPOWERS_AXP2101) + { + // gnss module power channel + PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO4); + + // lora radio power channel + PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO3); + + // m.2 interface + PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC3); + + // power->setPowerChannelVoltage(XPOWERS_DCDC4, 3300); + // power->enablePowerOutput(XPOWERS_DCDC4); + + // not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited + PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited + PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist + PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist + PMU->disablePowerOutput(XPOWERS_VBACKUP); + + // disable all axp chip interrupt + PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + + /* Set the constant current charging current of AXP2101 + opt: + XPOWERS_AXP2101_CHG_CUR_100MA, + XPOWERS_AXP2101_CHG_CUR_125MA, + XPOWERS_AXP2101_CHG_CUR_150MA, + XPOWERS_AXP2101_CHG_CUR_175MA, + XPOWERS_AXP2101_CHG_CUR_200MA, + XPOWERS_AXP2101_CHG_CUR_300MA, + XPOWERS_AXP2101_CHG_CUR_400MA, + XPOWERS_AXP2101_CHG_CUR_500MA, + XPOWERS_AXP2101_CHG_CUR_600MA, + XPOWERS_AXP2101_CHG_CUR_700MA, + XPOWERS_AXP2101_CHG_CUR_800MA, + XPOWERS_AXP2101_CHG_CUR_900MA, + XPOWERS_AXP2101_CHG_CUR_1000MA, + */ + PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); + + // Set up PMU interrupts + Serial.println("Setting up PMU interrupts"); + pinMode(PIN_PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING); + + // Reset and re-enable PMU interrupts + Serial.println("Re-enable interrupts"); + PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + PMU->clearIrqStatus(); + PMU->enableIRQ( + XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts + XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts + XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts + ); + } + + return true; +} + bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); @@ -33,7 +216,9 @@ bool radio_init() { return false; // fail } + radio.setCRC(1); + return true; // success } From 5e7c9a229f44b5d73b45d33740535984e3173f87 Mon Sep 17 00:00:00 2001 From: hank Date: Mon, 5 May 2025 23:58:21 -0700 Subject: [PATCH 002/103] Cleaning up power code for the TBeam --- variants/lilygo_tbeam/target.cpp | 68 +++++--------------------------- 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/variants/lilygo_tbeam/target.cpp b/variants/lilygo_tbeam/target.cpp index 71c12894..d5889533 100644 --- a/variants/lilygo_tbeam/target.cpp +++ b/variants/lilygo_tbeam/target.cpp @@ -55,13 +55,13 @@ bool TBeamBoard::power_init() PMU = new XPowersAXP2101(PMU_WIRE_PORT); if (!PMU->init()) { - Serial.println("Warning: Failed to find AXP2101 power management"); + // Serial.println("Warning: Failed to find AXP2101 power management"); delete PMU; PMU = NULL; } else { - Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU"); + // Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU"); } } if (!PMU) @@ -69,72 +69,46 @@ bool TBeamBoard::power_init() PMU = new XPowersAXP192(PMU_WIRE_PORT); if (!PMU->init()) { - Serial.println("Warning: Failed to find AXP192 power management"); + // Serial.println("Warning: Failed to find AXP192 power management"); delete PMU; PMU = NULL; } else { - Serial.println("AXP192 PMU init succeeded, using AXP192 PMU"); + // Serial.println("AXP192 PMU init succeeded, using AXP192 PMU"); } } if (!PMU) { + Serial.println("PMU init failed."); return false; } - Serial.printf("PMU ID:0x%x\n", PMU->getChipID()); - printPMU(); + // Serial.printf("PMU ID:0x%x\n", PMU->getChipID()); + // printPMU(); if (PMU->getChipModel() == XPOWERS_AXP192) { - // lora radio power channel PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300); PMU->enablePowerOutput(XPOWERS_LDO2); - // oled module power channel, // disable it will cause abnormal communication between boot and AXP power supply, // do not turn it off PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); // enable oled power PMU->enablePowerOutput(XPOWERS_DCDC1); - // gnss module power channel PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300); // power->enablePowerOutput(XPOWERS_LDO3); - // protected oled power source PMU->setProtectedChannel(XPOWERS_DCDC1); // protected esp32 power source PMU->setProtectedChannel(XPOWERS_DCDC3); - // disable not use channel PMU->disablePowerOutput(XPOWERS_DCDC2); - // disable all axp chip interrupt PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ); - - // - /* Set the constant current charging current of AXP192 - opt: - XPOWERS_AXP192_CHG_CUR_100MA, - XPOWERS_AXP192_CHG_CUR_190MA, - XPOWERS_AXP192_CHG_CUR_280MA, - XPOWERS_AXP192_CHG_CUR_360MA, - XPOWERS_AXP192_CHG_CUR_450MA, - XPOWERS_AXP192_CHG_CUR_550MA, - XPOWERS_AXP192_CHG_CUR_630MA, - XPOWERS_AXP192_CHG_CUR_700MA, - XPOWERS_AXP192_CHG_CUR_780MA, - XPOWERS_AXP192_CHG_CUR_880MA, - XPOWERS_AXP192_CHG_CUR_960MA, - XPOWERS_AXP192_CHG_CUR_1000MA, - XPOWERS_AXP192_CHG_CUR_1080MA, - XPOWERS_AXP192_CHG_CUR_1160MA, - XPOWERS_AXP192_CHG_CUR_1240MA, - XPOWERS_AXP192_CHG_CUR_1320MA, - */ PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_550MA); } else if (PMU->getChipModel() == XPOWERS_AXP2101) @@ -142,53 +116,31 @@ bool TBeamBoard::power_init() // gnss module power channel PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); PMU->enablePowerOutput(XPOWERS_ALDO4); - // lora radio power channel PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); PMU->enablePowerOutput(XPOWERS_ALDO3); - // m.2 interface PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); PMU->enablePowerOutput(XPOWERS_DCDC3); - // power->setPowerChannelVoltage(XPOWERS_DCDC4, 3300); // power->enablePowerOutput(XPOWERS_DCDC4); - // not use channel PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist PMU->disablePowerOutput(XPOWERS_VBACKUP); - // disable all axp chip interrupt PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); - - /* Set the constant current charging current of AXP2101 - opt: - XPOWERS_AXP2101_CHG_CUR_100MA, - XPOWERS_AXP2101_CHG_CUR_125MA, - XPOWERS_AXP2101_CHG_CUR_150MA, - XPOWERS_AXP2101_CHG_CUR_175MA, - XPOWERS_AXP2101_CHG_CUR_200MA, - XPOWERS_AXP2101_CHG_CUR_300MA, - XPOWERS_AXP2101_CHG_CUR_400MA, - XPOWERS_AXP2101_CHG_CUR_500MA, - XPOWERS_AXP2101_CHG_CUR_600MA, - XPOWERS_AXP2101_CHG_CUR_700MA, - XPOWERS_AXP2101_CHG_CUR_800MA, - XPOWERS_AXP2101_CHG_CUR_900MA, - XPOWERS_AXP2101_CHG_CUR_1000MA, - */ - PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); + PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); // Set up PMU interrupts - Serial.println("Setting up PMU interrupts"); + // Serial.println("Setting up PMU interrupts"); pinMode(PIN_PMU_IRQ, INPUT_PULLUP); attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING); // Reset and re-enable PMU interrupts - Serial.println("Re-enable interrupts"); + // Serial.println("Re-enable interrupts"); PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); PMU->clearIrqStatus(); PMU->enableIRQ( From 3a8dfc8fe928f1c37a7f14174ff70fe8cbf890f5 Mon Sep 17 00:00:00 2001 From: hank Date: Thu, 8 May 2025 01:10:56 -0700 Subject: [PATCH 003/103] Delete .vscode/settings.json Removing vscode file incorrectly committed --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 4a67f19a..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "*.tpp": "cpp" - } -} \ No newline at end of file From 73d066375de6df325b98f3862587eed8e9943e93 Mon Sep 17 00:00:00 2001 From: hank Date: Mon, 12 May 2025 00:56:30 -0700 Subject: [PATCH 004/103] Fixes to the PMU calls --- src/helpers/TBeamS3SupremeBoard.h | 23 +-- .../lilygo_tbeam_supreme_SX1262/target.cpp | 146 ++++++++++-------- 2 files changed, 93 insertions(+), 76 deletions(-) diff --git a/src/helpers/TBeamS3SupremeBoard.h b/src/helpers/TBeamS3SupremeBoard.h index 2b8232d8..200756a2 100644 --- a/src/helpers/TBeamS3SupremeBoard.h +++ b/src/helpers/TBeamS3SupremeBoard.h @@ -47,15 +47,17 @@ #define I2C_RTC_ADD 0x51 //RTC I2C address on Wire1 #define I2C_PMU_ADD 0x34 //AXP2101 I2C address on Wire1 - +#define PMU_WIRE_PORT Wire1 +#define XPOWERS_CHIP_AXP2101 class TBeamS3SupremeBoard : public ESP32Board { - + XPowersAXP2101 PMU; public: +#ifdef MESH_DEBUG + void printPMU(); +#endif + bool power_init(); void begin() { - - bool power_init(); - ESP32Board::begin(); esp_reset_reason_t reason = esp_reset_reason(); @@ -68,6 +70,7 @@ public: rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); } + power_init(); } void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { @@ -94,12 +97,14 @@ public: } uint16_t getBattMilliVolts() override { - - return 0; + return PMU.getBattVoltage(); } - uint16_t getBattPercent(); - + uint16_t getBattPercent() { + //Read the PMU fuel guage for battery % + uint16_t battPercent = PMU.getBatteryPercent(); + return battPercent; + } const char* getManufacturerName() const override { return "LilyGo T-Beam S3 Supreme SX1262"; } diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index bbdd604e..fe767729 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -3,9 +3,6 @@ TBeamS3SupremeBoard board; -// Using PMU AXP2102 -XPowersAXP2101 PMU; - bool pmuIntFlag; #ifndef LORA_CR @@ -28,103 +25,125 @@ SensorManager sensors; static void setPMUIntFlag(){ pmuIntFlag = true; } +#ifdef MESH_DEBUG +void TBeamS3SupremeBoard::printPMU() +{ + Serial.print("isCharging:"); Serial.println(PMU.isCharging() ? "YES" : "NO"); + Serial.print("isDischarge:"); Serial.println(PMU.isDischarge() ? "YES" : "NO"); + Serial.print("isVbusIn:"); Serial.println(PMU.isVbusIn() ? "YES" : "NO"); + Serial.print("getBattVoltage:"); Serial.print(PMU.getBattVoltage()); Serial.println("mV"); + Serial.print("getVbusVoltage:"); Serial.print(PMU.getVbusVoltage()); Serial.println("mV"); + Serial.print("getSystemVoltage:"); Serial.print(PMU.getSystemVoltage()); Serial.println("mV"); -bool power_init() { - //Start up Wire1 with PMU address - //Serial.println("Starting Wire1 for PMU"); - //Wire1.begin(I2C_PMU_ADD); - //Wire1.begin(PIN_BOARD_SDA1,PIN_BOARD_SCL1); - - //Set LED to indicate charge state - Serial.println("Setting charge led"); - PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); - - //Set up PMU interrupts - Serial.println("Setting up PMU interrupts"); - pinMode(PIN_PMU_IRQ,INPUT_PULLUP); - attachInterrupt(PIN_PMU_IRQ,setPMUIntFlag,FALLING); + // The battery percentage may be inaccurate at first use, the PMU will automatically + // learn the battery curve and will automatically calibrate the battery percentage + // after a charge and discharge cycle + if (PMU.isBatteryConnect()) { + Serial.print("getBatteryPercent:"); Serial.print(PMU.getBatteryPercent()); Serial.println("%"); + } - //GPS - Serial.println("Setting and enabling a-ldo4 for GPS"); - PMU.setALDO4Voltage(3300); - PMU.enableALDO4(); //disable to save power - - //Lora - Serial.println("Setting and enabling a-ldo3 for LoRa"); - PMU.setALDO3Voltage(3300); - PMU.enableALDO3(); + Serial.println(); +} +#endif - //To avoid SPI bus issues during power up, reset OLED, sensor, and SD card supplies - Serial.println("Reset a-ldo1&2 and b-ldo1"); - if(ESP_SLEEP_WAKEUP_UNDEFINED == esp_sleep_get_wakeup_cause()){ +bool TBeamS3SupremeBoard::power_init() +{ + bool result = PMU.begin(PMU_WIRE_PORT, I2C_PMU_ADD, PIN_BOARD_SDA1, PIN_BOARD_SCL1); + if (result == false) { + MESH_DEBUG_PRINTLN("power is not online..."); while (1)delay(50); + } + MESH_DEBUG_PRINTLN("Setting charge led"); + PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + + // Set up PMU interrupts + MESH_DEBUG_PRINTLN("Setting up PMU interrupts"); + pinMode(PIN_PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING); + + // GPS + MESH_DEBUG_PRINTLN("Setting and enabling a-ldo4 for GPS"); + PMU.setALDO4Voltage(3300); + PMU.enableALDO4(); // disable to save power + + // Lora + MESH_DEBUG_PRINTLN("Setting and enabling a-ldo3 for LoRa"); + PMU.setALDO3Voltage(3300); + PMU.enableALDO3(); + + // To avoid SPI bus issues during power up, reset OLED, sensor, and SD card supplies + MESH_DEBUG_PRINTLN("Reset a-ldo1&2 and b-ldo1"); + if (ESP_SLEEP_WAKEUP_UNDEFINED == esp_sleep_get_wakeup_cause()) + { PMU.enableALDO1(); PMU.enableALDO2(); PMU.enableBLDO1(); delay(250); } - - //BME280 and OLED - Serial.println("Setting and enabling a-ldo1 for oled"); + + // BME280 and OLED + MESH_DEBUG_PRINTLN("Setting and enabling a-ldo1 for oled"); PMU.setALDO1Voltage(3300); PMU.enableALDO1(); - //QMC6310U - Serial.println("Setting and enabling a-ldo2 for QMC"); + // QMC6310U + MESH_DEBUG_PRINTLN("Setting and enabling a-ldo2 for QMC"); PMU.setALDO2Voltage(3300); - PMU.enableALDO2(); //disable to save power + PMU.enableALDO2(); // disable to save power - //SD card - Serial.println("Setting and enabling b-ldo2 for SD card"); + // SD card + MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for SD card"); PMU.setBLDO1Voltage(3300); PMU.enableBLDO1(); - //Out to header pins - Serial.println("Setting and enabling b-ldo2 for output to header"); + // Out to header pins + MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header"); PMU.setBLDO2Voltage(3300); PMU.enableBLDO2(); - Serial.println("Setting and enabling dcdc4 for output to header"); - PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); //1.8V + MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header"); + PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V PMU.enableDC4(); - Serial.println("Setting and enabling dcdc5 for output to header"); + MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header"); PMU.setDC5Voltage(3300); PMU.enableDC5(); - //Other power rails - Serial.println("Setting and enabling dcdc3 for ?"); - PMU.setDC3Voltage(3300); //doesn't go anywhere in the schematic?? + // Other power rails + MESH_DEBUG_PRINTLN("Setting and enabling dcdc3 for ?"); + PMU.setDC3Voltage(3300); // doesn't go anywhere in the schematic?? PMU.enableDC3(); - //Unused power rails - Serial.println("Disabling unused supplies dcdc2, dldo1 and dldo2"); + // Unused power rails + MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dldo1 and dldo2"); PMU.disableDC2(); PMU.disableDLDO1(); - PMU.disableDLDO2(); + PMU.disableDLDO2(); - //Set charge current to 300mA - Serial.println("Setting battery charge current limit and voltage"); + // Set charge current to 300mA + MESH_DEBUG_PRINTLN("Setting battery charge current limit and voltage"); PMU.setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_300MA); PMU.setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2); - //enable battery voltage measurement - Serial.println("Enabling battery measurement"); + // enable battery voltage measurement + MESH_DEBUG_PRINTLN("Enabling battery measurement"); PMU.enableBattVoltageMeasure(); - //Reset and re-enable PMU interrupts - Serial.println("Re-enable interrupts"); + // Reset and re-enable PMU interrupts + MESH_DEBUG_PRINTLN("Re-enable interrupts"); PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ); PMU.clearIrqStatus(); PMU.enableIRQ( - XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | //Battery interrupts - XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | //VBUS interrupts - XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | //Power Key interrupts - XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ //Charging interrupts + XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts + XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts + XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts ); +#ifdef MESH_DEBUG + printPMU(); +#endif - //Set the power key off press time + // Set the power key off press time PMU.setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S); - return true; } @@ -154,13 +173,6 @@ bool radio_init() { return true; // success } -uint16_t getBattPercent() { - //Read the PMU fuel guage for battery % - uint16_t battPercent = PMU.getBatteryPercent(); - - return battPercent; -} - uint32_t radio_get_rng_seed() { return radio.random(0x7FFFFFFF); } From fd3781002251aa1ef9cd99a4f0883aa160059474 Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Mon, 19 May 2025 12:55:56 -0700 Subject: [PATCH 005/103] t-beam supreme: display fix, BME add, user btn fix -Fixed build issues after display refactor -Added BME280 support and updated SensorManager to include this data -Fixed user button and verified it turns the display on --- .gitignore | 1 + src/helpers/SensorManager.h | 4 +- src/helpers/TBeamS3SupremeBoard.h | 4 +- .../platformio.ini | 7 +- .../lilygo_tbeam_supreme_SX1262/target.cpp | 78 +++++++++++++++++-- variants/lilygo_tbeam_supreme_SX1262/target.h | 5 ++ 6 files changed, 89 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 51449c2d..7ca9335a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ out/ .direnv/ .DS_Store .vscode/settings.json +.vscode/extensions.json diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 0e4bc27d..f48c2a7b 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -10,10 +10,10 @@ class SensorManager { public: - double node_lat, node_lon; // modify these, if you want to affect Advert location + double node_lat, node_lon, node_temp, node_hum, node_pres; // modify these, if you want to affect Advert location double node_altitude; // altitude in meters - SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } + SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; node_temp = 0; node_hum = 0; node_pres = 0;} virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/TBeamS3SupremeBoard.h b/src/helpers/TBeamS3SupremeBoard.h index 74d9ca2e..9bc9a83d 100644 --- a/src/helpers/TBeamS3SupremeBoard.h +++ b/src/helpers/TBeamS3SupremeBoard.h @@ -22,7 +22,7 @@ #define PIN_BOARD_SCL1 41 //SCL for PMU and PFC8563 (RTC) #define PIN_PMU_IRQ 40 //IRQ pin for PMU -#define PIN_USER_BTN 0 +//#define PIN_USER_BTN 0 #define P_BOARD_SPI_MOSI 35 //SPI for SD Card and QMI8653 (IMU) #define P_BOARD_SPI_MISO 37 //SPI for SD Card and QMI8653 (IMU) @@ -55,7 +55,9 @@ class TBeamS3SupremeBoard : public ESP32Board { XPowersAXP2101 PMU; public: #ifdef MESH_DEBUG + void scanDevices(TwoWire *w); void printPMU(); + void printBMEValues(); #endif bool power_init(); diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index d3447673..5ab9fedc 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -8,18 +8,21 @@ build_flags = -D P_LORA_TX_LED=6 -D PIN_BOARD_SDA=17 -D PIN_BOARD_SCL=18 + -D PIN_USER_BTN=0 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper - ;-D DISPLAY_CLASS=SSD1306Display ;Needs to be modified for SH1106 + -D DISPLAY_CLASS=SH1106Display -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${esp32_base.build_src_filter} +<../variants/lilygo_tbeam_supreme_SX1262> + + board_build.partitions = min_spiffs.csv ; get around 4mb flash limit lib_deps = ${esp32_base.lib_deps} lewisxhe/XPowersLib @ ^0.2.7 - ;adafruit/Adafruit SSD1306 @ ^2.5.13 + adafruit/Adafruit SH110X @ ^2.1.13 stevemarple/MicroNMEA @ ^2.0.6 + adafruit/Adafruit BME280 Library @ ^2.3.0 ; === LILYGO T-Beam S3 Supreme with SX1262 environments === [env:T_Beam_S3_Supreme_SX1262_repeater] diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index f5b7080b..97fd8c76 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -1,10 +1,16 @@ #include #include "target.h" #include +#include TBeamS3SupremeBoard board; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + bool pmuIntFlag; +//#define SEALEVELPRESSURE_HPA (1013.25) #ifndef LORA_CR #define LORA_CR 5 @@ -23,6 +29,7 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); TbeamSupSensorManager sensors = TbeamSupSensorManager(nmea); +Adafruit_BME280 bme; static void setPMUIntFlag(){ pmuIntFlag = true; @@ -46,7 +53,7 @@ void scanDevices(TwoWire *w) switch (addr) { case 0x77: case 0x76: - Serial.println("\tFound BMX280 Sensor"); + Serial.println("\tFound BME280 Sensor"); deviceOnline |= BME280_ONLINE; break; case 0x34: @@ -107,6 +114,26 @@ void TBeamS3SupremeBoard::printPMU() Serial.println(); } +void printBMEValues() { + Serial.print("Temperature = "); + Serial.print(bme.readTemperature()); + Serial.println(" *C"); + + Serial.print("Pressure = "); + + Serial.print(bme.readPressure() / 100.0F); + Serial.println(" hPa"); + + Serial.print("Approx. Altitude = "); + Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(" m"); + + Serial.print("Humidity = "); + Serial.print(bme.readHumidity()); + Serial.println(" %"); + + Serial.println(); +} #endif bool TBeamS3SupremeBoard::power_init() @@ -289,6 +316,10 @@ bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire1); + + // #ifdef MESH_DEBUG + // printBMEValues(); + // #endif #ifdef SX126X_DIO3_TCXO_VOLTAGE float tcxo = SX126X_DIO3_TCXO_VOLTAGE; @@ -340,8 +371,14 @@ void TbeamSupSensorManager::sleep_gps() { } bool TbeamSupSensorManager::begin() { + //init BME280 + if (! bme.begin(0x77, &Wire)) { + MESH_DEBUG_PRINTLN("Could not find a valid BME280 sensor, check wiring!"); + } + else + MESH_DEBUG_PRINTLN("BME280 found and init!"); + // init GPS port - Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, P_GPS_RX, P_GPS_TX); bool result = false; @@ -359,22 +396,53 @@ bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneL if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { // does requester have permission? + telemetry.addTemperature(TELEM_CHANNEL_SELF, node_temp); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, node_hum); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, node_pres); + //telemetry.addAltitude(TELEM_CHANNEL_SELF, node_alt); + } return true; } void TbeamSupSensorManager::loop() { - static long next_gps_update = 0; + static long next_update = 0; _nmea->loop(); - if (millis() > next_gps_update) { + if (millis() > next_update) { if (_nmea->isValid()) { node_lat = ((double)_nmea->getLatitude())/1000000.; node_lon = ((double)_nmea->getLongitude())/1000000.; node_altitude = ((double)_nmea->getAltitude()) / 1000.0; //Serial.printf("lat %f lon %f\r\n", _lat, _lon); } - next_gps_update = millis() + 1000; + + //read BME280 values + //node_alt = bme.readAltitude(SEALEVELPRESSURE_HPA); + node_temp = bme.readTemperature(); + node_hum = bme.readHumidity(); + node_pres = (bme.readPressure() / 100.0F); + + #ifdef MESH_DEBUG + Serial.print("Temperature = "); + Serial.print(node_temp); + Serial.println(" *C"); + + Serial.print("Humidity = "); + Serial.print(node_hum); + Serial.println(" %"); + + Serial.print("Pressure = "); + Serial.print(node_pres); + Serial.println(" hPa"); + + // Serial.print("Approx. Altitude = "); + // Serial.print(node_alt); + // Serial.println(" m"); + #endif + + next_update = millis() + 1000; } } diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index 107e2950..99ffa1c5 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -31,6 +31,11 @@ extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern TbeamSupSensorManager sensors; +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; +#endif + enum { POWERMANAGE_ONLINE = _BV(0), DISPLAY_ONLINE = _BV(1), From 4990fe40e7d6a86553cc0da0c03aaebd7b69062d Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Mon, 19 May 2025 13:15:01 -0700 Subject: [PATCH 006/103] t-beam supreme: current limit increase Added the current limit increase define --- variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 5ab9fedc..0316bef2 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -12,6 +12,7 @@ build_flags = -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D DISPLAY_CLASS=SH1106Display + -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${esp32_base.build_src_filter} +<../variants/lilygo_tbeam_supreme_SX1262> From 4f503de7431cfa446e0bb3fb9113389ba6e35880 Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Tue, 20 May 2025 11:37:41 -0700 Subject: [PATCH 007/103] t-beam supreme: fixes and cleanup Reverted the SensorManager changes Moved BME into TbeamSupSensorManager Moved printBMEValues into TbeamSupSensorManager Moved scanDevices out of TBeamS3SupremeBoard --- src/helpers/SensorManager.h | 4 ++-- src/helpers/TBeamS3SupremeBoard.h | 2 -- variants/lilygo_tbeam_supreme_SX1262/target.cpp | 5 +---- variants/lilygo_tbeam_supreme_SX1262/target.h | 17 ++++++++++++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index f48c2a7b..41c34e55 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -10,10 +10,10 @@ class SensorManager { public: - double node_lat, node_lon, node_temp, node_hum, node_pres; // modify these, if you want to affect Advert location + double node_lat, node_lon; // modify these, if you want to affect Advert location double node_altitude; // altitude in meters - SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; node_temp = 0; node_hum = 0; node_pres = 0;} + SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0;} virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/TBeamS3SupremeBoard.h b/src/helpers/TBeamS3SupremeBoard.h index 9bc9a83d..1ae6a230 100644 --- a/src/helpers/TBeamS3SupremeBoard.h +++ b/src/helpers/TBeamS3SupremeBoard.h @@ -55,9 +55,7 @@ class TBeamS3SupremeBoard : public ESP32Board { XPowersAXP2101 PMU; public: #ifdef MESH_DEBUG - void scanDevices(TwoWire *w); void printPMU(); - void printBMEValues(); #endif bool power_init(); diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 97fd8c76..4dbfd625 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -1,7 +1,6 @@ #include #include "target.h" #include -#include TBeamS3SupremeBoard board; @@ -10,7 +9,6 @@ TBeamS3SupremeBoard board; #endif bool pmuIntFlag; -//#define SEALEVELPRESSURE_HPA (1013.25) #ifndef LORA_CR #define LORA_CR 5 @@ -29,7 +27,6 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); TbeamSupSensorManager sensors = TbeamSupSensorManager(nmea); -Adafruit_BME280 bme; static void setPMUIntFlag(){ pmuIntFlag = true; @@ -114,7 +111,7 @@ void TBeamS3SupremeBoard::printPMU() Serial.println(); } -void printBMEValues() { +void TbeamSupSensorManager::printBMEValues() { Serial.print("Temperature = "); Serial.print(bme.readTemperature()); Serial.println(" *C"); diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index 99ffa1c5..dbb24a8e 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -8,15 +8,20 @@ #include #include #include +#include class TbeamSupSensorManager: public SensorManager { bool gps_active = false; LocationProvider * _nmea; - + Adafruit_BME280 bme; + double node_temp, node_hum, node_pres; + + #define SEALEVELPRESSURE_HPA (1013.25) + void start_gps(); void sleep_gps(); public: - TbeamSupSensorManager(LocationProvider &nmea): _nmea(&nmea) { } + TbeamSupSensorManager(LocationProvider &nmea): _nmea(&nmea) {node_temp = 0; node_hum = 0; node_pres = 0;} bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; void loop() override; @@ -24,6 +29,11 @@ class TbeamSupSensorManager: public SensorManager { const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + + #ifdef MESH_DEBUG + void printBMEValues(); + #endif + }; extern TBeamS3SupremeBoard board; @@ -53,7 +63,8 @@ enum { OSC32768_ONLINE = _BV(13), }; -bool power_init(); +void scanDevices(TwoWire *w); +static bool l76kProbe(); bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); From 1d94df1d048d9f2476db41fa7a4a501150dd481d Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Thu, 22 May 2025 12:06:34 +0000 Subject: [PATCH 008/103] Update platformio.ini --- variants/heltec_v3/platformio.ini | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 0814ad4d..0b7ea006 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -19,9 +19,14 @@ build_flags = -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${esp32_base.build_src_filter} +<../variants/heltec_v3> + + 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 [env:Heltec_v3_repeater] extends = Heltec_lora32_v3 @@ -38,9 +43,14 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> + + lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + 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 [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 @@ -57,9 +67,14 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_room_server> + + lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + 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 [env:Heltec_v3_terminal_chat] extends = Heltec_lora32_v3 @@ -71,9 +86,14 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_secure_chat/main.cpp> + + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_usb] extends = Heltec_lora32_v3 @@ -89,9 +109,14 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/companion_radio> + + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_ble] extends = Heltec_lora32_v3 @@ -111,9 +136,14 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> + + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 @@ -133,9 +163,14 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> + + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 From 648953ce8d5ed8d3e63c51f5376592df38d771be Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Thu, 22 May 2025 12:07:06 +0000 Subject: [PATCH 009/103] Update target.cpp --- variants/heltec_v3/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_v3/target.cpp b/variants/heltec_v3/target.cpp index ab9b709f..be99a1e3 100644 --- a/variants/heltec_v3/target.cpp +++ b/variants/heltec_v3/target.cpp @@ -14,7 +14,7 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; From 0caa2b4cd1b9ed45262539ee9e95c9cec76a0bc3 Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Thu, 22 May 2025 12:07:45 +0000 Subject: [PATCH 010/103] Update target.h --- variants/heltec_v3/target.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index 76ad58a7..701f9cd5 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef DISPLAY_CLASS #include #endif @@ -14,7 +15,7 @@ extern HeltecV3Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; From cd7fc59f067e30ce34d3aeb381b16c09066cfa9c Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Thu, 22 May 2025 13:24:26 +0000 Subject: [PATCH 011/103] Update platformio.ini --- variants/heltec_v3/platformio.ini | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 0b7ea006..9f4c23c1 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -47,10 +47,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} - 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 [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 @@ -71,10 +67,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} - 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 [env:Heltec_v3_terminal_chat] extends = Heltec_lora32_v3 @@ -90,10 +82,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_usb] extends = Heltec_lora32_v3 @@ -113,10 +101,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_ble] extends = Heltec_lora32_v3 @@ -140,10 +124,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 @@ -167,10 +147,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.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 [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 From e1351effb18cd3d3554027e175b144e23b10c6bb Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Thu, 22 May 2025 15:50:54 +0000 Subject: [PATCH 012/103] Update platformio.ini --- variants/heltec_v3/platformio.ini | 6 ------ 1 file changed, 6 deletions(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 9f4c23c1..a5687987 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -43,7 +43,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> - + lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} @@ -63,7 +62,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_room_server> - + lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} @@ -78,7 +76,6 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_secure_chat/main.cpp> - + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -97,7 +94,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/companion_radio> - + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -120,7 +116,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> - + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -143,7 +138,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> - + lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 From e742d1f7228a4e155b2b98178744c39cf8dd884c Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Thu, 22 May 2025 16:50:06 -0700 Subject: [PATCH 013/103] t-beam supreme: minor GPS and BME fixes Fixed GPS initial state to default to off after init. Removed redundant current limit define --- .../platformio.ini | 1 - .../lilygo_tbeam_supreme_SX1262/target.cpp | 54 +++++++++++-------- variants/lilygo_tbeam_supreme_SX1262/target.h | 2 + 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 4367dc15..8e46f22c 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -12,7 +12,6 @@ build_flags = -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D DISPLAY_CLASS=SH1106Display - -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_CURRENT_LIMIT=140 build_src_filter = ${esp32_base.build_src_filter} diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 4dbfd625..dfe453ee 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -370,23 +370,28 @@ void TbeamSupSensorManager::sleep_gps() { bool TbeamSupSensorManager::begin() { //init BME280 if (! bme.begin(0x77, &Wire)) { - MESH_DEBUG_PRINTLN("Could not find a valid BME280 sensor, check wiring!"); + MESH_DEBUG_PRINTLN("Could not find a valid BME280 sensor"); + bme_active = false; } else MESH_DEBUG_PRINTLN("BME280 found and init!"); + bme_active = true; // init GPS port Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, P_GPS_RX, P_GPS_TX); - bool result = false; + bool gps_alive = false; for ( int i = 0; i < 3; ++i) { - result = l76kProbe(); - if (result) { - gps_active = true; - return result; + gps_alive = l76kProbe(); + if (gps_alive) { + MESH_DEBUG_PRINTLN("GPS is init and active. Shutting down for initial state."); + sleep_gps(); + return gps_alive; } } - return result; + gps_active = gps_alive; + MESH_DEBUG_PRINTLN("GPS init failed and GPS is not active"); + return gps_alive; } bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { @@ -422,17 +427,17 @@ void TbeamSupSensorManager::loop() { node_pres = (bme.readPressure() / 100.0F); #ifdef MESH_DEBUG - Serial.print("Temperature = "); - Serial.print(node_temp); - Serial.println(" *C"); + // Serial.print("Temperature = "); + // Serial.print(node_temp); + // Serial.println(" *C"); - Serial.print("Humidity = "); - Serial.print(node_hum); - Serial.println(" %"); + // Serial.print("Humidity = "); + // Serial.print(node_hum); + // Serial.println(" %"); - Serial.print("Pressure = "); - Serial.print(node_pres); - Serial.println(" hPa"); + // Serial.print("Pressure = "); + // Serial.print(node_pres); + // Serial.println(" hPa"); // Serial.print("Approx. Altitude = "); // Serial.print(node_alt); @@ -443,17 +448,24 @@ void TbeamSupSensorManager::loop() { } } -int TbeamSupSensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) +int TbeamSupSensorManager::getNumSettings() const { + return sensorNum; +} const char* TbeamSupSensorManager::getSettingName(int i) const { - return i == 0 ? "gps" : NULL; + switch(i){ + case 0: return "gps"; + case 1: return "bme280"; + default: NULL; + } } const char* TbeamSupSensorManager::getSettingValue(int i) const { - if (i == 0) { - return gps_active ? "1" : "0"; + switch(i){ + case 0: return gps_active == true ? "1" : "0"; + case 1: return bme_active == true ? "1" : "0"; + default: NULL; } - return NULL; } bool TbeamSupSensorManager::setSettingValue(const char* name, const char* value) { diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index dbb24a8e..6acf6cdf 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -12,9 +12,11 @@ class TbeamSupSensorManager: public SensorManager { bool gps_active = false; + bool bme_active = false; LocationProvider * _nmea; Adafruit_BME280 bme; double node_temp, node_hum, node_pres; + int sensorNum = 2; #define SEALEVELPRESSURE_HPA (1013.25) From 42284edcfe1e46de9501599ed5716ffd827aa4de Mon Sep 17 00:00:00 2001 From: Memo <58190287+memo-567@users.noreply.github.com> Date: Sat, 24 May 2025 10:39:05 +0000 Subject: [PATCH 014/103] Update platformio.ini --- variants/heltec_v3/platformio.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index a5687987..28c6d562 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -17,6 +17,10 @@ build_flags = -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_INA3221=1 + -D ENV_INCLUDE_INA219=1 build_src_filter = ${esp32_base.build_src_filter} +<../variants/heltec_v3> + From 2f8d9cf96ab06bc24c258d9f77b48465a5ca2613 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 24 May 2025 20:42:00 +1000 Subject: [PATCH 015/103] * refactor of RadioLibWrapper::isReceiving() --- src/helpers/CustomLLCC68Wrapper.h | 14 ++------------ src/helpers/CustomLR1110Wrapper.h | 14 ++------------ src/helpers/CustomSTM32WLxWrapper.h | 14 ++------------ src/helpers/CustomSX1262Wrapper.h | 15 ++------------- src/helpers/CustomSX1268Wrapper.h | 14 ++------------ src/helpers/CustomSX1276Wrapper.h | 14 ++------------ src/helpers/RadioLibWrappers.cpp | 12 ++++++++++++ src/helpers/RadioLibWrappers.h | 8 ++++++++ 8 files changed, 32 insertions(+), 73 deletions(-) diff --git a/src/helpers/CustomLLCC68Wrapper.h b/src/helpers/CustomLLCC68Wrapper.h index c7d95c41..d547880f 100644 --- a/src/helpers/CustomLLCC68Wrapper.h +++ b/src/helpers/CustomLLCC68Wrapper.h @@ -6,18 +6,8 @@ class CustomLLCC68Wrapper : public RadioLibWrapper { public: CustomLLCC68Wrapper(CustomLLCC68& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomLLCC68 *)_radio)->isReceiving()) return true; - - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomLLCC68 *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomLLCC68 *)_radio)->isReceiving(); } float getLastRSSI() const override { return ((CustomLLCC68 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomLLCC68 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomLR1110Wrapper.h b/src/helpers/CustomLR1110Wrapper.h index 3a96d3c2..c02052a1 100644 --- a/src/helpers/CustomLR1110Wrapper.h +++ b/src/helpers/CustomLR1110Wrapper.h @@ -6,18 +6,8 @@ class CustomLR1110Wrapper : public RadioLibWrapper { public: CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomLR1110 *)_radio)->isReceiving()) return true; - - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomLR1110 *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomLR1110 *)_radio)->isReceiving(); } void onSendFinished() override { diff --git a/src/helpers/CustomSTM32WLxWrapper.h b/src/helpers/CustomSTM32WLxWrapper.h index 84f78376..491d648e 100644 --- a/src/helpers/CustomSTM32WLxWrapper.h +++ b/src/helpers/CustomSTM32WLxWrapper.h @@ -7,18 +7,8 @@ class CustomSTM32WLxWrapper : public RadioLibWrapper { public: CustomSTM32WLxWrapper(CustomSTM32WLx& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomSTM32WLx *)_radio)->isReceiving()) return true; - - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomSTM32WLx *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomSTM32WLx *)_radio)->isReceiving(); } float getLastRSSI() const override { return ((CustomSTM32WLx *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSTM32WLx *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1262Wrapper.h b/src/helpers/CustomSX1262Wrapper.h index ea2da5fe..3aee2966 100644 --- a/src/helpers/CustomSX1262Wrapper.h +++ b/src/helpers/CustomSX1262Wrapper.h @@ -2,23 +2,12 @@ #include "CustomSX1262.h" #include "RadioLibWrappers.h" -#include class CustomSX1262Wrapper : public RadioLibWrapper { public: CustomSX1262Wrapper(CustomSX1262& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomSX1262 *)_radio)->isReceiving()) return true; - - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomSX1262 *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomSX1262 *)_radio)->isReceiving(); } float getLastRSSI() const override { return ((CustomSX1262 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1262 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1268Wrapper.h b/src/helpers/CustomSX1268Wrapper.h index f9eee447..5ee8e6a2 100644 --- a/src/helpers/CustomSX1268Wrapper.h +++ b/src/helpers/CustomSX1268Wrapper.h @@ -6,18 +6,8 @@ class CustomSX1268Wrapper : public RadioLibWrapper { public: CustomSX1268Wrapper(CustomSX1268& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomSX1268 *)_radio)->isReceiving()) return true; - - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomSX1268 *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomSX1268 *)_radio)->isReceiving(); } float getLastRSSI() const override { return ((CustomSX1268 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1268 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1276Wrapper.h b/src/helpers/CustomSX1276Wrapper.h index f9900705..26e925a9 100644 --- a/src/helpers/CustomSX1276Wrapper.h +++ b/src/helpers/CustomSX1276Wrapper.h @@ -6,18 +6,8 @@ class CustomSX1276Wrapper : public RadioLibWrapper { public: CustomSX1276Wrapper(CustomSX1276& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceiving() override { - if (((CustomSX1276 *)_radio)->isReceiving()) return true; - - idle(); // put into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = (((CustomSX1276 *)_radio)->tryScanChannel() == RADIOLIB_PREAMBLE_DETECTED); - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + bool isReceivingPacket() override { + return ((CustomSX1276 *)_radio)->isReceiving(); } float getLastRSSI() const override { return ((CustomSX1276 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1276 *)_radio)->getSNR(); } diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index 39fb340e..358c5f42 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -108,6 +108,18 @@ void RadioLibWrapper::onSendFinished() { state = STATE_IDLE; } +bool RadioLibWrapper::isChannelActive() { + idle(); // put sx126x into standby + // do some basic CAD (blocks for ~12780 micros (on SF 10)!) + bool activity = _radio->scanChannel() == RADIOLIB_LORA_DETECTED; + if (activity) { + startRecv(); + } else { + idle(); + } + return activity; +} + float RadioLibWrapper::getLastRSSI() const { return _radio->getRSSI(); } diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index bdbadb19..3e8b9fea 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -12,6 +12,7 @@ protected: void idle(); void startRecv(); float packetScoreInt(float snr, int sf, int packet_len); + virtual bool isReceivingPacket() =0; public: RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; } @@ -23,6 +24,13 @@ public: bool isSendComplete() override; void onSendFinished() override; bool isInRecvMode() const override; + bool isChannelActive(); + + bool isReceiving() override { + if (isReceivingPacket()) return true; + + return isChannelActive(); + } uint32_t getPacketsRecv() const { return n_recv; } uint32_t getPacketsSent() const { return n_sent; } From f2243b78ae8467e271491cbd9109592afa759714 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 24 May 2025 21:24:44 +1000 Subject: [PATCH 016/103] * added Radio::loop() virtual function * RadioLibWrapper: new isChannelActive() based on current RSSI being above noise_floor + THRESHOLD --- src/Dispatcher.cpp | 2 ++ src/Dispatcher.h | 5 ++++ src/helpers/CustomLLCC68Wrapper.h | 3 +++ src/helpers/CustomLR1110Wrapper.h | 5 ++++ src/helpers/CustomSTM32WLxWrapper.h | 3 +++ src/helpers/CustomSX1262Wrapper.h | 3 +++ src/helpers/CustomSX1268Wrapper.h | 3 +++ src/helpers/CustomSX1276Wrapper.h | 3 +++ src/helpers/RadioLibWrappers.cpp | 36 +++++++++++++++++++++-------- src/helpers/RadioLibWrappers.h | 6 +++++ 10 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 3d5b04fc..6412b6a9 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -36,6 +36,8 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { } void Dispatcher::loop() { + _radio->loop(); + // check for radio 'stuck' in mode other than Rx bool is_recv = _radio->isInRecvMode(); if (is_recv != prev_isrecv_mode) { diff --git a/src/Dispatcher.h b/src/Dispatcher.h index d03c9f73..7a48067d 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -56,6 +56,11 @@ public: */ virtual void onSendFinished() = 0; + /** + * \brief do any processing needed on each loop cycle + */ + virtual void loop() { } + virtual bool isInRecvMode() const = 0; /** diff --git a/src/helpers/CustomLLCC68Wrapper.h b/src/helpers/CustomLLCC68Wrapper.h index d547880f..f7dd7a9f 100644 --- a/src/helpers/CustomLLCC68Wrapper.h +++ b/src/helpers/CustomLLCC68Wrapper.h @@ -9,6 +9,9 @@ public: bool isReceivingPacket() override { return ((CustomLLCC68 *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + return ((CustomLLCC68 *)_radio)->getRSSI(false); + } float getLastRSSI() const override { return ((CustomLLCC68 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomLLCC68 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomLR1110Wrapper.h b/src/helpers/CustomLR1110Wrapper.h index c02052a1..7e2ffa2d 100644 --- a/src/helpers/CustomLR1110Wrapper.h +++ b/src/helpers/CustomLR1110Wrapper.h @@ -9,6 +9,11 @@ public: bool isReceivingPacket() override { return ((CustomLR1110 *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + float rssi = -110; + ((CustomLR1110 *)_radio)->getRssiInst(&rssi); + return rssi; + } void onSendFinished() override { RadioLibWrapper::onSendFinished(); diff --git a/src/helpers/CustomSTM32WLxWrapper.h b/src/helpers/CustomSTM32WLxWrapper.h index 491d648e..9e2d0441 100644 --- a/src/helpers/CustomSTM32WLxWrapper.h +++ b/src/helpers/CustomSTM32WLxWrapper.h @@ -10,6 +10,9 @@ public: bool isReceivingPacket() override { return ((CustomSTM32WLx *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + return ((CustomSTM32WLx *)_radio)->getRSSI(false); + } float getLastRSSI() const override { return ((CustomSTM32WLx *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSTM32WLx *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1262Wrapper.h b/src/helpers/CustomSX1262Wrapper.h index 3aee2966..119f6dce 100644 --- a/src/helpers/CustomSX1262Wrapper.h +++ b/src/helpers/CustomSX1262Wrapper.h @@ -9,6 +9,9 @@ public: bool isReceivingPacket() override { return ((CustomSX1262 *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + return ((CustomSX1262 *)_radio)->getRSSI(false); + } float getLastRSSI() const override { return ((CustomSX1262 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1262 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1268Wrapper.h b/src/helpers/CustomSX1268Wrapper.h index 5ee8e6a2..5d7106b4 100644 --- a/src/helpers/CustomSX1268Wrapper.h +++ b/src/helpers/CustomSX1268Wrapper.h @@ -9,6 +9,9 @@ public: bool isReceivingPacket() override { return ((CustomSX1268 *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + return ((CustomSX1268 *)_radio)->getRSSI(false); + } float getLastRSSI() const override { return ((CustomSX1268 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1268 *)_radio)->getSNR(); } diff --git a/src/helpers/CustomSX1276Wrapper.h b/src/helpers/CustomSX1276Wrapper.h index 26e925a9..28257990 100644 --- a/src/helpers/CustomSX1276Wrapper.h +++ b/src/helpers/CustomSX1276Wrapper.h @@ -9,6 +9,9 @@ public: bool isReceivingPacket() override { return ((CustomSX1276 *)_radio)->isReceiving(); } + float getCurrentRSSI() override { + return ((CustomSX1276 *)_radio)->getRSSI(false); + } float getLastRSSI() const override { return ((CustomSX1276 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1276 *)_radio)->getSNR(); } diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index 358c5f42..d52d81ff 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -8,6 +8,12 @@ #define STATE_TX_DONE 4 #define STATE_INT_READY 16 +#ifndef INTERFERENCE_THRESHOLD_DB + #define INTERFERENCE_THRESHOLD_DB 14 +#endif + +#define NUM_NOISE_FLOOR_SAMPLES 64 + static volatile uint8_t state = STATE_IDLE; // this function is called when a complete packet @@ -28,6 +34,12 @@ void RadioLibWrapper::begin() { if (_board->getStartupReason() == BD_STARTUP_RX_PACKET) { // received a LoRa packet (while in deep sleep) setFlag(); // LoRa packet is already received } + + _noise_floor = -140; + + // start average out some samples + _num_floor_samples = 0; + _floor_sample_sum = 0; } void RadioLibWrapper::idle() { @@ -35,6 +47,20 @@ void RadioLibWrapper::idle() { state = STATE_IDLE; // need another startReceive() } +void RadioLibWrapper::loop() { + if (state == STATE_RX && _num_floor_samples < NUM_NOISE_FLOOR_SAMPLES) { + if (!isReceivingPacket()) { + _num_floor_samples++; + _floor_sample_sum += getCurrentRSSI(); + } + } else if (_floor_sample_sum != 0) { + _noise_floor = _floor_sample_sum / NUM_NOISE_FLOOR_SAMPLES; + _floor_sample_sum = 0; + + MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor); + } +} + void RadioLibWrapper::startRecv() { int err = _radio->startReceive(); if (err == RADIOLIB_ERR_NONE) { @@ -109,15 +135,7 @@ void RadioLibWrapper::onSendFinished() { } bool RadioLibWrapper::isChannelActive() { - idle(); // put sx126x into standby - // do some basic CAD (blocks for ~12780 micros (on SF 10)!) - bool activity = _radio->scanChannel() == RADIOLIB_LORA_DETECTED; - if (activity) { - startRecv(); - } else { - idle(); - } - return activity; + return getCurrentRSSI() > _noise_floor + INTERFERENCE_THRESHOLD_DB; } float RadioLibWrapper::getLastRSSI() const { diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index 3e8b9fea..211e0187 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -8,11 +8,15 @@ protected: PhysicalLayer* _radio; mesh::MainBoard* _board; uint32_t n_recv, n_sent; + int16_t _noise_floor; + uint16_t _num_floor_samples; + int32_t _floor_sample_sum; void idle(); void startRecv(); float packetScoreInt(float snr, int sf, int packet_len); virtual bool isReceivingPacket() =0; + virtual float getCurrentRSSI() =0; public: RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; } @@ -32,6 +36,8 @@ public: return isChannelActive(); } + void loop() override; + uint32_t getPacketsRecv() const { return n_recv; } uint32_t getPacketsSent() const { return n_sent; } void resetStats() { n_recv = n_sent = 0; } From 0e35ae5ec63e6b16d633712df86d2b6821223758 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 25 May 2025 21:44:15 +1000 Subject: [PATCH 017/103] * dynamic noise floor sampling --- src/Dispatcher.cpp | 8 ++++++++ src/Dispatcher.h | 6 ++++++ src/helpers/RadioLibWrappers.cpp | 18 ++++++++++++++---- src/helpers/RadioLibWrappers.h | 6 +++++- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 6412b6a9..06c5e035 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -10,6 +10,10 @@ namespace mesh { #define MAX_RX_DELAY_MILLIS 32000 // 32 seconds +#ifndef NOISE_FLOOR_CALIB_INTERVAL + #define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds +#endif + void Dispatcher::begin() { n_sent_flood = n_sent_direct = 0; n_recv_flood = n_recv_direct = 0; @@ -36,6 +40,10 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { } void Dispatcher::loop() { + if (millisHasNowPassed(next_floor_calib_time)) { + _radio->triggerNoiseFloorCalibrate(); + next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); + } _radio->loop(); // check for radio 'stuck' in mode other than Rx diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 7a48067d..37f327e3 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -61,6 +61,10 @@ public: */ virtual void loop() { } + virtual int getNoiseFloor() const { return 0; } + + virtual void triggerNoiseFloorCalibrate() { } + virtual bool isInRecvMode() const = 0; /** @@ -112,6 +116,7 @@ class Dispatcher { unsigned long next_tx_time; unsigned long cad_busy_start; unsigned long radio_nonrx_start; + unsigned long next_floor_calib_time; bool prev_isrecv_mode; uint32_t n_sent_flood, n_sent_direct; uint32_t n_recv_flood, n_recv_direct; @@ -129,6 +134,7 @@ protected: { outbound = NULL; total_air_time = 0; next_tx_time = 0; cad_busy_start = 0; + next_floor_calib_time = 0; _err_flags = 0; radio_nonrx_start = 0; prev_isrecv_mode = true; diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index d52d81ff..d7b93a2f 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -35,7 +35,7 @@ void RadioLibWrapper::begin() { setFlag(); // LoRa packet is already received } - _noise_floor = -140; + _noise_floor = 0; // start average out some samples _num_floor_samples = 0; @@ -47,13 +47,23 @@ void RadioLibWrapper::idle() { state = STATE_IDLE; // need another startReceive() } +void RadioLibWrapper::triggerNoiseFloorCalibrate() { + if (_num_floor_samples >= NUM_NOISE_FLOOR_SAMPLES) { // ignore trigger if currently sampling + _num_floor_samples = 0; + _floor_sample_sum = 0; + } +} + void RadioLibWrapper::loop() { if (state == STATE_RX && _num_floor_samples < NUM_NOISE_FLOOR_SAMPLES) { if (!isReceivingPacket()) { - _num_floor_samples++; - _floor_sample_sum += getCurrentRSSI(); + int rssi = getCurrentRSSI(); + if (rssi < _noise_floor + INTERFERENCE_THRESHOLD_DB) { // only consider samples below current floor+THRESHOLD + _num_floor_samples++; + _floor_sample_sum += rssi; + } } - } else if (_floor_sample_sum != 0) { + } else if (_num_floor_samples >= NUM_NOISE_FLOOR_SAMPLES && _floor_sample_sum != 0) { _noise_floor = _floor_sample_sum / NUM_NOISE_FLOOR_SAMPLES; _floor_sample_sum = 0; diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index 211e0187..9ac7e72c 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -16,7 +16,6 @@ protected: void startRecv(); float packetScoreInt(float snr, int sf, int packet_len); virtual bool isReceivingPacket() =0; - virtual float getCurrentRSSI() =0; public: RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; } @@ -36,6 +35,11 @@ public: return isChannelActive(); } + virtual float getCurrentRSSI() =0; + + int getNoiseFloor() const override { return _noise_floor; } + void triggerNoiseFloorCalibrate() override; + void loop() override; uint32_t getPacketsRecv() const { return n_recv; } From e5ddb8a598d91fd4a1cf1d43c8c5c9e18b777867 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 26 May 2025 12:23:52 +1000 Subject: [PATCH 018/103] * RAK: "start ota" now replies with Bluetooth MAC address --- src/helpers/nrf52/RAK4631Board.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/helpers/nrf52/RAK4631Board.cpp b/src/helpers/nrf52/RAK4631Board.cpp index eb1a42c5..c75ecf29 100644 --- a/src/helpers/nrf52/RAK4631Board.cpp +++ b/src/helpers/nrf52/RAK4631Board.cpp @@ -80,6 +80,11 @@ bool RAK4631Board::startOTAUpdate(const char* id, char reply[]) { 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"); + uint8_t mac_addr[6]; + memset(mac_addr, 0, sizeof(mac_addr)); + Bluefruit.getAddr(mac_addr); + sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + return true; } From 4b103ca0de7fdcad35ac7cf59f539e822e7a024d Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Sun, 25 May 2025 21:23:31 -0700 Subject: [PATCH 019/103] t-beam supreme: fixes and consolidation Made changes requested by Scott Simplified gps init sequence and removed unnecessary code Reverted SensorManager change Updated PMU flow to enable header outputs --- src/helpers/SensorManager.h | 2 +- src/helpers/TBeamS3SupremeBoard.h | 4 +- .../lilygo_tbeam_supreme_SX1262/target.cpp | 139 +++++------------- variants/lilygo_tbeam_supreme_SX1262/target.h | 4 +- 4 files changed, 44 insertions(+), 105 deletions(-) diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 41c34e55..0e4bc27d 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -13,7 +13,7 @@ public: double node_lat, node_lon; // modify these, if you want to affect Advert location double node_altitude; // altitude in meters - SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0;} + SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/TBeamS3SupremeBoard.h b/src/helpers/TBeamS3SupremeBoard.h index 1ae6a230..ccb8e24c 100644 --- a/src/helpers/TBeamS3SupremeBoard.h +++ b/src/helpers/TBeamS3SupremeBoard.h @@ -61,10 +61,10 @@ public: void begin() { - power_init(); - ESP32Board::begin(); + power_init(); + esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { long wakeup_source = esp_sleep_get_ext1_wakeup_status(); diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index dfe453ee..6a37f7c2 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -143,9 +143,9 @@ bool TBeamS3SupremeBoard::power_init() PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); // Set up PMU interrupts - // MESH_DEBUG_PRINTLN("Setting up PMU interrupts"); - // pinMode(PIN_PMU_IRQ, INPUT_PULLUP); - // attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING); + MESH_DEBUG_PRINTLN("Setting up PMU interrupts"); + pinMode(PIN_PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING); // GPS MESH_DEBUG_PRINTLN("Setting and enabling a-ldo4 for GPS"); @@ -188,22 +188,22 @@ bool TBeamS3SupremeBoard::power_init() PMU.enableBLDO1(); // Out to header pins - // MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header"); - // PMU.setBLDO2Voltage(3300); - // PMU.enableBLDO2(); + MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header"); + PMU.setBLDO2Voltage(3300); + PMU.enableBLDO2(); - // MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header"); - // PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V - // PMU.enableDC4(); + MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header"); + PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V + PMU.enableDC4(); - // MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header"); - // PMU.setDC5Voltage(3300); - // PMU.enableDC5(); + MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header"); + PMU.setDC5Voltage(3300); + PMU.enableDC5(); // Unused power rails - MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dldo1 and dldo2"); + MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dcdc5, dldo1 and dldo2"); PMU.disableDC2(); - PMU.disableDC5(); + //PMU.disableDC5(); PMU.disableDLDO1(); PMU.disableDLDO2(); @@ -223,18 +223,18 @@ bool TBeamS3SupremeBoard::power_init() PMU.enableVbusVoltageMeasure(); // Reset and re-enable PMU interrupts - // MESH_DEBUG_PRINTLN("Re-enable interrupts"); - // PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ); - // PMU.clearIrqStatus(); - // PMU.enableIRQ( - // XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts - // XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts - // XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts - // XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts - // ); + MESH_DEBUG_PRINTLN("Re-enable interrupts"); + PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + PMU.clearIrqStatus(); + PMU.enableIRQ( + XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts + XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts + XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts + ); #ifdef MESH_DEBUG - // scanDevices(&Wire); - // scanDevices(&Wire1); + scanDevices(&Wire); + scanDevices(&Wire1); printPMU(); #endif @@ -259,56 +259,6 @@ static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, u return millis() < timeout; // false, if timed out } -static bool l76kProbe() -{ - bool result = false; - uint32_t startTimeout ; - Serial1.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); - delay(5); - // Get version information - startTimeout = millis() + 3000; - MESH_DEBUG_PRINTLN("Trying to init L76K GPS"); - // Serial1.flush(); - while (Serial1.available()) { - int c = Serial1.read(); - // Serial.write(c); - // Serial.print("."); - // Serial.flush(); - // Serial1.flush(); - if (millis() > startTimeout) { - MESH_DEBUG_PRINTLN("L76K NMEA timeout!"); - return false; - } - }; - - Serial1.flush(); - delay(200); - - Serial1.write("$PCAS06,0*1B\r\n"); - - char ver[100]; - if (!readStringUntil(Serial1, ver, sizeof(ver), '\n', 500)) { - MESH_DEBUG_PRINTLN("Get L76K timeout!"); - return false; - } - - if (memcmp(ver, "$GPTXT,01,01,02", 15) == 0) { - MESH_DEBUG_PRINTLN("L76K GNSS init succeeded, using L76K GNSS Module\n"); - result = true; - } - delay(500); - - // Initialize the L76K Chip, use GPS + GLONASS - Serial1.write("$PCAS04,5*1C\r\n"); - delay(250); - // only ask for RMC and GGA - Serial1.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); - delay(250); - // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g - Serial1.write("$PCAS11,3*1E\r\n"); - return result; -} - bool radio_init() { fallback_clock.begin(); @@ -380,25 +330,16 @@ bool TbeamSupSensorManager::begin() { // init GPS port Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, P_GPS_RX, P_GPS_TX); - bool gps_alive = false; - for ( int i = 0; i < 3; ++i) { - gps_alive = l76kProbe(); - if (gps_alive) { - MESH_DEBUG_PRINTLN("GPS is init and active. Shutting down for initial state."); - sleep_gps(); - return gps_alive; - } - } - gps_active = gps_alive; - MESH_DEBUG_PRINTLN("GPS init failed and GPS is not active"); - return gps_alive; + MESH_DEBUG_PRINTLN("Sleeping GPS for initial state"); + sleep_gps(); + return true; } bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } - if (requester_permissions & TELEM_PERM_ENVIRONMENT) { // does requester have permission? + if (requester_permissions & TELEM_PERM_ENVIRONMENT && bme_active) { // does requester have permission? telemetry.addTemperature(TELEM_CHANNEL_SELF, node_temp); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, node_hum); telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, node_pres); @@ -413,20 +354,21 @@ void TbeamSupSensorManager::loop() { _nmea->loop(); if (millis() > next_update) { - if (_nmea->isValid()) { + if (_nmea->isValid() && gps_active) { node_lat = ((double)_nmea->getLatitude())/1000000.; node_lon = ((double)_nmea->getLongitude())/1000000.; node_altitude = ((double)_nmea->getAltitude()) / 1000.0; - //Serial.printf("lat %f lon %f\r\n", _lat, _lon); + MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); } //read BME280 values - //node_alt = bme.readAltitude(SEALEVELPRESSURE_HPA); - node_temp = bme.readTemperature(); - node_hum = bme.readHumidity(); - node_pres = (bme.readPressure() / 100.0F); - - #ifdef MESH_DEBUG + if(bme_active){ + //node_alt = bme.readAltitude(SEALEVELPRESSURE_HPA); + node_temp = bme.readTemperature(); + node_hum = bme.readHumidity(); + node_pres = (bme.readPressure() / 100.0F); + + #ifdef MESH_DEBUG // Serial.print("Temperature = "); // Serial.print(node_temp); // Serial.println(" *C"); @@ -442,7 +384,8 @@ void TbeamSupSensorManager::loop() { // Serial.print("Approx. Altitude = "); // Serial.print(node_alt); // Serial.println(" m"); - #endif + #endif + } next_update = millis() + 1000; } @@ -455,7 +398,6 @@ int TbeamSupSensorManager::getNumSettings() const { const char* TbeamSupSensorManager::getSettingName(int i) const { switch(i){ case 0: return "gps"; - case 1: return "bme280"; default: NULL; } } @@ -463,7 +405,6 @@ const char* TbeamSupSensorManager::getSettingName(int i) const { const char* TbeamSupSensorManager::getSettingValue(int i) const { switch(i){ case 0: return gps_active == true ? "1" : "0"; - case 1: return bme_active == true ? "1" : "0"; default: NULL; } } diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index 6acf6cdf..293580ee 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -16,7 +16,7 @@ class TbeamSupSensorManager: public SensorManager { LocationProvider * _nmea; Adafruit_BME280 bme; double node_temp, node_hum, node_pres; - int sensorNum = 2; + int sensorNum = 1; #define SEALEVELPRESSURE_HPA (1013.25) @@ -65,8 +65,6 @@ enum { OSC32768_ONLINE = _BV(13), }; -void scanDevices(TwoWire *w); -static bool l76kProbe(); bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); From 3ae2e851a0f58e3b976129522e63a9dddb898c22 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 26 May 2025 14:39:44 +1000 Subject: [PATCH 020/103] * minor tidy ups --- variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 2 +- variants/lilygo_tbeam_supreme_SX1262/target.cpp | 2 +- variants/lilygo_tbeam_supreme_SX1262/target.h | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 8e46f22c..4e6721f9 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -67,8 +67,8 @@ build_flags = -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=8 diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 6a37f7c2..4b38b11d 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -392,7 +392,7 @@ void TbeamSupSensorManager::loop() { } int TbeamSupSensorManager::getNumSettings() const { - return sensorNum; + return 1; } const char* TbeamSupSensorManager::getSettingName(int i) const { diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index 293580ee..a3023750 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -16,7 +16,6 @@ class TbeamSupSensorManager: public SensorManager { LocationProvider * _nmea; Adafruit_BME280 bme; double node_temp, node_hum, node_pres; - int sensorNum = 1; #define SEALEVELPRESSURE_HPA (1013.25) From 0e8b807a8b5b1e5bfe2888c61763c613f1d46622 Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Sun, 25 May 2025 22:48:04 -0700 Subject: [PATCH 021/103] RAK4631: Add RAK12500 GPS support Added RAK12500 GPS support Added socket scan capability to determine which socket the GPS is connected to T-beam supreme: added conditional to displaying location data to only when gps is active --- src/helpers/nrf52/RAK4631Board.h | 11 + .../lilygo_tbeam_supreme_SX1262/target.cpp | 2 +- variants/rak4631/platformio.ini | 2 + variants/rak4631/target.cpp | 196 +++++++++++++++++- variants/rak4631/target.h | 48 ++++- 5 files changed, 256 insertions(+), 3 deletions(-) diff --git a/src/helpers/nrf52/RAK4631Board.h b/src/helpers/nrf52/RAK4631Board.h index a389135d..9232e39c 100644 --- a/src/helpers/nrf52/RAK4631Board.h +++ b/src/helpers/nrf52/RAK4631Board.h @@ -12,6 +12,17 @@ #define P_LORA_MISO 45 #define P_LORA_MOSI 44 #define SX126X_POWER_EN 37 + +#define P_GPS_SDA 13 //GPS SDA pin (output option) +#define P_GPS_SCL 14 //GPS SCL pin (output option) +#define P_GPS_TX 16 //GPS TX pin +#define P_GPS_RX 15 //GPS RX pin +#define P_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) +#define P_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) +#define P_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) +#define P_GPS_1PPS 17 //GPS PPS pin +#define GPS_BAUD_RATE 9600 +#define GPS_ADDRESS 0x42 //i2c address for GPS #define SX126X_DIO2_AS_RF_SWITCH true #define SX126X_DIO3_TCXO_VOLTAGE 1.8 diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 6a37f7c2..5ab13b57 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -336,7 +336,7 @@ bool TbeamSupSensorManager::begin() { } bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } if (requester_permissions & TELEM_PERM_ENVIRONMENT && bme_active) { // does requester have permission? diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index c2845fd4..54fd063a 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -22,6 +22,8 @@ build_src_filter = ${nrf52840_base.build_src_filter} lib_deps = ${nrf52840_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 + stevemarple/MicroNMEA @ ^2.0.6 + sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index 9b23af83..1f55dac1 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" #include +#include RAK4631Board board; @@ -10,7 +11,8 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Wire); +RAK4631SensorManager sensors = RAK4631SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -20,6 +22,68 @@ SensorManager sensors; #define LORA_CR 5 #endif +#ifdef MESH_DEBUG +uint32_t deviceOnline = 0x00; +void scanDevices(TwoWire *w) +{ + uint8_t err, addr; + int nDevices = 0; + uint32_t start = 0; + + Serial.println("Scanning I2C for Devices"); + for (addr = 1; addr < 127; addr++) { + start = millis(); + w->beginTransmission(addr); delay(2); + err = w->endTransmission(); + if (err == 0) { + nDevices++; + switch (addr) { + case 0x42: + Serial.println("\tFound RAK12500 GPS Sensor"); + deviceOnline |= RAK12500_ONLINE; + break; + default: + Serial.print("\tI2C device found at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.print(addr, HEX); + Serial.println(" !"); + break; + } + + } else if (err == 4) { + Serial.print("Unknow error at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.println(addr, HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + + Serial.println("Scan for devices is complete."); + Serial.println("\n"); +} +#endif + +static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, unsigned int timeout_millis) { + unsigned long timeout = millis() + timeout_millis; + char *dp = dest; + while (millis() < timeout && dp - dest < max_len - 1) { + if (s.available()) { + char c = s.read(); + if (c == term) break; + *dp++ = c; // append to dest[] + } else { + delay(1); + } + } + *dp = 0; // null terminator + return millis() < timeout; // false, if timed out +} + bool radio_init() { rtc_clock.begin(Wire); @@ -68,6 +132,136 @@ void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } +void RAK4631SensorManager::start_gps() +{ + //function currently not used + gps_active = true; + pinMode(disStandbyPin, OUTPUT); + digitalWrite(disStandbyPin, 1); + MESH_DEBUG_PRINTLN("GPS should be on now"); +} + +void RAK4631SensorManager::stop_gps() +{ + //function currently not used + gps_active = false; + pinMode(disStandbyPin, OUTPUT); + digitalWrite(disStandbyPin, 0); + MESH_DEBUG_PRINTLN("GPS should be off now"); +} + +void RAK4631SensorManager::sleep_gps() { + gps_active = false; + ublox_GNSS.powerSaveMode(); + MESH_DEBUG_PRINTLN("GPS should be sleeping now"); +} + +void RAK4631SensorManager::wake_gps() { + gps_active = true; + ublox_GNSS.powerSaveMode(false); + MESH_DEBUG_PRINTLN("GPS should be waking now"); +} + +bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ + + int pinInitialState = 0; + + //set initial waking state + pinMode(ioPin,OUTPUT); + digitalWrite(ioPin,0); + delay(1000); + digitalWrite(ioPin,1); + delay(1000); + + if (ublox_GNSS.begin(Wire) == true){ + MESH_DEBUG_PRINTLN("GPS init correctly and GPS is turned on"); + ublox_GNSS.setI2COutput(COM_TYPE_NMEA); + ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); + disStandbyPin = ioPin; + gps_active = true; + gps_present = true; + return true; + } + else + MESH_DEBUG_PRINTLN("GPS failed to init on this IO pin... try the next"); + //digitalWrite(ioPin,pinInitialState); //reset the IO pin to initial state + return false; +} + +bool RAK4631SensorManager::begin() { + + #ifdef MESH_DEBUG + scanDevices(&Wire); + #endif + + //search for the correct IO standby pin depending on socket used + if(gpsIsAwake(P_GPS_STANDBY_A)){ + MESH_DEBUG_PRINTLN("GPS is on socket A"); + } + else if(gpsIsAwake(P_GPS_STANDBY_C)){ + MESH_DEBUG_PRINTLN("GPS is on socket C"); + } + else if(gpsIsAwake(P_GPS_STANDBY_F)){ + MESH_DEBUG_PRINTLN("GPS is on socket F"); + } + else{ + MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); + gps_active = false; + gps_present = false; + return false; + } + + //Now that GPS is found and set up, set to sleep for initial state + stop_gps(); +} + +bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void RAK4631SensorManager::loop() { + static long next_update = 0; + + _nmea->loop(); + + if (millis() > next_update && gps_active) { + node_lat = (double)ublox_GNSS.getLatitude()/10000000.; + node_lon = (double)ublox_GNSS.getLongitude()/10000000.; + node_altitude = (double)ublox_GNSS.getAltitude()/1000.; + MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); + + next_update = millis() + 1000; + } +} + +int RAK4631SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) + +const char* RAK4631SensorManager::getSettingName(int i) const { + return i == 0 ? "gps" : NULL; +} + +const char* RAK4631SensorManager::getSettingValue(int i) const { + if (i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} + +bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) { + if (strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + return false; // not supported +} + mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index a50d6f2c..7ae3e914 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -7,19 +7,65 @@ #include #include #include +#include +#include #ifdef DISPLAY_CLASS #include #endif +#define _BV(x) (1 << x) + +class RAK4631SensorManager: public SensorManager { + bool gps_active = false; + bool gps_present = false; + LocationProvider * _nmea; + SFE_UBLOX_GNSS ublox_GNSS; + uint32_t disStandbyPin = 0; + + void start_gps(); + void stop_gps(); + void sleep_gps(); + void wake_gps(); + bool gpsIsAwake(uint32_t ioPin); + + public: + RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; + }; + extern RAK4631Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern RAK4631SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; #endif +enum { + POWERMANAGE_ONLINE = _BV(0), + DISPLAY_ONLINE = _BV(1), + RADIO_ONLINE = _BV(2), + GPS_ONLINE = _BV(3), + PSRAM_ONLINE = _BV(4), + SDCARD_ONLINE = _BV(5), + AXDL345_ONLINE = _BV(6), + BME280_ONLINE = _BV(7), + BMP280_ONLINE = _BV(8), + BME680_ONLINE = _BV(9), + QMC6310_ONLINE = _BV(10), + QMI8658_ONLINE = _BV(11), + PCF8563_ONLINE = _BV(12), + OSC32768_ONLINE = _BV(13), + RAK12500_ONLINE = _BV(14), +}; + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); From b3d78ac8a798279e45338c2d2b49cf09bb454474 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 26 May 2025 17:18:49 +1000 Subject: [PATCH 022/103] * interference threshold now stored in prefs, CLI: set/get "int.thresh" --- examples/simple_repeater/main.cpp | 4 ++++ examples/simple_room_server/main.cpp | 4 ++++ src/Dispatcher.cpp | 2 +- src/Dispatcher.h | 3 ++- src/helpers/CommonCLI.cpp | 8 ++++++++ src/helpers/CommonCLI.h | 1 + src/helpers/RadioLibWrappers.cpp | 16 ++++++++-------- src/helpers/RadioLibWrappers.h | 4 ++-- 8 files changed, 30 insertions(+), 12 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 12c843b7..06edec13 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -327,6 +327,9 @@ protected: uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); return getRNG()->nextInt(0, 6)*t; } + int getInterferenceThreshold() const override { + return _prefs.interference_threshold; + } void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (type == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) @@ -565,6 +568,7 @@ public: _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 3; // 3 hours _prefs.flood_max = 64; + _prefs.interference_threshold = 14; // DB } CommonCLI* getCLI() { return &_cli; } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index dad7ce78..d46270ac 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -406,6 +406,9 @@ protected: uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); return getRNG()->nextInt(0, 6)*t; } + int getInterferenceThreshold() const override { + return _prefs.interference_threshold; + } bool allowPacketForward(const mesh::Packet* packet) override { if (_prefs.disable_fwd) return false; @@ -711,6 +714,7 @@ public: _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 3; // 3 hours _prefs.flood_max = 64; + _prefs.interference_threshold = 14; // DB #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 06c5e035..7ac5cbe3 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -41,7 +41,7 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { void Dispatcher::loop() { if (millisHasNowPassed(next_floor_calib_time)) { - _radio->triggerNoiseFloorCalibrate(); + _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); } _radio->loop(); diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 37f327e3..bce13b6b 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -63,7 +63,7 @@ public: virtual int getNoiseFloor() const { return 0; } - virtual void triggerNoiseFloorCalibrate() { } + virtual void triggerNoiseFloorCalibrate(int threshold) { } virtual bool isInRecvMode() const = 0; @@ -153,6 +153,7 @@ protected: virtual int calcRxDelay(float score, uint32_t air_time) const; virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; + virtual int getInterferenceThreshold() const { return 0; } // disabled by default public: void begin(); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 8b8296f5..baad8f40 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -56,6 +56,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read(pad, 4); // 120 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 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -109,6 +110,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write(pad, 4); // 120 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.close(); } @@ -176,6 +178,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch const char* config = &command[4]; if (memcmp(config, "af", 2) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); + } else if (memcmp(config, "int.thresh", 10) == 0) { + sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); } else if (memcmp(config, "allow.read.only", 15) == 0) { sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off"); } else if (memcmp(config, "flood.advert.interval", 21) == 0) { @@ -223,6 +227,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->airtime_factor = atof(&config[3]); savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "int.thresh ", 11) == 0) { + _prefs->interference_threshold = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "allow.read.only ", 16) == 0) { _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 0e88c266..37402c09 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -24,6 +24,7 @@ struct NodePrefs { // persisted to file uint8_t reserved2; float bw; uint8_t flood_max; + uint8_t interference_threshold; }; class CommonCLICallbacks { diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index d7b93a2f..d37bc498 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -8,10 +8,6 @@ #define STATE_TX_DONE 4 #define STATE_INT_READY 16 -#ifndef INTERFERENCE_THRESHOLD_DB - #define INTERFERENCE_THRESHOLD_DB 14 -#endif - #define NUM_NOISE_FLOOR_SAMPLES 64 static volatile uint8_t state = STATE_IDLE; @@ -36,6 +32,7 @@ void RadioLibWrapper::begin() { } _noise_floor = 0; + _threshold = 0; // start average out some samples _num_floor_samples = 0; @@ -47,8 +44,9 @@ void RadioLibWrapper::idle() { state = STATE_IDLE; // need another startReceive() } -void RadioLibWrapper::triggerNoiseFloorCalibrate() { - if (_num_floor_samples >= NUM_NOISE_FLOOR_SAMPLES) { // ignore trigger if currently sampling +void RadioLibWrapper::triggerNoiseFloorCalibrate(int threshold) { + _threshold = threshold; + if (threshold > 0 && _num_floor_samples >= NUM_NOISE_FLOOR_SAMPLES) { // ignore trigger if currently sampling _num_floor_samples = 0; _floor_sample_sum = 0; } @@ -58,7 +56,7 @@ void RadioLibWrapper::loop() { if (state == STATE_RX && _num_floor_samples < NUM_NOISE_FLOOR_SAMPLES) { if (!isReceivingPacket()) { int rssi = getCurrentRSSI(); - if (rssi < _noise_floor + INTERFERENCE_THRESHOLD_DB) { // only consider samples below current floor+THRESHOLD + if (rssi < _noise_floor + _threshold) { // only consider samples below current floor+THRESHOLD _num_floor_samples++; _floor_sample_sum += rssi; } @@ -145,7 +143,9 @@ void RadioLibWrapper::onSendFinished() { } bool RadioLibWrapper::isChannelActive() { - return getCurrentRSSI() > _noise_floor + INTERFERENCE_THRESHOLD_DB; + return _threshold == 0 + ? false // interference check is disabled + : getCurrentRSSI() > _noise_floor + _threshold; } float RadioLibWrapper::getLastRSSI() const { diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index 9ac7e72c..bb308071 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -8,7 +8,7 @@ protected: PhysicalLayer* _radio; mesh::MainBoard* _board; uint32_t n_recv, n_sent; - int16_t _noise_floor; + int16_t _noise_floor, _threshold; uint16_t _num_floor_samples; int32_t _floor_sample_sum; @@ -38,7 +38,7 @@ public: virtual float getCurrentRSSI() =0; int getNoiseFloor() const override { return _noise_floor; } - void triggerNoiseFloorCalibrate() override; + void triggerNoiseFloorCalibrate(int threshold) override; void loop() override; From 0e90b73110d290dd5b548d048ad0d7a3e62f9562 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 26 May 2025 19:52:32 +1000 Subject: [PATCH 023/103] * companion: PUSH_CODE_LOGIN_SUCCESS frame, now includes server clock timestamp --- examples/companion_radio/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 13db88c0..6eee0591 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -695,6 +695,7 @@ protected: if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false + memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; if (keep_alive_secs > 0) { @@ -702,11 +703,13 @@ protected: } out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = data[6]; // permissions (eg. is_admin) + memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix + memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp } else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix _serial->writeFrame(out_frame, i); } else if (len > 4 && // check for status response pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme From a86364e6d8390e0378b4252eaaa0cfa62ba3e99b Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 27 May 2025 00:28:23 +1000 Subject: [PATCH 024/103] * stats: curr_free_queue_len now repurposed to noise_floor --- examples/simple_repeater/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 06edec13..5db62ff1 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -79,7 +79,7 @@ struct RepeaterStats { uint16_t batt_milli_volts; uint16_t curr_tx_queue_len; - uint16_t curr_free_queue_len; + int16_t noise_floor; int16_t last_rssi; uint32_t n_packets_recv; uint32_t n_packets_sent; @@ -183,7 +183,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { RepeaterStats stats; stats.batt_milli_volts = board.getBattMilliVolts(); stats.curr_tx_queue_len = _mgr->getOutboundCount(0xFFFFFFFF); - stats.curr_free_queue_len = _mgr->getFreeCount(); + stats.noise_floor = (int16_t)_radio->getNoiseFloor(); stats.last_rssi = (int16_t) radio_driver.getLastRSSI(); stats.n_packets_recv = radio_driver.getPacketsRecv(); stats.n_packets_sent = radio_driver.getPacketsSent(); From 30488e6f675fece372c58af5c07090574f06c57d Mon Sep 17 00:00:00 2001 From: seagull9000 Date: Tue, 27 May 2025 11:07:51 +1200 Subject: [PATCH 025/103] Connect RTTTL shutdown melody to shutdown procedure Added a new UITask shutdown method to run non-board specific shutdown code. This avoids having to update all the board files for different hardware. UITask::shutdown(bool restart = false); Where the buzzer is available and defined, the RTTTL shutdown melody is played when the button is held down for >5s. --- examples/companion_radio/UITask.cpp | 25 ++++++++++++++++++++++++- examples/companion_radio/UITask.h | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index f97b47f4..1932512f 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -256,7 +256,7 @@ void UITask::buttonHandler() { digitalWrite(PIN_STATUS_LED, LOW); delay(10); #endif - _board->powerOff(); + shutdown(); } } btn_state_change_time = millis(); @@ -267,6 +267,29 @@ void UITask::buttonHandler() { #endif } +/* hardware-agnostic pre-shutdown activity should be done here +*/ +void UITask::shutdown(bool restart){ + + #ifdef PIN_BUZZER + /* note: we have a choice here - + we can do a blocking buzzer.loop() with non-deterministic consequences + or we can set a flag and delay the shutdown for a couple of seconds + while a non-blocking buzzer.loop() plays out in UITask::loop() + */ + buzzer.shutdown(); + uint32_t buzzer_timer = millis(); // fail-safe shutdown + while (buzzer.isPlaying() && (millis() - 2500) < buzzer_timer) + buzzer.loop(); + + #endif // PIN_BUZZER + + if (restart) + _board->reboot(); + else + _board->powerOff(); +} + void UITask::loop() { buttonHandler(); userLedHandler(); diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 134b5a16..d774e54c 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -39,7 +39,6 @@ class UITask { void buttonHandler(); void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); - public: @@ -55,5 +54,6 @@ public: void msgRead(int msgcount); void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void soundBuzzer(UIEventType bet = UIEventType::none); + void shutdown(bool restart = false); void loop(); }; From 67462cb861eb3d3791aa0389418844a9f2c4f69a Mon Sep 17 00:00:00 2001 From: hank Date: Mon, 26 May 2025 17:41:55 -0700 Subject: [PATCH 026/103] Fixing compilation issue, missing tbeam func --- variants/lilygo_tbeam/target.cpp | 42 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/variants/lilygo_tbeam/target.cpp b/variants/lilygo_tbeam/target.cpp index 5259cb36..47c14297 100644 --- a/variants/lilygo_tbeam/target.cpp +++ b/variants/lilygo_tbeam/target.cpp @@ -7,27 +7,10 @@ TBeamBoard board; #define PMU_WIRE_PORT Wire bool pmuIntFlag = false; - -void TBeamBoard::printPMU() -{ - Serial.print("isCharging:"); Serial.println(PMU->isCharging() ? "YES" : "NO"); - Serial.print("isDischarge:"); Serial.println(PMU->isDischarge() ? "YES" : "NO"); - Serial.print("isVbusIn:"); Serial.println(PMU->isVbusIn() ? "YES" : "NO"); - Serial.print("getBattVoltage:"); Serial.print(PMU->getBattVoltage()); Serial.println("mV"); - Serial.print("getVbusVoltage:"); Serial.print(PMU->getVbusVoltage()); Serial.println("mV"); - Serial.print("getSystemVoltage:"); Serial.print(PMU->getSystemVoltage()); Serial.println("mV"); - - // The battery percentage may be inaccurate at first use, the PMU will automatically - // learn the battery curve and will automatically calibrate the battery percentage - // after a charge and discharge cycle - if (PMU->isBatteryConnect()) { - Serial.print("getBatteryPercent:"); Serial.print(PMU->getBatteryPercent()); Serial.println("%"); - } - - Serial.println(); +static void setPMUIntFlag(){ + pmuIntFlag = true; } - #if defined(P_LORA_SCLK) static SPIClass spi; RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); @@ -197,3 +180,24 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +#ifdef MESH_DEBUG +void TBeamBoard::printPMU() +{ + Serial.print("isCharging:"); Serial.println(PMU->isCharging() ? "YES" : "NO"); + Serial.print("isDischarge:"); Serial.println(PMU->isDischarge() ? "YES" : "NO"); + Serial.print("isVbusIn:"); Serial.println(PMU->isVbusIn() ? "YES" : "NO"); + Serial.print("getBattVoltage:"); Serial.print(PMU->getBattVoltage()); Serial.println("mV"); + Serial.print("getVbusVoltage:"); Serial.print(PMU->getVbusVoltage()); Serial.println("mV"); + Serial.print("getSystemVoltage:"); Serial.print(PMU->getSystemVoltage()); Serial.println("mV"); + + // The battery percentage may be inaccurate at first use, the PMU will automatically + // learn the battery curve and will automatically calibrate the battery percentage + // after a charge and discharge cycle + if (PMU->isBatteryConnect()) { + Serial.print("getBatteryPercent:"); Serial.print(PMU->getBatteryPercent()); Serial.println("%"); + } + + Serial.println(); +} +#endif \ No newline at end of file From b3fc6bedf94e5163782227f53102fca2d2a91be9 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 27 May 2025 18:45:06 +1000 Subject: [PATCH 027/103] * companion: saveContacts() now deferred for 5 secs (lazy writes) --- examples/companion_radio/main.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 6eee0591..3dadb88d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -57,6 +57,7 @@ #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f #define DIRECT_SEND_PERHOP_FACTOR 6.0f #define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define LAZY_CONTACTS_WRITE_DELAY 5000 #define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" @@ -198,6 +199,7 @@ class MyMesh : public BaseChatMesh { uint8_t app_target_ver; uint8_t* sign_data; uint32_t sign_data_len; + unsigned long dirty_contacts_expiry; uint8_t cmd_frame[MAX_FRAME_SIZE+1]; uint8_t out_frame[MAX_FRAME_SIZE+1]; CayenneLPP telemetry; @@ -524,7 +526,7 @@ protected: #endif } - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } void onContactPathUpdated(const ContactInfo& contact) override { @@ -532,7 +534,7 @@ protected: memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } bool processAck(const uint8_t *data) override { @@ -603,7 +605,8 @@ protected: void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { markConnectionActive(from); - saveContacts(); // from.sync_since change needs to be persisted + // from.sync_since change needs to be persisted + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text); } @@ -797,6 +800,7 @@ public: pending_login = pending_status = pending_telemetry = 0; next_ack_idx = 0; sign_data = NULL; + dirty_contacts_expiry = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -1148,7 +1152,7 @@ public: if (recipient) { recipient->out_path_len = -1; //recipient->lastmod = ?? shouldn't be needed, app already has this version of contact - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } else { writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact @@ -1159,7 +1163,7 @@ public: if (recipient) { updateContactFromFrame(*recipient, cmd_frame, len); //recipient->lastmod = ?? shouldn't be needed, app already has this version of contact - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } else { ContactInfo contact; @@ -1167,7 +1171,7 @@ public: contact.lastmod = getRTCClock()->getCurrentTime(); contact.sync_since = 0; if (addContact(contact)) { - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } else { writeErrFrame(ERR_CODE_TABLE_FULL); @@ -1177,7 +1181,7 @@ public: uint8_t* pub_key = &cmd_frame[1]; ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient && removeContact(*recipient)) { - saveContacts(); + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove @@ -1296,6 +1300,9 @@ public: savePrefs(); writeOKFrame(); } else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { + if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? + saveContacts(); + } board.reboot(); } else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { uint8_t reply[3]; @@ -1566,6 +1573,12 @@ public: checkConnections(); } + // is there are pending dirty contacts write needed? + if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { + saveContacts(); + dirty_contacts_expiry = 0; + } + #ifdef DISPLAY_CLASS ui_task.setHasConnection(_serial->isConnected()); ui_task.loop(); From 4c3f8ac6b68fbdd2e8b061f818694351c45de901 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 27 May 2025 22:38:01 +1000 Subject: [PATCH 028/103] * Room server: stats refactor -> noise_floor --- examples/simple_room_server/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index d46270ac..5ba6cbca 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -126,7 +126,7 @@ struct PostInfo { struct ServerStats { uint16_t batt_milli_volts; uint16_t curr_tx_queue_len; - uint16_t curr_free_queue_len; + int16_t noise_floor; int16_t last_rssi; uint32_t n_packets_recv; uint32_t n_packets_sent; @@ -287,7 +287,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { ServerStats stats; stats.batt_milli_volts = board.getBattMilliVolts(); stats.curr_tx_queue_len = _mgr->getOutboundCount(0xFFFFFFFF); - stats.curr_free_queue_len = _mgr->getFreeCount(); + stats.noise_floor = (int16_t)_radio->getNoiseFloor(); stats.last_rssi = (int16_t) radio_driver.getLastRSSI(); stats.n_packets_recv = radio_driver.getPacketsRecv(); stats.n_packets_sent = radio_driver.getPacketsSent(); From fec064c1a246172f669913e05fca9f7899aff092 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 27 May 2025 22:48:28 +1000 Subject: [PATCH 029/103] * companion: interference threshold default (14) --- examples/companion_radio/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 3dadb88d..30a1c9cc 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -490,6 +490,10 @@ protected: return _prefs.airtime_factor; } + int getInterferenceThreshold() const override { + return 14; // hard-coded for now + } + int calcRxDelay(float score, uint32_t air_time) const override { if (_prefs.rx_delay_base <= 0.0f) return 0; return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); From d8c2b3ab4701f4713b1a4fc5b05a2ba0e7603b87 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 27 May 2025 23:37:59 +1000 Subject: [PATCH 030/103] * TBeam: fix for debug output --- variants/lilygo_tbeam/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/lilygo_tbeam/target.cpp b/variants/lilygo_tbeam/target.cpp index 47c14297..69a980fc 100644 --- a/variants/lilygo_tbeam/target.cpp +++ b/variants/lilygo_tbeam/target.cpp @@ -65,7 +65,7 @@ bool TBeamBoard::power_init() if (!PMU) { - Serial.println("PMU init failed."); + MESH_DEBUG_PRINTLN("PMU init failed."); return false; } From e7761dc9dccc54fa900a743006368a3b678c652e Mon Sep 17 00:00:00 2001 From: JQ Date: Tue, 27 May 2025 19:10:56 -0700 Subject: [PATCH 031/103] initial button manager --- examples/companion_radio/Button.cpp | 127 ++++++++++++++++++++++++++++ examples/companion_radio/Button.h | 77 +++++++++++++++++ examples/companion_radio/UITask.cpp | 118 +++++++++++++++----------- examples/companion_radio/UITask.h | 15 +++- 4 files changed, 288 insertions(+), 49 deletions(-) create mode 100644 examples/companion_radio/Button.cpp create mode 100644 examples/companion_radio/Button.h diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp new file mode 100644 index 00000000..161bb6aa --- /dev/null +++ b/examples/companion_radio/Button.cpp @@ -0,0 +1,127 @@ +#include "Button.h" + +Button::Button(uint8_t pin, bool activeState) + : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) { + _currentState = !_activeState; + _lastState = _currentState; +} + +Button::Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold) + : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) { + _currentState = !_activeState; + _lastState = _currentState; +} + +void Button::begin() { + if (!_isAnalog) { + pinMode(_pin, INPUT_PULLUP); + } + _currentState = readButton(); + _lastState = _currentState; +} + +void Button::update() { + uint32_t now = millis(); + + // Read button at specified interval + if (now - _lastReadTime < BUTTON_READ_INTERVAL_MS) { + return; + } + _lastReadTime = now; + + bool newState = readButton(); + + // Check if state has changed + if (newState != _lastState) { + _stateChangeTime = now; + } + + // Debounce check + if ((now - _stateChangeTime) > BUTTON_DEBOUNCE_TIME_MS) { + if (newState != _currentState) { + _currentState = newState; + handleStateChange(); + } + } + + _lastState = newState; + + // Handle multi-click timeout + if (_state == WAITING_FOR_MULTI_CLICK && (now - _releaseTime) > BUTTON_CLICK_TIMEOUT_MS) { + // Timeout reached, process the clicks + if (_clickCount == 1) { + triggerEvent(SHORT_PRESS); + } else if (_clickCount == 2) { + triggerEvent(DOUBLE_PRESS); + } else if (_clickCount >= 3) { + triggerEvent(TRIPLE_PRESS); + } + _clickCount = 0; + _state = IDLE; + } + + // Handle long press + if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { + triggerEvent(LONG_PRESS); + _state = IDLE; // Prevent multiple long press events + _clickCount = 0; + } +} + +bool Button::readButton() { + if (_isAnalog) { + return (analogRead(_pin) < _analogThreshold) ? _activeState : !_activeState; + } else { + return digitalRead(_pin); + } +} + +void Button::handleStateChange() { + uint32_t now = millis(); + + if (_currentState == _activeState) { + // Button pressed + _pressTime = now; + _state = PRESSED; + triggerEvent(ANY_PRESS); + } else { + // Button released + if (_state == PRESSED) { + uint32_t pressDuration = now - _pressTime; + + if (pressDuration < BUTTON_LONG_PRESS_TIME_MS) { + // Short press detected + _clickCount++; + _releaseTime = now; + _state = WAITING_FOR_MULTI_CLICK; + } else { + // Long press already handled in update() + _state = IDLE; + } + } + } +} + +void Button::triggerEvent(EventType event) { + _lastEvent = event; + + switch (event) { + case ANY_PRESS: + if (_onAnyPress) _onAnyPress(); + break; + case SHORT_PRESS: + if (_onShortPress) _onShortPress(); + break; + case DOUBLE_PRESS: + if (_onDoublePress) _onDoublePress(); + break; + case TRIPLE_PRESS: + if (_onTriplePress) _onTriplePress(); + break; + case LONG_PRESS: + if (_onLongPress) _onLongPress(); + break; + default: + break; + } +} \ No newline at end of file diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h new file mode 100644 index 00000000..a7406824 --- /dev/null +++ b/examples/companion_radio/Button.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +// Button timing configuration +#define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms +#define BUTTON_CLICK_TIMEOUT_MS 400 // Max time between clicks for multi-click +#define BUTTON_LONG_PRESS_TIME_MS 5000 // Time to trigger long press (5 seconds) +#define BUTTON_READ_INTERVAL_MS 10 // How often to read the button + +class Button { +public: + enum EventType { + NONE, + SHORT_PRESS, + DOUBLE_PRESS, + TRIPLE_PRESS, + LONG_PRESS, + ANY_PRESS + }; + + using EventCallback = std::function; + + Button(uint8_t pin, bool activeState = LOW); + Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold = 20); + + void begin(); + void update(); + + // Set callbacks for different events + void onShortPress(EventCallback callback) { _onShortPress = callback; } + void onDoublePress(EventCallback callback) { _onDoublePress = callback; } + void onTriplePress(EventCallback callback) { _onTriplePress = callback; } + void onLongPress(EventCallback callback) { _onLongPress = callback; } + void onAnyPress(EventCallback callback) { _onAnyPress = callback; } // New method + + // State getters + bool isPressed() const { return _currentState == _activeState; } + EventType getLastEvent() const { return _lastEvent; } + +private: + enum State { + IDLE, + PRESSED, + RELEASED, + WAITING_FOR_MULTI_CLICK + }; + + uint8_t _pin; + bool _activeState; + bool _isAnalog; + uint16_t _analogThreshold; + + State _state = IDLE; + bool _currentState; + bool _lastState; + + uint32_t _stateChangeTime = 0; + uint32_t _pressTime = 0; + uint32_t _releaseTime = 0; + uint32_t _lastReadTime = 0; + + uint8_t _clickCount = 0; + EventType _lastEvent = NONE; + + // Callbacks + EventCallback _onShortPress = nullptr; + EventCallback _onDoublePress = nullptr; + EventCallback _onTriplePress = nullptr; + EventCallback _onLongPress = nullptr; + EventCallback _onAnyPress = nullptr; + + bool readButton(); + void handleStateChange(); + void triggerEvent(EventType event); +}; \ No newline at end of file diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 675972dc..15897fa2 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -57,6 +57,24 @@ void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs, const char* bu #ifdef PIN_BUZZER buzzer.begin(); #endif + + // Initialize button with appropriate configuration +#if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) + #ifdef PIN_USER_BTN + _userButton = new Button(PIN_USER_BTN, USER_BTN_PRESSED); + #else + _userButton = new Button(PIN_USER_BTN_ANA, USER_BTN_PRESSED, true, 20); + #endif + + _userButton->begin(); + + // Set up button callbacks + _userButton->onShortPress([this]() { handleButtonShortPress(); }); + _userButton->onDoublePress([this]() { handleButtonDoublePress(); }); + _userButton->onTriplePress([this]() { handleButtonTriplePress(); }); + _userButton->onLongPress([this]() { handleButtonLongPress(); }); + _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); +#endif } void UITask::soundBuzzer(UIEventType bet) { @@ -233,53 +251,8 @@ void UITask::userLedHandler() { #endif } -void UITask::buttonHandler() { - #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) - static int prev_btn_state = !USER_BTN_PRESSED; - static int prev_btn_state_ana = !USER_BTN_PRESSED; - static unsigned long btn_state_change_time = 0; - static unsigned long next_read = 0; - int cur_time = millis(); - if (cur_time >= next_read) { - int btn_state = 0; - int btn_state_ana = 0; - #ifdef PIN_USER_BTN - btn_state = digitalRead(PIN_USER_BTN); - #endif - #ifdef PIN_USER_BTN_ANA - btn_state_ana = (analogRead(PIN_USER_BTN_ANA) < 20); // analogRead returns a value hopefully below 20 when button is pressed. - #endif - if (btn_state != prev_btn_state || btn_state_ana != prev_btn_state_ana) { // check for either digital or analogue button change of state - if (btn_state == USER_BTN_PRESSED || btn_state_ana == USER_BTN_PRESSED) { // pressed? - if (_display != NULL) { - if (_display->isOn()) { - clearMsgPreview(); - } else { - _display->turnOn(); - _need_refresh = true; - } - _auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer - } - } else { // unpressed ? check pressed time ... - if ((cur_time - btn_state_change_time) > 5000) { - #ifdef PIN_STATUS_LED - digitalWrite(PIN_STATUS_LED, LOW); - delay(10); - #endif - shutdown(); // without restart - } - } - btn_state_change_time = millis(); - prev_btn_state = btn_state; - prev_btn_state_ana = btn_state_ana; - } - next_read = millis() + 100; // 10 reads per second - } - #endif - } - - -/* hardware-agnostic pre-shutdown activity should be done here +/* + hardware-agnostic pre-shutdown activity should be done here */ void UITask::shutdown(bool restart){ @@ -303,7 +276,11 @@ void UITask::shutdown(bool restart){ } void UITask::loop() { - buttonHandler(); + #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) + if (_userButton) { + _userButton->update(); + } + #endif userLedHandler(); #ifdef PIN_BUZZER @@ -328,3 +305,48 @@ void UITask::loop() { } } } + +void UITask::handleButtonAnyPress() { + MESH_DEBUG_PRINTLN("UITask: any press triggered"); + if (_display != NULL) { + if (!_display->isOn()) { + _display->turnOn(); + _need_refresh = true; + } else { + // Turn on display + _display->turnOn(); + _need_refresh = true; + } + _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + } +} + +void UITask::handleButtonShortPress() { + MESH_DEBUG_PRINTLN("UITask: short press triggered"); + if (_display != NULL) { + if (_display->isOn()) { + // If display is on and showing message preview, clear it + if (_origin[0] && _msg[0]) { + clearMsgPreview(); + } + } + } +} + +void UITask::handleButtonDoublePress() { + MESH_DEBUG_PRINTLN("UITask: double press triggered"); + // Not implemented. TODO: possibly send an advert here? +} + +void UITask::handleButtonTriplePress() { + MESH_DEBUG_PRINTLN("UITask: triple press triggered"); + // Toggle buzzer quiet mode + #ifdef PIN_BUZZER + buzzer.quiet(!buzzer.isQuiet()); + #endif +} + +void UITask::handleButtonLongPress() { + MESH_DEBUG_PRINTLN("UITask: long press triggered"); + shutdown(); +} \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index d774e54c..a4bf6103 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -9,6 +9,7 @@ #endif #include "NodePrefs.h" +#include "Button.h" enum class UIEventType { @@ -35,10 +36,22 @@ class UITask { int _msgcount; bool _need_refresh = true; + // Button handlers +#if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) + Button* _userButton = nullptr; +#endif + void renderCurrScreen(); - void buttonHandler(); void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); + + // Button action handlers + void handleButtonAnyPress(); + void handleButtonShortPress(); + void handleButtonDoublePress(); + void handleButtonTriplePress(); + void handleButtonLongPress(); + public: From d47c0cfccfa17066619336f7d6d7a42eea19eedc Mon Sep 17 00:00:00 2001 From: JQ Date: Tue, 27 May 2025 19:20:35 -0700 Subject: [PATCH 032/103] add scaling to bitmaps for EPD display, and enable buzzer for Thinknode M1 --- src/helpers/ui/GxEPDDisplay.cpp | 34 +++++++++++++++++++++++++++- variants/thinknode_m1/platformio.ini | 3 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index 7faaf2c3..d37a0981 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -81,7 +81,39 @@ void GxEPDDisplay::drawRect(int x, int y, int w, int h) { } void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { - display.drawBitmap(x*SCALE_X, (y*SCALE_Y) + 10, bits, w, h, GxEPD_BLACK); + // Calculate the base position in display coordinates + uint16_t startX = x * SCALE_X; + uint16_t startY = y * SCALE_Y; + + // Width in bytes for bitmap processing + uint16_t widthInBytes = (w + 7) / 8; + + // Process the bitmap row by row + for (uint16_t by = 0; by < h; by++) { + // Calculate the target y-coordinates for this logical row + int y1 = startY + (int)(by * SCALE_Y); + int y2 = startY + (int)((by + 1) * SCALE_Y); + int block_h = y2 - y1; + + // Scan across the row bit by bit + for (uint16_t bx = 0; bx < w; bx++) { + // Calculate the target x-coordinates for this logical column + int x1 = startX + (int)(bx * SCALE_X); + int x2 = startX + (int)((bx + 1) * SCALE_X); + int block_w = x2 - x1; + + // Get the current bit + uint16_t byteOffset = (by * widthInBytes) + (bx / 8); + uint8_t bitMask = 0x80 >> (bx & 7); + bool bitSet = pgm_read_byte(bits + byteOffset) & bitMask; + + // If the bit is set, draw a block of pixels + if (bitSet) { + // Draw the block as a filled rectangle + display.fillRect(x1, y1, block_w, block_h, GxEPD_BLACK); + } + } + } } uint16_t GxEPDDisplay::getTextWidth(const char* str) { diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 9bad98e2..65ed0ead 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -71,6 +71,7 @@ build_flags = -D DISPLAY_ROTATION=4 -D DISPLAY_CLASS=GxEPDDisplay -D OFFLINE_QUEUE_SIZE=256 + -D PIN_BUZZER=6 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 @@ -79,8 +80,10 @@ build_src_filter = ${ThinkNode_M1.build_src_filter} + + + + + +<../examples/companion_radio> lib_deps = ${ThinkNode_M1.lib_deps} densaugeo/base64 @ ~1.4.0 zinggjm/GxEPD2 @ 1.6.2 + end2endzone/NonBlockingRTTTL@^1.3.0 From 59a236effb231fd4240d7cbffd9d287a3839fc25 Mon Sep 17 00:00:00 2001 From: JQ Date: Tue, 27 May 2025 21:38:48 -0700 Subject: [PATCH 033/103] add GPS support to thinknode M1 --- variants/thinknode_m1/platformio.ini | 3 + variants/thinknode_m1/target.cpp | 92 +++++++++++++++++++++++++++- variants/thinknode_m1/target.h | 21 ++++++- variants/thinknode_m1/variant.h | 10 +-- 4 files changed, 119 insertions(+), 7 deletions(-) diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 9bad98e2..846c96ec 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -25,6 +25,9 @@ build_src_filter = ${nrf52840_thinknode_m1.build_src_filter} + + +<../variants/thinknode_m1> +lib_deps = + ${nrf52840_thinknode_m1.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 debug_tool = jlink upload_protocol = nrfutil diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index 5a09eb9a..c88ab848 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" #include +#include ThinkNodeM1Board board; @@ -10,7 +11,8 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +ThinkNodeM1SensorManager sensors = ThinkNodeM1SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -72,3 +74,91 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +void ThinkNodeM1SensorManager::start_gps() { + if (!gps_active) { + gps_active = true; + _location->begin(); + } +} + +void ThinkNodeM1SensorManager::stop_gps() { + if (gps_active) { + gps_active = false; + _location->stop(); + } +} + +bool ThinkNodeM1SensorManager::begin() { + Serial1.begin(9600); + + // Try to detect if GPS is physically connected to determine if we should expose the setting + pinMode(GPS_EN, OUTPUT); + digitalWrite(GPS_EN, HIGH); // Power on GPS + + // Give GPS a moment to send data if it's physically connected and powered + delay(1500); + + // We'll consider GPS detected if we see any data on Serial1 + gps_detected = (Serial1.available() > 0); + + 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); + } + + return true; +} + +bool ThinkNodeM1SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void ThinkNodeM1SensorManager::loop() { + static long next_gps_update = 0; + + _location->loop(); + + if (millis() > next_gps_update) { + if (_location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + next_gps_update = millis() + 1000; + } +} + +int ThinkNodeM1SensorManager::getNumSettings() const { + return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected +} + +const char* ThinkNodeM1SensorManager::getSettingName(int i) const { + return (gps_detected && i == 0) ? "gps" : NULL; +} + +const char* ThinkNodeM1SensorManager::getSettingValue(int i) const { + if (gps_detected && i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} + +bool ThinkNodeM1SensorManager::setSettingValue(const char* name, const char* value) { + if (gps_detected && strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + return false; // not supported +} diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index c958d0e3..7f7a1055 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -7,14 +7,33 @@ #include #include #include +#include #ifdef DISPLAY_CLASS #include #endif +class ThinkNodeM1SensorManager : public SensorManager { + bool gps_active = false; + bool gps_detected = false; + LocationProvider* _location; + + void start_gps(); + void stop_gps(); +public: + ThinkNodeM1SensorManager(LocationProvider &location): _location(&location) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; +}; + extern ThinkNodeM1Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern ThinkNodeM1SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; diff --git a/variants/thinknode_m1/variant.h b/variants/thinknode_m1/variant.h index 3f35ace0..8e4e90bf 100644 --- a/variants/thinknode_m1/variant.h +++ b/variants/thinknode_m1/variant.h @@ -40,8 +40,8 @@ //////////////////////////////////////////////////////////////////////////////// // UART pin definition -#define PIN_SERIAL1_RX (41) // GPS TX -#define PIN_SERIAL1_TX (40) // GPS RX +#define PIN_SERIAL1_RX PIN_GPS_TX +#define PIN_SERIAL1_TX PIN_GPS_RX //////////////////////////////////////////////////////////////////////////////// // I2C pin definition @@ -125,9 +125,9 @@ extern const int SCK; //////////////////////////////////////////////////////////////////////////////// // GPS -#define PIN_GPS_RX (41) -#define PIN_GPS_TX (40) -#define PIN_GPS_WAKEUP (34) +#define PIN_GPS_RX (40) +#define PIN_GPS_TX (41) +#define GPS_EN (34) #define PIN_GPS_RESET (37) #define PIN_GPS_PPS (36) #define PIN_GPS_STANDBY (34) From 1072da0eeb366a9579af3405e338bcafaa105dd9 Mon Sep 17 00:00:00 2001 From: JQ Date: Tue, 27 May 2025 23:24:08 -0700 Subject: [PATCH 034/103] handle gps switch --- variants/thinknode_m1/target.cpp | 55 +++++++++++++++++++++----------- variants/thinknode_m1/target.h | 2 +- variants/thinknode_m1/variant.h | 1 + 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index c88ab848..70b5c61e 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -92,22 +92,16 @@ void ThinkNodeM1SensorManager::stop_gps() { bool ThinkNodeM1SensorManager::begin() { Serial1.begin(9600); - // Try to detect if GPS is physically connected to determine if we should expose the setting + // Initialize GPS switch pin + pinMode(PIN_GPS_SWITCH, INPUT); + last_gps_switch_state = digitalRead(PIN_GPS_SWITCH); + + // Initialize GPS power pin pinMode(GPS_EN, OUTPUT); - digitalWrite(GPS_EN, HIGH); // Power on GPS - // Give GPS a moment to send data if it's physically connected and powered - delay(1500); - - // We'll consider GPS detected if we see any data on Serial1 - gps_detected = (Serial1.available() > 0); - - 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); + // Check initial switch state to determine if GPS should be active + if (last_gps_switch_state == HIGH) { // Switch is HIGH when ON + start_gps(); } return true; @@ -122,6 +116,31 @@ bool ThinkNodeM1SensorManager::querySensors(uint8_t requester_permissions, Cayen void ThinkNodeM1SensorManager::loop() { static long next_gps_update = 0; + static long last_switch_check = 0; + + // Check GPS switch state every second + if (millis() - last_switch_check > 1000) { + bool current_switch_state = digitalRead(PIN_GPS_SWITCH); + + // Detect switch state change + if (current_switch_state != last_gps_switch_state) { + last_gps_switch_state = current_switch_state; + + if (current_switch_state == HIGH) { // Switch is ON + MESH_DEBUG_PRINTLN("GPS switch ON"); + start_gps(); + } else { // Switch is OFF + MESH_DEBUG_PRINTLN("GPS switch OFF"); + stop_gps(); + } + } + + last_switch_check = millis(); + } + + if (!gps_active) { + return; // GPS is not active, skip further processing + } _location->loop(); @@ -137,22 +156,22 @@ void ThinkNodeM1SensorManager::loop() { } int ThinkNodeM1SensorManager::getNumSettings() const { - return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected + return 1; // always show GPS setting } const char* ThinkNodeM1SensorManager::getSettingName(int i) const { - return (gps_detected && i == 0) ? "gps" : NULL; + return (i == 0) ? "gps" : NULL; } const char* ThinkNodeM1SensorManager::getSettingValue(int i) const { - if (gps_detected && i == 0) { + if (i == 0) { return gps_active ? "1" : "0"; } return NULL; } bool ThinkNodeM1SensorManager::setSettingValue(const char* name, const char* value) { - if (gps_detected && strcmp(name, "gps") == 0) { + if (strcmp(name, "gps") == 0) { if (strcmp(value, "0") == 0) { stop_gps(); } else { diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index 7f7a1055..550ee62a 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -14,7 +14,7 @@ class ThinkNodeM1SensorManager : public SensorManager { bool gps_active = false; - bool gps_detected = false; + bool last_gps_switch_state = false; LocationProvider* _location; void start_gps(); diff --git a/variants/thinknode_m1/variant.h b/variants/thinknode_m1/variant.h index 8e4e90bf..2b58e341 100644 --- a/variants/thinknode_m1/variant.h +++ b/variants/thinknode_m1/variant.h @@ -131,3 +131,4 @@ extern const int SCK; #define PIN_GPS_RESET (37) #define PIN_GPS_PPS (36) #define PIN_GPS_STANDBY (34) +#define PIN_GPS_SWITCH (33) From 4b16cda03a26ac6c957ef04cf514430f9fa7fb2c Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 28 May 2025 23:31:31 +1000 Subject: [PATCH 035/103] * RAK4632 targets, now requiring ENV_INCLUDE_GPS to enable GPS --- variants/rak4631/platformio.ini | 23 +++++++++++++++++++++++ variants/rak4631/target.cpp | 27 +++++++++++---------------- variants/rak4631/target.h | 17 +++++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 54fd063a..c7f1fa53 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -97,6 +97,29 @@ lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:RAK_4631_GPS_companion_radio_ble] +extends = rak4631 +build_flags = + ${rak4631.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D ENV_INCLUDE_GPS=1 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${rak4631.build_src_filter} + + + + + +<../examples/companion_radio> +lib_deps = + ${rak4631.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:RAK_4631_terminal_chat] extends = rak4631 build_flags = diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index 1f55dac1..daadd044 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -11,8 +11,13 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Wire); RAK4631SensorManager sensors = RAK4631SensorManager(nmea); +#else +RAK4631SensorManager sensors; +#endif #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -68,22 +73,6 @@ void scanDevices(TwoWire *w) } #endif -static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, unsigned int timeout_millis) { - unsigned long timeout = millis() + timeout_millis; - char *dp = dest; - while (millis() < timeout && dp - dest < max_len - 1) { - if (s.available()) { - char c = s.read(); - if (c == term) break; - *dp++ = c; // append to dest[] - } else { - delay(1); - } - } - *dp = 0; // null terminator - return millis() < timeout; // false, if timed out -} - bool radio_init() { rtc_clock.begin(Wire); @@ -132,6 +121,7 @@ void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } +#if ENV_INCLUDE_GPS void RAK4631SensorManager::start_gps() { //function currently not used @@ -187,6 +177,7 @@ bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ //digitalWrite(ioPin,pinInitialState); //reset the IO pin to initial state return false; } +#endif bool RAK4631SensorManager::begin() { @@ -194,6 +185,7 @@ bool RAK4631SensorManager::begin() { scanDevices(&Wire); #endif +#if ENV_INCLUDE_GPS //search for the correct IO standby pin depending on socket used if(gpsIsAwake(P_GPS_STANDBY_A)){ MESH_DEBUG_PRINTLN("GPS is on socket A"); @@ -213,8 +205,10 @@ bool RAK4631SensorManager::begin() { //Now that GPS is found and set up, set to sleep for initial state stop_gps(); +#endif } +#if ENV_INCLUDE_GPS bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); @@ -261,6 +255,7 @@ bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) } return false; // not supported } +#endif mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index 7ae3e914..3e6db0aa 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -7,8 +7,10 @@ #include #include #include -#include -#include +#if ENV_INCLUDE_GPS + #include + #include +#endif #ifdef DISPLAY_CLASS #include #endif @@ -16,6 +18,7 @@ #define _BV(x) (1 << x) class RAK4631SensorManager: public SensorManager { + #if ENV_INCLUDE_GPS bool gps_active = false; bool gps_present = false; LocationProvider * _nmea; @@ -27,17 +30,23 @@ class RAK4631SensorManager: public SensorManager { void sleep_gps(); void wake_gps(); bool gpsIsAwake(uint32_t ioPin); + #endif public: + #if ENV_INCLUDE_GPS RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } - bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; void loop() override; int getNumSettings() const override; const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; - }; + #else + RAK4631SensorManager() { } + #endif + bool begin() override; +}; extern RAK4631Board board; extern WRAPPER_CLASS radio_driver; From ce87156a435d1e4c6fefd94fc32bd0d381da4d82 Mon Sep 17 00:00:00 2001 From: JQ Date: Wed, 28 May 2025 16:45:41 -0700 Subject: [PATCH 036/103] cleanup --- examples/companion_radio/Button.cpp | 1 + examples/companion_radio/Button.h | 2 +- examples/companion_radio/UITask.cpp | 6 +----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index 161bb6aa..09172404 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -97,6 +97,7 @@ void Button::handleStateChange() { } else { // Long press already handled in update() _state = IDLE; + _clickCount = 0; // Reset click count after long press } } } diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h index a7406824..74e5b3f7 100644 --- a/examples/companion_radio/Button.h +++ b/examples/companion_radio/Button.h @@ -5,7 +5,7 @@ // Button timing configuration #define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms -#define BUTTON_CLICK_TIMEOUT_MS 400 // Max time between clicks for multi-click +#define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click #define BUTTON_LONG_PRESS_TIME_MS 5000 // Time to trigger long press (5 seconds) #define BUTTON_READ_INTERVAL_MS 10 // How often to read the button diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 15897fa2..7b2a6c52 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -311,12 +311,8 @@ void UITask::handleButtonAnyPress() { if (_display != NULL) { if (!_display->isOn()) { _display->turnOn(); - _need_refresh = true; - } else { - // Turn on display - _display->turnOn(); - _need_refresh = true; } + _need_refresh = true; _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer } } From ece7479843b725d09d9ca8190462318f2c810f54 Mon Sep 17 00:00:00 2001 From: JQ Date: Wed, 28 May 2025 22:21:51 -0700 Subject: [PATCH 037/103] add gps for techo --- variants/techo/platformio.ini | 3 ++ variants/techo/target.cpp | 81 ++++++++++++++++++++++++++++++++++- variants/techo/target.h | 19 +++++++- variants/techo/variant.cpp | 4 +- variants/techo/variant.h | 7 ++- 5 files changed, 106 insertions(+), 8 deletions(-) diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index c4bc8f85..95c9ff84 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -25,6 +25,9 @@ build_src_filter = ${nrf52840_techo.build_src_filter} + + +<../variants/techo> +lib_deps = + ${nrf52840_techo.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 debug_tool = jlink upload_protocol = nrfutil diff --git a/variants/techo/target.cpp b/variants/techo/target.cpp index 880af612..cf807e4d 100644 --- a/variants/techo/target.cpp +++ b/variants/techo/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" #include +#include TechoBoard board; @@ -10,7 +11,8 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +TechoSensorManager sensors = TechoSensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -72,3 +74,80 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +void TechoSensorManager::start_gps() { + if (!gps_active) { + gps_active = true; + _location->begin(); + } +} + +void TechoSensorManager::stop_gps() { + if (gps_active) { + gps_active = false; + _location->stop(); + } +} + +bool TechoSensorManager::begin() { + Serial1.begin(9600); + + // GPS enabled pin + pinMode(GPS_EN, OUTPUT); + + return true; +} + +bool TechoSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void TechoSensorManager::loop() { + static long next_gps_update = 0; + + if (!gps_active) { + return; // GPS is not active, skip further processing + } + + _location->loop(); + + if (millis() > next_gps_update) { + if (_location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + next_gps_update = millis() + 1000; + } +} + +int TechoSensorManager::getNumSettings() const { + return 1; // always show GPS setting +} + +const char* TechoSensorManager::getSettingName(int i) const { + return (i == 0) ? "gps" : NULL; +} + +const char* TechoSensorManager::getSettingValue(int i) const { + if (i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} + +bool TechoSensorManager::setSettingValue(const char* name, const char* value) { + if (strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + return false; // not supported +} diff --git a/variants/techo/target.h b/variants/techo/target.h index 15524111..8fb8b6ca 100644 --- a/variants/techo/target.h +++ b/variants/techo/target.h @@ -7,15 +7,32 @@ #include #include #include +#include #ifdef DISPLAY_CLASS #include #endif +class TechoSensorManager : public SensorManager { + bool gps_active = false; + LocationProvider* _location; + + void start_gps(); + void stop_gps(); +public: + TechoSensorManager(LocationProvider &location): _location(&location) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; +}; extern TechoBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern TechoSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; diff --git a/variants/techo/variant.cpp b/variants/techo/variant.cpp index 155aa42d..ad1fd560 100644 --- a/variants/techo/variant.cpp +++ b/variants/techo/variant.cpp @@ -29,6 +29,6 @@ void initVariant() { digitalWrite(PIN_TXCO, HIGH); // shutdown gps - pinMode(PIN_GPS_STANDBY, OUTPUT); - digitalWrite(PIN_GPS_STANDBY, LOW); + pinMode(GPS_EN, OUTPUT); + digitalWrite(GPS_EN, LOW); } diff --git a/variants/techo/variant.h b/variants/techo/variant.h index f553ab42..ef35525a 100644 --- a/variants/techo/variant.h +++ b/variants/techo/variant.h @@ -40,8 +40,8 @@ //////////////////////////////////////////////////////////////////////////////// // UART pin definition -#define PIN_SERIAL1_RX (41) // GPS TX -#define PIN_SERIAL1_TX (40) // GPS RX +#define PIN_SERIAL1_RX PIN_GPS_TX +#define PIN_SERIAL1_TX PIN_GPS_RX //////////////////////////////////////////////////////////////////////////////// // I2C pin definition @@ -127,7 +127,6 @@ extern const int SCK; #define PIN_GPS_RX (41) #define PIN_GPS_TX (40) -#define PIN_GPS_WAKEUP (34) +#define GPS_EN (34) #define PIN_GPS_RESET (37) #define PIN_GPS_PPS (36) -#define PIN_GPS_STANDBY (34) From 25850cbc781249a8cc4a7a6195a8c572a2313393 Mon Sep 17 00:00:00 2001 From: JQ Date: Wed, 28 May 2025 23:21:28 -0700 Subject: [PATCH 038/103] fix pins --- variants/techo/variant.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/techo/variant.h b/variants/techo/variant.h index ef35525a..6aebf82f 100644 --- a/variants/techo/variant.h +++ b/variants/techo/variant.h @@ -125,8 +125,8 @@ extern const int SCK; //////////////////////////////////////////////////////////////////////////////// // GPS -#define PIN_GPS_RX (41) -#define PIN_GPS_TX (40) +#define PIN_GPS_RX (40) +#define PIN_GPS_TX (41) #define GPS_EN (34) #define PIN_GPS_RESET (37) #define PIN_GPS_PPS (36) From 585558a9bbe20ada738c8ce65daf624383ff1c89 Mon Sep 17 00:00:00 2001 From: recrof Date: Fri, 30 May 2025 14:31:44 +0200 Subject: [PATCH 039/103] added basic support for LilyGo Tlora C6 --- platformio.ini | 5 ++ src/helpers/ESP32Board.cpp | 2 +- variants/lilygo_tlora_c6/platformio.ini | 68 ++++++++++++++++++++ variants/lilygo_tlora_c6/target.cpp | 82 +++++++++++++++++++++++++ variants/lilygo_tlora_c6/target.h | 20 ++++++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 variants/lilygo_tlora_c6/platformio.ini create mode 100644 variants/lilygo_tlora_c6/target.cpp create mode 100644 variants/lilygo_tlora_c6/target.h diff --git a/platformio.ini b/platformio.ini index 80f850ea..94c961cf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,6 +47,11 @@ lib_deps = me-no-dev/ESPAsyncWebServer @ ^3.6.0 file://arch/esp32/AsyncElegantOTA +; esp32c6 uses arduino framework 3.x +[esp32c6_base] +extends = esp32_base +platform = https://github.com/pioarduino/platform-espressif32.git + ; ----------------- NRF52 --------------------- [nrf52_base] diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index dd1f1801..4dce467c 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -2,7 +2,7 @@ #include "ESP32Board.h" -#if defined(ADMIN_PASSWORD) // Repeater or Room Server only +#if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only #include #include #include diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini new file mode 100644 index 00000000..a973aa49 --- /dev/null +++ b/variants/lilygo_tlora_c6/platformio.ini @@ -0,0 +1,68 @@ +[tlora_c6] +extends = esp32c6_base +board = esp32-c6-devkitm-1 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit +build_flags = + ${esp32c6_base.build_flags} + -I variants/lilygo_tlora_c6 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D ARDUINO_USB_MODE=1 + -D P_LORA_TX_LED=7 + -D P_LORA_SCLK=6 + -D P_LORA_MISO=1 + -D P_LORA_MOSI=0 + -D P_LORA_NSS=18 + -D P_LORA_DIO_1=23 + -D P_LORA_BUSY=22 + -D P_LORA_RESET=21 + -D PIN_BOARD_SDA=8 + -D PIN_BOARD_SCL=9 + -D SX126X_RXEN=15 + -D SX126X_TXEN=14 + -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 RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SKIP_WIFI_OTA=1 +build_src_filter = ${esp32c6_base.build_src_filter} + +<../variants/lilygo_tlora_c6> + +[env:LilyGo_Tlora_c6_Repeater] +extends = tlora_c6 +build_src_filter = ${tlora_c6.build_src_filter} + +<../examples/simple_repeater/main.cpp> +build_flags = + ${tlora_c6.build_flags} + -D ADVERT_NAME='"Tlora C6 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 = + ${tlora_c6.lib_deps} +; ${esp32_ota.lib_deps} + +[env:LilyGo_Tlora_c6_companion_radio_ble] +extends = tlora_c6 +build_flags = ${tlora_c6.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${tlora_c6.build_src_filter} + + + - + +<../examples/companion_radio> +lib_deps = + ${tlora_c6.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tlora_c6/target.cpp b/variants/lilygo_tlora_c6/target.cpp new file mode 100644 index 00000000..1e60dc32 --- /dev/null +++ b/variants/lilygo_tlora_c6/target.cpp @@ -0,0 +1,82 @@ +#include +#include "target.h" + +ESP32Board board; + +#if defined(P_LORA_SCLK) + static SPIClass spi(0); + 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; + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#ifdef SX126X_DIO3_TCXO_VOLTAGE + float tcxo = SX126X_DIO3_TCXO_VOLTAGE; +#else + float tcxo = 1.6f; +#endif + +#if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); +#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); + +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + +#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() { + 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/lilygo_tlora_c6/target.h b/variants/lilygo_tlora_c6/target.h new file mode 100644 index 00000000..eef923ab --- /dev/null +++ b/variants/lilygo_tlora_c6/target.h @@ -0,0 +1,20 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +extern ESP32Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager 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(); From db8e72791c5b9397b65a1b673e6edd46446048f8 Mon Sep 17 00:00:00 2001 From: JQ Date: Fri, 30 May 2025 20:32:49 -0700 Subject: [PATCH 040/103] usability fixes, fix t114 build src filter --- examples/companion_radio/Button.cpp | 6 +++--- examples/companion_radio/Button.h | 4 ++-- examples/companion_radio/UITask.cpp | 15 +++++++++++---- examples/companion_radio/UITask.h | 1 + variants/t114/platformio.ini | 3 +-- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index 09172404..00ae39ff 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -60,10 +60,10 @@ void Button::update() { _state = IDLE; } - // Handle long press + // Handle long press while button is held if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { triggerEvent(LONG_PRESS); - _state = IDLE; // Prevent multiple long press events + _state = IDLE; // Prevent multiple press events _clickCount = 0; } } @@ -97,7 +97,7 @@ void Button::handleStateChange() { } else { // Long press already handled in update() _state = IDLE; - _clickCount = 0; // Reset click count after long press + _clickCount = 0; } } } diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h index 74e5b3f7..82e953ab 100644 --- a/examples/companion_radio/Button.h +++ b/examples/companion_radio/Button.h @@ -6,7 +6,7 @@ // Button timing configuration #define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms #define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click -#define BUTTON_LONG_PRESS_TIME_MS 5000 // Time to trigger long press (5 seconds) +#define BUTTON_LONG_PRESS_TIME_MS 3000 // Time to trigger long press (3 seconds) #define BUTTON_READ_INTERVAL_MS 10 // How often to read the button class Button { @@ -33,7 +33,7 @@ public: void onDoublePress(EventCallback callback) { _onDoublePress = callback; } void onTriplePress(EventCallback callback) { _onTriplePress = callback; } void onLongPress(EventCallback callback) { _onLongPress = callback; } - void onAnyPress(EventCallback callback) { _onAnyPress = callback; } // New method + void onAnyPress(EventCallback callback) { _onAnyPress = callback; } // State getters bool isPressed() const { return _currentState == _activeState; } diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 7b2a6c52..39364e42 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -308,11 +308,13 @@ void UITask::loop() { void UITask::handleButtonAnyPress() { MESH_DEBUG_PRINTLN("UITask: any press triggered"); + // called on any button press before other events, to wake up the display quickly + // do not refresh the display here, as it may block the button handler if (_display != NULL) { - if (!_display->isOn()) { + _displayWasOn = _display->isOn(); // Track display state before any action + if (!_displayWasOn) { _display->turnOn(); } - _need_refresh = true; _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer } } @@ -320,12 +322,17 @@ void UITask::handleButtonAnyPress() { void UITask::handleButtonShortPress() { MESH_DEBUG_PRINTLN("UITask: short press triggered"); if (_display != NULL) { - if (_display->isOn()) { - // If display is on and showing message preview, clear it + // Only clear message preview if display was already on before button press + if (_displayWasOn) { + // If display was on and showing message preview, clear it if (_origin[0] && _msg[0]) { clearMsgPreview(); + } else { + // Otherwise, refresh the display + _need_refresh = true; } } + // Note: Display turn-on and auto-off timer extension are handled by handleButtonAnyPress } } diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index a4bf6103..9546aaf0 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -35,6 +35,7 @@ class UITask { char _msg[80]; int _msgcount; bool _need_refresh = true; + bool _displayWasOn = false; // Track display state before button press // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index 9e72bbd6..37e31e6f 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -79,8 +79,7 @@ build_flags = build_src_filter = ${Heltec_t114.build_src_filter} + + - +<../examples/companion_radio/main.cpp> - +<../examples/companion_radio/UITask.cpp> + +<../examples/companion_radio> + + + From c445bbeaf250792cbdce943c9f76afb08d3da32a Mon Sep 17 00:00:00 2001 From: JQ Date: Fri, 30 May 2025 22:14:37 -0700 Subject: [PATCH 041/103] simplify logic --- examples/companion_radio/Button.cpp | 10 +++++----- examples/companion_radio/Button.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index 00ae39ff..0e425b9a 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -2,13 +2,13 @@ Button::Button(uint8_t pin, bool activeState) : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) { - _currentState = !_activeState; + _currentState = false; // Initialize as not pressed _lastState = _currentState; } Button::Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold) : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) { - _currentState = !_activeState; + _currentState = false; // Initialize as not pressed _lastState = _currentState; } @@ -70,16 +70,16 @@ void Button::update() { bool Button::readButton() { if (_isAnalog) { - return (analogRead(_pin) < _analogThreshold) ? _activeState : !_activeState; + return (analogRead(_pin) < _analogThreshold); } else { - return digitalRead(_pin); + return (digitalRead(_pin) == _activeState); } } void Button::handleStateChange() { uint32_t now = millis(); - if (_currentState == _activeState) { + if (_currentState) { // Button pressed _pressTime = now; _state = PRESSED; diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h index 82e953ab..47c792bd 100644 --- a/examples/companion_radio/Button.h +++ b/examples/companion_radio/Button.h @@ -36,7 +36,7 @@ public: void onAnyPress(EventCallback callback) { _onAnyPress = callback; } // State getters - bool isPressed() const { return _currentState == _activeState; } + bool isPressed() const { return _currentState; } EventType getLastEvent() const { return _lastEvent; } private: From f69efaf027ac997fdb91f0de61ea76c0eb5ba90c Mon Sep 17 00:00:00 2001 From: JQ Date: Fri, 30 May 2025 22:26:29 -0700 Subject: [PATCH 042/103] removing pinmode --- examples/companion_radio/Button.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index 0e425b9a..ec1f0f69 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -13,9 +13,6 @@ Button::Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThre } void Button::begin() { - if (!_isAnalog) { - pinMode(_pin, INPUT_PULLUP); - } _currentState = readButton(); _lastState = _currentState; } From cf171af72ca1ef662135d21fd86aa59bdaeb417a Mon Sep 17 00:00:00 2001 From: JQ Date: Fri, 30 May 2025 22:55:53 -0700 Subject: [PATCH 043/103] add ack for quiet mode --- examples/companion_radio/UITask.cpp | 11 ++++++++++- examples/companion_radio/UITask.h | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 39364e42..b2142a4d 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -87,6 +87,9 @@ switch(bet){ case UIEventType::channelMessage: buzzer.play("kerplop:d=16,o=6,b=120:32g#,32c#"); break; + case UIEventType::ack: + buzzer.play("ack:d=32,o=7,b=120:c"); + break; case UIEventType::roomMessage: case UIEventType::newContactMessage: case UIEventType::none: @@ -345,7 +348,13 @@ void UITask::handleButtonTriplePress() { MESH_DEBUG_PRINTLN("UITask: triple press triggered"); // Toggle buzzer quiet mode #ifdef PIN_BUZZER - buzzer.quiet(!buzzer.isQuiet()); + if (buzzer.isQuiet()) { + buzzer.quiet(false); + soundBuzzer(UIEventType::ack); + } else { + soundBuzzer(UIEventType::ack); + buzzer.quiet(true); + } #endif } diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 9546aaf0..acf5237e 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -17,7 +17,8 @@ contactMessage, channelMessage, roomMessage, - newContactMessage + newContactMessage, + ack }; class UITask { From 4ec3675091c1285825fe7f330174761e076c4025 Mon Sep 17 00:00:00 2001 From: JQ Date: Fri, 30 May 2025 22:58:30 -0700 Subject: [PATCH 044/103] update sound --- examples/companion_radio/UITask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index b2142a4d..5ff5f140 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -88,7 +88,7 @@ switch(bet){ buzzer.play("kerplop:d=16,o=6,b=120:32g#,32c#"); break; case UIEventType::ack: - buzzer.play("ack:d=32,o=7,b=120:c"); + buzzer.play("ack:d=32,o=8,b=120:c"); break; case UIEventType::roomMessage: case UIEventType::newContactMessage: From 390694137cf6e72ae1347372817a773bcba9c336 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Sat, 31 May 2025 08:06:53 +0200 Subject: [PATCH 045/103] fixed old def --- variants/lilygo_tlora_c6/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini index a973aa49..48e310c0 100644 --- a/variants/lilygo_tlora_c6/platformio.ini +++ b/variants/lilygo_tlora_c6/platformio.ini @@ -26,7 +26,7 @@ build_flags = -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 - -D SKIP_WIFI_OTA=1 + -D DISABLE_WIFI_OTA=1 build_src_filter = ${esp32c6_base.build_src_filter} +<../variants/lilygo_tlora_c6> From 08aad7338b1ab449a307743410026aa3d2d0dc33 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 31 May 2025 10:57:22 +0200 Subject: [PATCH 046/103] gps : sync time on fix --- examples/companion_radio/main.cpp | 2 +- src/helpers/sensors/LocationProvider.h | 6 ++++ .../sensors/MicroNMEALocationProvider.h | 29 +++++++++++++++++-- variants/t1000-e/target.cpp | 21 ++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 30a1c9cc..8c6c715c 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -1643,7 +1643,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) ; diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 056e61e0..f51eea28 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -4,13 +4,19 @@ class LocationProvider { +protected: + bool _time_sync_needed = true; public: + virtual void syncTime() { _time_sync_needed = true; } + virtual bool waitingTimeSync() { return _time_sync_needed; } virtual long getLatitude() = 0; virtual long getLongitude() = 0; virtual long getAltitude() = 0; + virtual long satellitesCount() = 0; virtual bool isValid() = 0; virtual long getTimestamp() = 0; + virtual void sendSentence(const char * sentence); virtual void reset(); virtual void begin(); virtual void stop(); diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 9f439e25..ee6e43eb 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -19,13 +19,16 @@ class MicroNMEALocationProvider : public LocationProvider { char _nmeaBuffer[100]; MicroNMEA nmea; + mesh::RTCClock* _clock; Stream* _gps_serial; int _pin_reset; int _pin_en; + long next_check = 0; + long time_valid = 0; public : - MicroNMEALocationProvider(Stream& ser, 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) { + MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = &rtc_clock, 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) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); digitalWrite(_pin_reset, GPS_RESET_FORCE); @@ -59,6 +62,7 @@ public : } } + void syncTime() override { nmea.clear(); LocationProvider::syncTime(); } long getLatitude() override { return nmea.getLatitude(); } long getLongitude() override { return nmea.getLongitude(); } long getAltitude() override { @@ -66,6 +70,7 @@ public : nmea.getAltitude(alt); return alt; } + long satellitesCount() override { return nmea.getNumSatellites(); } bool isValid() override { return nmea.isValid(); } long getTimestamp() override { @@ -73,7 +78,12 @@ public : return dt.unixtime(); } + void sendSentence(const char *sentence) override { + nmea.sendSentence(*_gps_serial, sentence); + } + void loop() override { + while (_gps_serial->available()) { char c = _gps_serial->read(); #ifdef GPS_NMEA_DEBUG @@ -81,5 +91,20 @@ public : #endif nmea.process(c); } + + if (!isValid()) time_valid = 0; + + if (millis() > next_check) { + next_check = millis() + 1000; + if (_time_sync_needed && time_valid > 2) { + if (_clock != NULL) { + rtc_clock.setCurrentTime(getTimestamp()); + _time_sync_needed = false; + } + } + if (isValid()) { + time_valid ++; + } + } } }; \ No newline at end of file diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index be82ca76..296551e1 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -9,7 +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; -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); T1000SensorManager sensors = T1000SensorManager(nmea); #ifdef DISPLAY_CLASS @@ -179,14 +179,26 @@ void T1000SensorManager::loop() { } } -int T1000SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) +int T1000SensorManager::getNumSettings() const { return 2; } // just one supported: "gps" (power switch) const char* T1000SensorManager::getSettingName(int i) const { - return i == 0 ? "gps" : NULL; + switch (i) { + case 0: + return "gps"; + break; + case 1: + return "sync"; + break; + default: + return NULL; + break; + } } const char* T1000SensorManager::getSettingValue(int i) const { if (i == 0) { return gps_active ? "1" : "0"; + } else if (i == 1) { + return _nmea->waitingTimeSync() ? "1" : "0"; } return NULL; } @@ -198,6 +210,9 @@ bool T1000SensorManager::setSettingValue(const char* name, const char* value) { start_gps(); } return true; + } else if (strcmp(name, "sync") == 0) { + _nmea->syncTime(); // whatever the value ... + return true; } return false; // not supported } From 2a645ee42734e9679845b3d9e4d805434ec06342 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 31 May 2025 15:52:59 +0200 Subject: [PATCH 047/103] wio-e5 : make distinct targets for dev board and mini dev board --- .../{wio-e5 => wio-e5-dev}/platformio.ini | 4 +- variants/{wio-e5 => wio-e5-dev}/target.cpp | 0 variants/wio-e5-dev/target.h | 33 +++++++++ variants/wio-e5-dev/variant.h | 10 +++ variants/wio-e5-mini/platformio.ini | 32 ++++++++ variants/wio-e5-mini/target.cpp | 73 +++++++++++++++++++ variants/{wio-e5 => wio-e5-mini}/target.h | 8 +- variants/{wio-e5 => wio-e5-mini}/variant.h | 6 +- 8 files changed, 160 insertions(+), 6 deletions(-) rename variants/{wio-e5 => wio-e5-dev}/platformio.ini (94%) rename variants/{wio-e5 => wio-e5-dev}/target.cpp (100%) create mode 100644 variants/wio-e5-dev/target.h create mode 100644 variants/wio-e5-dev/variant.h create mode 100644 variants/wio-e5-mini/platformio.ini create mode 100644 variants/wio-e5-mini/target.cpp rename variants/{wio-e5 => wio-e5-mini}/target.h (78%) rename variants/{wio-e5 => wio-e5-mini}/variant.h (50%) diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5-dev/platformio.ini similarity index 94% rename from variants/wio-e5/platformio.ini rename to variants/wio-e5-dev/platformio.ini index d8f7954f..22c2d880 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5-dev/platformio.ini @@ -7,9 +7,9 @@ build_flags = ${stm32_base.build_flags} -D WRAPPER_CLASS=CustomSTM32WLxWrapper -D SPI_INTERFACES_COUNT=0 -D RX_BOOSTED_GAIN=true - -I variants/wio-e5 + -I variants/wio-e5-dev build_src_filter = ${stm32_base.build_src_filter} - +<../variants/wio-e5> + +<../variants/wio-e5-dev> [env:wio-e5-repeater] extends = lora_e5 diff --git a/variants/wio-e5/target.cpp b/variants/wio-e5-dev/target.cpp similarity index 100% rename from variants/wio-e5/target.cpp rename to variants/wio-e5-dev/target.cpp diff --git a/variants/wio-e5-dev/target.h b/variants/wio-e5-dev/target.h new file mode 100644 index 00000000..83fd9cf1 --- /dev/null +++ b/variants/wio-e5-dev/target.h @@ -0,0 +1,33 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +class WIOE5Board : public STM32Board { +public: + const char* getManufacturerName() const override { + return "Seeed Wio E5 Dev Board"; + } + + // Just returns ADC value for now to test adc + uint16_t getBattMilliVolts() override { + uint32_t raw = analogRead(PIN_A3); + return raw; + } +}; + +extern WIOE5Board board; +extern WRAPPER_CLASS radio_driver; +extern VolatileRTCClock rtc_clock; +extern SensorManager 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-e5-dev/variant.h b/variants/wio-e5-dev/variant.h new file mode 100644 index 00000000..821fc410 --- /dev/null +++ b/variants/wio-e5-dev/variant.h @@ -0,0 +1,10 @@ +#pragma once + +// UART Definitions +// #ifndef SERIAL_UART_INSTANCE +// #define SERIAL_UART_INSTANCE 101 +// #endif + +#include + +#undef RNG diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini new file mode 100644 index 00000000..55ab0bc8 --- /dev/null +++ b/variants/wio-e5-mini/platformio.ini @@ -0,0 +1,32 @@ +[lora_e5_mini] +extends = stm32_base +board = lora_e5_mini +board_upload.maximum_size = 229376 ; 32kb for FS +build_flags = ${stm32_base.build_flags} + -D RADIO_CLASS=CustomSTM32WLx + -D WRAPPER_CLASS=CustomSTM32WLxWrapper + -D SPI_INTERFACES_COUNT=0 + -D RX_BOOSTED_GAIN=true + -I variants/wio-e5-mini +build_src_filter = ${stm32_base.build_src_filter} + +<../variants/wio-e5-mini> + +[env:wio-e5-mini-repeater] +extends = lora_e5_mini +build_flags = ${lora_e5_mini.build_flags} + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"wio-e5-mini Repeater"' + -D ADMIN_PASSWORD='"password"' +build_src_filter = ${lora_e5_mini.build_src_filter} + +<../examples/simple_repeater/main.cpp> + +[env:wio-e5-mini_companion_radio_usb] +extends = lora_e5_mini +build_flags = ${lora_e5_mini.build_flags} + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +build_src_filter = ${lora_e5_mini.build_src_filter} + +<../examples/companion_radio/*.cpp> +lib_deps = ${lora_e5_mini.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp new file mode 100644 index 00000000..8ccbe384 --- /dev/null +++ b/variants/wio-e5-mini/target.cpp @@ -0,0 +1,73 @@ +#include +#include "target.h" +#include + +WIOE5Board board; + +RADIO_CLASS radio = new STM32WLx_Module(); + +WRAPPER_CLASS radio_driver(radio, board); + +static const uint32_t rfswitch_pins[] = {PA4, PA5, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {STM32WLx::MODE_IDLE, {LOW, LOW}}, + {STM32WLx::MODE_RX, {HIGH, LOW}}, + {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, // for LoRa-E5 mini +// {STM32WLx::MODE_TX_LP, {HIGH, HIGH}}, // for LoRa-E5-LE mini + END_OF_MODE_TABLE, +}; + +VolatileRTCClock rtc_clock; +SensorManager sensors; + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { +// rtc_clock.begin(Wire); + +// #ifdef SX126X_DIO3_TCXO_VOLTAGE +// float tcxo = SX126X_DIO3_TCXO_VOLTAGE; +// #else +// float tcxo = 1.6f; +// #endif + + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, 1.7, 0); + + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + + #ifdef RX_BOOSTED_GAIN + radio.setRxBoostedGainMode(RX_BOOSTED_GAIN); + #endif + + radio.setCRC(1); + + 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-e5/target.h b/variants/wio-e5-mini/target.h similarity index 78% rename from variants/wio-e5/target.h rename to variants/wio-e5-mini/target.h index cc6131cf..f7a882ee 100644 --- a/variants/wio-e5/target.h +++ b/variants/wio-e5-mini/target.h @@ -11,7 +11,13 @@ class WIOE5Board : public STM32Board { public: const char* getManufacturerName() const override { - return "Seeed Wio E5"; + return "Seeed Wio E5 mini"; + } + + uint16_t getBattMilliVolts() override { + analogReadResolution(12); + uint32_t raw = analogRead(PIN_A3); + return raw; } }; diff --git a/variants/wio-e5/variant.h b/variants/wio-e5-mini/variant.h similarity index 50% rename from variants/wio-e5/variant.h rename to variants/wio-e5-mini/variant.h index 84b9cd16..79540c79 100644 --- a/variants/wio-e5/variant.h +++ b/variants/wio-e5-mini/variant.h @@ -1,9 +1,9 @@ #pragma once // UART Definitions -#ifndef SERIAL_UART_INSTANCE - #define SERIAL_UART_INSTANCE 101 -#endif +// #ifndef SERIAL_UART_INSTANCE +// #define SERIAL_UART_INSTANCE 101 +// #endif #include From c8877b3bc717b731caa36500f75e4f69f7d38b0f Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 31 May 2025 20:29:03 +0200 Subject: [PATCH 048/103] wio_e5 : bme280 support --- variants/wio-e5-mini/platformio.ini | 2 ++ variants/wio-e5-mini/target.cpp | 34 ++++++++++++++++++++++------- variants/wio-e5-mini/target.h | 15 ++++++++++++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 55ab0bc8..887304ea 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -10,6 +10,8 @@ build_flags = ${stm32_base.build_flags} -I variants/wio-e5-mini build_src_filter = ${stm32_base.build_src_filter} +<../variants/wio-e5-mini> +lib_deps = ${stm32_base.lib_deps} + finitespace/BME280 @ ^3.0.0 [env:wio-e5-mini-repeater] extends = lora_e5_mini diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index 8ccbe384..7a2b0d39 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,20 +18,15 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -SensorManager sensors; +BME280I2C bme; +WIOE5SensorManager sensors(bme); #ifndef LORA_CR #define LORA_CR 5 #endif bool radio_init() { -// rtc_clock.begin(Wire); - -// #ifdef SX126X_DIO3_TCXO_VOLTAGE -// float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -// #else -// float tcxo = 1.6f; -// #endif + Wire.begin(); radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); @@ -71,3 +66,26 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (!has_bme) return false; + + float temp(NAN), hum(NAN), pres(NAN); + + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + BME280::PresUnit presUnit(BME280::PresUnit_bar); + + _bme->read(pres, temp, hum, tempUnit, presUnit); + + telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, pres); + + return true; +} + +bool WIOE5SensorManager::begin() { + has_bme = _bme->begin(); + + return has_bme; +} \ No newline at end of file diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index f7a882ee..daf7e174 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -8,6 +8,9 @@ #include #include +#include +#include + class WIOE5Board : public STM32Board { public: const char* getManufacturerName() const override { @@ -21,10 +24,20 @@ public: } }; +class WIOE5SensorManager : public SensorManager { + BME280I2C* _bme; + bool has_bme = false; + +public: + WIOE5SensorManager(BME280I2C& bme) : _bme(&bme) {} + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; +}; + extern WIOE5Board board; extern WRAPPER_CLASS radio_driver; extern VolatileRTCClock rtc_clock; -extern SensorManager sensors; +extern WIOE5SensorManager sensors; bool radio_init(); uint32_t radio_get_rng_seed(); From 9cfeb6285f204d4bf9a781c89b955da778e35f69 Mon Sep 17 00:00:00 2001 From: JQ Date: Sat, 31 May 2025 15:22:59 -0700 Subject: [PATCH 049/103] better fonts for GxEPDDisplay --- src/helpers/ui/GxEPDDisplay.cpp | 28 +++++++++++++++++----------- src/helpers/ui/GxEPDDisplay.h | 4 +++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index d37a0981..875e29ac 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -5,22 +5,15 @@ #define DISPLAY_ROTATION 3 #endif -#ifdef TECHO_ZOOM - #define SCALE_X (1.5625f * 1.5f) // 200 / 128 (with 1.5 scale) - #define SCALE_Y (1.5625f * 1.5f) // 200 / 128 (with 1.5 scale) -#else - #define SCALE_X 1.5625f // 200 / 128 - #define SCALE_Y 1.5625f // 200 / 128 -#endif +#define SCALE_X 1.5625f // 200 / 128 +#define SCALE_Y 1.5625f // 200 / 128 bool GxEPDDisplay::begin() { display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); SPI1.begin(); display.init(115200, true, 2, false); display.setRotation(DISPLAY_ROTATION); - #ifdef TECHO_ZOOM - display.setFont(&FreeMono9pt7b); - #endif + setTextSize(1); // Default to size 1 display.setPartialWindow(0, 0, display.width(), display.height()); display.fillScreen(GxEPD_WHITE); @@ -57,7 +50,20 @@ void GxEPDDisplay::startFrame(Color bkg) { } void GxEPDDisplay::setTextSize(int sz) { - display.setTextSize(sz); + switch(sz) { + case 1: // Small + display.setFont(&FreeSans9pt7b); + break; + case 2: // Medium Bold + display.setFont(&FreeSansBold12pt7b); + break; + case 3: // Large + display.setFont(&FreeSans18pt7b); + break; + default: + display.setFont(&FreeSans9pt7b); + break; + } } void GxEPDDisplay::setColor(Color c) { diff --git a/src/helpers/ui/GxEPDDisplay.h b/src/helpers/ui/GxEPDDisplay.h index 01c900ac..ec2bcec0 100644 --- a/src/helpers/ui/GxEPDDisplay.h +++ b/src/helpers/ui/GxEPDDisplay.h @@ -9,7 +9,9 @@ #include #include #include -#include +#include +#include +#include #define GxEPD2_DISPLAY_CLASS GxEPD2_BW #define GxEPD2_DRIVER_CLASS GxEPD2_150_BN // DEPG0150BN 200x200, SSD1681, (FPC8101), TTGO T5 V2.4.1 From f58a34f5f4c201db63f3da9ce58d5b0f69c0fd6f Mon Sep 17 00:00:00 2001 From: hank Date: Thu, 29 May 2025 13:06:24 -0700 Subject: [PATCH 050/103] Refactored MyMesh, advert on doublepress Pulled the class out of main.cpp, made a header to go along with it, externed globals in headers to make them accessible to button code. Added button code to send an advert on double press. Refactored ini files to prevent linker errors. --- examples/companion_radio/MyMesh.cpp | 1819 ++++++++++++++++++++++++ examples/companion_radio/MyMesh.h | 283 ++++ examples/companion_radio/UITask.cpp | 11 +- examples/companion_radio/UITask.h | 4 +- examples/companion_radio/main.cpp | 1588 +-------------------- variants/generic_espnow/platformio.ini | 2 +- variants/promicro/platformio.ini | 4 +- variants/t114/platformio.ini | 4 +- variants/xiao_nrf52/platformio.ini | 4 +- variants/xiao_s3_wio/platformio.ini | 2 +- 10 files changed, 2135 insertions(+), 1586 deletions(-) create mode 100644 examples/companion_radio/MyMesh.cpp create mode 100644 examples/companion_radio/MyMesh.h diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp new file mode 100644 index 00000000..6e9688a5 --- /dev/null +++ b/examples/companion_radio/MyMesh.cpp @@ -0,0 +1,1819 @@ +#include // needed for PlatformIO +#include +#include "MyMesh.h" + +#ifdef DISPLAY_CLASS + #include "UITask.h" +#endif + +void MyMesh::loadMainIdentity() +{ + if (!_identity_store->load("_main", self_id)) + { + self_id = radio_new_identity(); // create new random identity + int count = 0; + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) + { // reserved id hashes + self_id = radio_new_identity(); + count++; + } + saveMainIdentity(self_id); + } +} + +bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) +{ + return _identity_store->save("_main", identity); +} + +void MyMesh::loadContacts() +{ + if (_fs->exists("/contacts3")) + { +#if defined(RP2040_PLATFORM) + File file = _fs->open("/contacts3", "r"); +#else + File file = _fs->open("/contacts3"); +#endif + if (file) + { + bool full = false; + while (!full) + { + ContactInfo c; + uint8_t pub_key[32]; + uint8_t unused; + + bool success = (file.read(pub_key, 32) == 32); + success = success && (file.read((uint8_t *)&c.name, 32) == 32); + success = success && (file.read(&c.type, 1) == 1); + success = success && (file.read(&c.flags, 1) == 1); + success = success && (file.read(&unused, 1) == 1); + success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved' + success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.read(c.out_path, 64) == 64); + success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4); + success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); + success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); + + if (!success) + break; // EOF + + c.id = mesh::Identity(pub_key); + if (!addContact(c)) + full = true; + } + file.close(); + } + } +} + +void MyMesh::saveContacts() +{ +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/contacts3"); + File file = _fs->open("/contacts3", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/contacts3", "w"); +#else + File file = _fs->open("/contacts3", "w", true); +#endif + if (file) + { + ContactsIterator iter; + ContactInfo c; + uint8_t unused = 0; + + while (iter.hasNext(this, c)) + { + bool success = (file.write(c.id.pub_key, 32) == 32); + success = success && (file.write((uint8_t *)&c.name, 32) == 32); + success = success && (file.write(&c.type, 1) == 1); + success = success && (file.write(&c.flags, 1) == 1); + success = success && (file.write(&unused, 1) == 1); + success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4); + success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.write(c.out_path, 64) == 64); + success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4); + success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); + success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); + + if (!success) + break; // write failed + } + file.close(); + } +} + +void MyMesh::loadChannels() +{ + if (_fs->exists("/channels2")) + { +#if defined(RP2040_PLATFORM) + File file = _fs->open("/channels2", "r"); +#else + File file = _fs->open("/channels2"); +#endif + if (file) + { + bool full = false; + uint8_t channel_idx = 0; + while (!full) + { + ChannelDetails ch; + uint8_t unused[4]; + + bool success = (file.read(unused, 4) == 4); + success = success && (file.read((uint8_t *)ch.name, 32) == 32); + success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); + + if (!success) + break; // EOF + + if (setChannel(channel_idx, ch)) + { + channel_idx++; + } + else + { + full = true; + } + } + file.close(); + } + } +} + +void MyMesh::saveChannels() +{ +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/channels2"); + File file = _fs->open("/channels2", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/channels2", "w"); +#else + File file = _fs->open("/channels2", "w", true); +#endif + if (file) + { + uint8_t channel_idx = 0; + ChannelDetails ch; + uint8_t unused[4]; + memset(unused, 0, 4); + + while (getChannel(channel_idx, ch)) + { + bool success = (file.write(unused, 4) == 4); + success = success && (file.write((uint8_t *)ch.name, 32) == 32); + success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); + + if (!success) + break; // write failed + channel_idx++; + } + file.close(); + } +} + +int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) +{ + char path[64]; + char fname[18]; + + if (key_len > 8) + key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + 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 + if (f) + { + int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! + f.close(); + return len; + } + } + return 0; // not found +} + +bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) +{ + char path[64]; + char fname[18]; + + if (key_len > 8) + key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + sprintf(path, "/bl/%s", fname); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(path); + File f = _fs->open(path, FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File f = _fs->open(path, "w"); +#else + File f = _fs->open(path, "w", true); +#endif + if (f) + { + int n = f.write(src_buf, len); + f.close(); + if (n == len) + return true; // success! + + _fs->remove(path); // blob was only partially written! + } + return false; // error +} + +void MyMesh::writeOKFrame() +{ + uint8_t buf[1]; + buf[0] = RESP_CODE_OK; + _serial->writeFrame(buf, 1); +} +void MyMesh::writeErrFrame(uint8_t err_code) +{ + uint8_t buf[2]; + buf[0] = RESP_CODE_ERR; + buf[1] = err_code; + _serial->writeFrame(buf, 2); +} + +void MyMesh::writeDisabledFrame() +{ + uint8_t buf[1]; + buf[0] = RESP_CODE_DISABLED; + _serial->writeFrame(buf, 1); +} + +void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) +{ + int i = 0; + out_frame[i++] = code; + memcpy(&out_frame[i], contact.id.pub_key, PUB_KEY_SIZE); + i += PUB_KEY_SIZE; + out_frame[i++] = contact.type; + out_frame[i++] = contact.flags; + out_frame[i++] = contact.out_path_len; + memcpy(&out_frame[i], contact.out_path, MAX_PATH_SIZE); + i += MAX_PATH_SIZE; + StrHelper::strzcpy((char *)&out_frame[i], contact.name, 32); + i += 32; + memcpy(&out_frame[i], &contact.last_advert_timestamp, 4); + i += 4; + memcpy(&out_frame[i], &contact.gps_lat, 4); + i += 4; + memcpy(&out_frame[i], &contact.gps_lon, 4); + i += 4; + memcpy(&out_frame[i], &contact.lastmod, 4); + i += 4; + _serial->writeFrame(out_frame, i); +} + +void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) +{ + int i = 0; + uint8_t code = frame[i++]; // eg. CMD_ADD_UPDATE_CONTACT + memcpy(contact.id.pub_key, &frame[i], PUB_KEY_SIZE); + i += PUB_KEY_SIZE; + contact.type = frame[i++]; + contact.flags = frame[i++]; + contact.out_path_len = frame[i++]; + memcpy(contact.out_path, &frame[i], MAX_PATH_SIZE); + i += MAX_PATH_SIZE; + memcpy(contact.name, &frame[i], 32); + i += 32; + memcpy(&contact.last_advert_timestamp, &frame[i], 4); + i += 4; + if (i + 8 >= len) + { // optional fields + memcpy(&contact.gps_lat, &frame[i], 4); + i += 4; + memcpy(&contact.gps_lon, &frame[i], 4); + i += 4; + } +} + +void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) +{ + if (offline_queue_len >= OFFLINE_QUEUE_SIZE) + { + MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); + } + else + { + offline_queue[offline_queue_len].len = len; + memcpy(offline_queue[offline_queue_len].buf, frame, len); + offline_queue_len++; + } +} +int MyMesh::getFromOfflineQueue(uint8_t frame[]) +{ + if (offline_queue_len > 0) + { // check offline queue + size_t len = offline_queue[0].len; // take from top of queue + memcpy(frame, offline_queue[0].buf, len); + + offline_queue_len--; + for (int i = 0; i < offline_queue_len; i++) + { // delete top item from queue + offline_queue[i] = offline_queue[i + 1]; + } + return len; + } + return 0; // queue is empty +} + +float MyMesh::getAirtimeBudgetFactor() const +{ + return _prefs.airtime_factor; +} + +int MyMesh::getInterferenceThreshold() const +{ + return 14; // hard-coded for now +} + +int MyMesh::calcRxDelay(float score, uint32_t air_time) const +{ + if (_prefs.rx_delay_base <= 0.0f) + return 0; + return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); +} + +void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) +{ + if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) + { + int i = 0; + out_frame[i++] = PUSH_CODE_LOG_RX_DATA; + out_frame[i++] = (int8_t)(snr * 4); + out_frame[i++] = (int8_t)(rssi); + memcpy(&out_frame[i], raw, len); + i += len; + + _serial->writeFrame(out_frame, i); + } +} + +bool MyMesh::isAutoAddEnabled() const +{ + return (_prefs.manual_add_contacts & 1) == 0; +} + +void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) +{ + if (_serial->isConnected()) + { + if (!isAutoAddEnabled() && is_new) + { + writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); + } + else + { + out_frame[0] = PUSH_CODE_ADVERT; + memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); + _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); + } + } + else + { +#ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::newContactMessage); +#endif + } + + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); +} + +void MyMesh::onContactPathUpdated(const ContactInfo &contact) +{ + out_frame[0] = PUSH_CODE_PATH_UPDATED; + memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); + _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected + + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); +} + +bool MyMesh::processAck(const uint8_t *data) +{ + // see if matches any in a table + for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) + { + if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) + { // got an ACK from recipient + out_frame[0] = PUSH_CODE_SEND_CONFIRMED; + memcpy(&out_frame[1], data, 4); + uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent; + memcpy(&out_frame[5], &trip_time, 4); + _serial->writeFrame(out_frame, 9); + + // NOTE: the same ACK can be received multiple times! + expected_ack_table[i].ack = 0; // clear expected hash, now that we have received ACK + return true; + } + } + return checkConnectionsAck(data); +} + +void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) +{ + int i = 0; + if (app_target_ver >= 3) + { + out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; + out_frame[i++] = (int8_t)(pkt->getSNR() * 4); + out_frame[i++] = 0; // reserved1 + out_frame[i++] = 0; // reserved2 + } + else + { + out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; + } + memcpy(&out_frame[i], from.id.pub_key, 6); + i += 6; // just 6-byte prefix + uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + out_frame[i++] = txt_type; + memcpy(&out_frame[i], &sender_timestamp, 4); + i += 4; + if (extra_len > 0) + { + memcpy(&out_frame[i], extra, extra_len); + i += extra_len; + } + int tlen = strlen(text); // TODO: UTF-8 ?? + if (i + tlen > MAX_FRAME_SIZE) + { + tlen = MAX_FRAME_SIZE - i; + } + memcpy(&out_frame[i], text, tlen); + i += tlen; + addToOfflineQueue(out_frame, i); + + if (_serial->isConnected()) + { + uint8_t frame[1]; + frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' + _serial->writeFrame(frame, 1); + } + else + { +#ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::contactMessage); +#endif + } +#ifdef DISPLAY_CLASS + ui_task.newMsg(path_len, from.name, text, offline_queue_len); +#endif +} + +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 + queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); +} + +void MyMesh::onCommandDataRecv(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 + queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); +} + +void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) +{ + markConnectionActive(from); + // from.sync_since change needs to be persisted + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text); +} + +void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, const char *text) +{ + int i = 0; + if (app_target_ver >= 3) + { + out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; + out_frame[i++] = (int8_t)(pkt->getSNR() * 4); + out_frame[i++] = 0; // reserved1 + out_frame[i++] = 0; // reserved2 + } + else + { + out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; + } + + out_frame[i++] = findChannelIdx(channel); + uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + + out_frame[i++] = TXT_TYPE_PLAIN; + memcpy(&out_frame[i], ×tamp, 4); + i += 4; + int tlen = strlen(text); // TODO: UTF-8 ?? + if (i + tlen > MAX_FRAME_SIZE) + { + tlen = MAX_FRAME_SIZE - i; + } + memcpy(&out_frame[i], text, tlen); + i += tlen; + addToOfflineQueue(out_frame, i); + + if (_serial->isConnected()) + { + uint8_t frame[1]; + frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' + _serial->writeFrame(frame, 1); + } + else + { +#ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::channelMessage); +#endif + } +#ifdef DISPLAY_CLASS + ui_task.newMsg(path_len, "Public", text, offline_queue_len); +#endif +} + +uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) +{ + if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) + { + uint8_t permissions = 0; + uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) + + if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) + { + permissions = TELEM_PERM_BASE; + } + else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) + { + permissions = cp & TELEM_PERM_BASE; + } + + if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) + { + permissions |= TELEM_PERM_LOCATION; + } + else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) + { + permissions |= cp & TELEM_PERM_LOCATION; + } + + if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) + { + permissions |= TELEM_PERM_ENVIRONMENT; + } + else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) + { + permissions |= cp & TELEM_PERM_ENVIRONMENT; + } + + if (permissions & TELEM_PERM_BASE) + { // only respond if base permission bit is set + telemetry.reset(); + telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); + // query other sensors -- target specific + sensors.querySensors(permissions, telemetry); + + memcpy(reply, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') + + uint8_t tlen = telemetry.getSize(); + memcpy(&reply[4], telemetry.getBuffer(), tlen); + return 4 + tlen; + } + } + return 0; // unknown +} + +void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) +{ + uint32_t tag; + memcpy(&tag, data, 4); + + if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) + { // check for login response + // yes, is response to pending sendLogin() + pending_login = 0; + + int i = 0; + if (memcmp(&data[4], "OK", 2) == 0) + { // legacy Repeater login OK response + out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; + out_frame[i++] = 0; // legacy: is_admin = false + memcpy(&out_frame[i], contact.id.pub_key, 6); + i += 6; // pub_key_prefix + } + else if (data[4] == RESP_SERVER_LOGIN_OK) + { // new login response + uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; + if (keep_alive_secs > 0) + { + startConnection(contact, keep_alive_secs); + } + out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; + out_frame[i++] = data[6]; // permissions (eg. is_admin) + memcpy(&out_frame[i], contact.id.pub_key, 6); + i += 6; // pub_key_prefix + memcpy(&out_frame[i], &tag, 4); + i += 4; // NEW: include server timestamp + } + else + { + out_frame[i++] = PUSH_CODE_LOGIN_FAIL; + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); + i += 6; // pub_key_prefix + } + _serial->writeFrame(out_frame, i); + } + else if (len > 4 && // check for status response + pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme + // FUTURE: tag == pending_status + ) + { + pending_status = 0; + + int i = 0; + out_frame[i++] = PUSH_CODE_STATUS_RESPONSE; + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); + i += 6; // pub_key_prefix + memcpy(&out_frame[i], &data[4], len - 4); + i += (len - 4); + _serial->writeFrame(out_frame, i); + } + else if (len > 4 && tag == pending_telemetry) + { // check for telemetry response + pending_telemetry = 0; + + int i = 0; + out_frame[i++] = PUSH_CODE_TELEMETRY_RESPONSE; + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); + i += 6; // pub_key_prefix + memcpy(&out_frame[i], &data[4], len - 4); + i += (len - 4); + _serial->writeFrame(out_frame, i); + } +} + +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); + return; + } + int i = 0; + out_frame[i++] = PUSH_CODE_RAW_DATA; + out_frame[i++] = (int8_t)(_radio->getLastSNR() * 4); + out_frame[i++] = (int8_t)(_radio->getLastRSSI()); + out_frame[i++] = 0xFF; // reserved (possibly path_len in future) + 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("onRawDataRecv(), data received while app offline"); + } +} + +void MyMesh::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) +{ + int i = 0; + out_frame[i++] = PUSH_CODE_TRACE_DATA; + out_frame[i++] = 0; // reserved + out_frame[i++] = path_len; + out_frame[i++] = flags; + memcpy(&out_frame[i], &tag, 4); + i += 4; + memcpy(&out_frame[i], &auth_code, 4); + i += 4; + memcpy(&out_frame[i], path_hashes, path_len); + i += path_len; + memcpy(&out_frame[i], path_snrs, path_len); + i += path_len; + out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node) + + if (_serial->isConnected()) + { + _serial->writeFrame(out_frame, i); + } + else + { + MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); + } +} + +uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const +{ + return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); +} +uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const +{ + return SEND_TIMEOUT_BASE_MILLIS + + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); +} + +void MyMesh::onSendTimeout() +{ +} + +MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) + : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), + telemetry(MAX_PACKET_PAYLOAD - 4) +{ + _iter_started = false; + offline_queue_len = 0; + app_target_ver = 0; + _identity_store = NULL; + pending_login = pending_status = pending_telemetry = 0; + next_ack_idx = 0; + sign_data = NULL; + dirty_contacts_expiry = 0; + + // defaults + memset(&_prefs, 0, sizeof(_prefs)); + _prefs.airtime_factor = 1.0; // one half + strcpy(_prefs.node_name, "NONAME"); + _prefs.freq = LORA_FREQ; + _prefs.sf = LORA_SF; + _prefs.bw = LORA_BW; + _prefs.cr = LORA_CR; + _prefs.tx_power_dbm = LORA_TX_POWER; + //_prefs.rx_delay_base = 10.0f; enable once new algo fixed +} + +void MyMesh::loadPrefsInt(const char *filename) +{ +#if defined(RP2040_PLATFORM) + File file = _fs->open(filename, "r"); +#else + File file = _fs->open(filename); +#endif + if (file) + { + uint8_t pad[8]; + + file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 + file.read((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 + file.read(pad, 4); // 36 + file.read((uint8_t *)&sensors.node_lat, sizeof(sensors.node_lat)); // 40 + file.read((uint8_t *)&sensors.node_lon, sizeof(sensors.node_lon)); // 48 + file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 + file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 + file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 + file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 + file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 + file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 + file.read((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 + file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 + file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 + file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 + file.read(pad, 4); // 76 + file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 + + // sanitise bad pref values + _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.cr = constrain(_prefs.cr, 5, 8); + _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); + + file.close(); + } +} + +void MyMesh::begin(FILESYSTEM &fs, bool has_display) +{ + _fs = &fs; + + BaseChatMesh::begin(); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _identity_store = new IdentityStore(fs, ""); +#elif defined(RP2040_PLATFORM) + _identity_store = new IdentityStore(fs, "/identity"); + _identity_store->begin(); +#else + _identity_store = new IdentityStore(fs, "/identity"); +#endif + + loadMainIdentity(); + + // use hex of first 4 bytes of identity public key as default node name + char pub_key_hex[10]; + mesh::Utils::toHex(pub_key_hex, self_id.pub_key, 4); + strcpy(_prefs.node_name, pub_key_hex); + +// if name is provided as a build flag, use that as default node name instead +#ifdef ADVERT_NAME + strcpy(_prefs.node_name, ADVERT_NAME); +#endif + + // load persisted prefs + if (_fs->exists("/new_prefs")) + { + loadPrefsInt("/new_prefs"); // new filename + } + else if (_fs->exists("/node_prefs")) + { + loadPrefsInt("/node_prefs"); + savePrefs(); // save to new filename + _fs->remove("/node_prefs"); // remove old + } + +#ifdef BLE_PIN_CODE + if (_prefs.ble_pin == 0) + { +#ifdef DISPLAY_CLASS + if (has_display) + { + StdRNG rng; + _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session + } + else + { + _active_ble_pin = BLE_PIN_CODE; // otherwise static pin + } +#else + _active_ble_pin = BLE_PIN_CODE; // otherwise static pin +#endif + } + else + { + _active_ble_pin = _prefs.ble_pin; + } +#else + _active_ble_pin = 0; +#endif + + // init 'blob store' support + _fs->mkdir("/bl"); + + loadContacts(); + addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel + loadChannels(); + + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + radio_set_tx_power(_prefs.tx_power_dbm); +} + +const char *MyMesh::getNodeName() { return _prefs.node_name; } +NodePrefs *MyMesh::getNodePrefs() +{ + return &_prefs; +} +uint32_t MyMesh::getBLEPin() { return _active_ble_pin; } + +void MyMesh::startInterface(BaseSerialInterface &serial) +{ + _serial = &serial; + serial.enable(); +} + +void MyMesh::savePrefs() +{ +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/new_prefs"); + File file = _fs->open("/new_prefs", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/new_prefs", "w"); +#else + File file = _fs->open("/new_prefs", "w", true); +#endif + if (file) + { + uint8_t pad[8]; + memset(pad, 0, sizeof(pad)); + + file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 + file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 + file.write(pad, 4); // 36 + file.write((uint8_t *)&sensors.node_lat, sizeof(sensors.node_lat)); // 40 + file.write((uint8_t *)&sensors.node_lon, sizeof(sensors.node_lon)); // 48 + file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 + file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 + file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 + file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 + file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 + file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 + file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 + file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 + file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 + file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 + file.write(pad, 4); // 76 + file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 + + file.close(); + } +} + +void MyMesh::handleCmdFrame(size_t len) +{ + if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) + { // sent when app establishes connection + app_target_ver = cmd_frame[1]; // which version of protocol does app understand + + int i = 0; + out_frame[i++] = RESP_CODE_DEVICE_INFO; + out_frame[i++] = FIRMWARE_VER_CODE; + out_frame[i++] = MAX_CONTACTS / 2; // v3+ + out_frame[i++] = MAX_GROUP_CHANNELS; // v3+ + memcpy(&out_frame[i], &_prefs.ble_pin, 4); + i += 4; + memset(&out_frame[i], 0, 12); + strcpy((char *)&out_frame[i], FIRMWARE_BUILD_DATE); + i += 12; + StrHelper::strzcpy((char *)&out_frame[i], board.getManufacturerName(), 40); + i += 40; + StrHelper::strzcpy((char *)&out_frame[i], FIRMWARE_VERSION, 20); + i += 20; + _serial->writeFrame(out_frame, i); + } + else if (cmd_frame[0] == CMD_APP_START && len >= 8) + { // sent when app establishes connection, respond with node ID + // cmd_frame[1..7] reserved future + char *app_name = (char *)&cmd_frame[8]; + cmd_frame[len] = 0; // make app_name null terminated + MESH_DEBUG_PRINTLN("App %s connected", app_name); + + _iter_started = false; // stop any left-over ContactsIterator + int i = 0; + out_frame[i++] = RESP_CODE_SELF_INFO; + out_frame[i++] = ADV_TYPE_CHAT; // what this node Advert identifies as (maybe node's pronouns too?? :-) + out_frame[i++] = _prefs.tx_power_dbm; + out_frame[i++] = MAX_LORA_TX_POWER; + memcpy(&out_frame[i], self_id.pub_key, PUB_KEY_SIZE); + i += PUB_KEY_SIZE; + + int32_t lat, lon; + lat = (sensors.node_lat * 1000000.0); + lon = (sensors.node_lon * 1000000.0); + memcpy(&out_frame[i], &lat, 4); + i += 4; + memcpy(&out_frame[i], &lon, 4); + i += 4; + out_frame[i++] = 0; // reserved + out_frame[i++] = 0; // reserved + out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ + out_frame[i++] = _prefs.manual_add_contacts; + + uint32_t freq = _prefs.freq * 1000; + memcpy(&out_frame[i], &freq, 4); + i += 4; + uint32_t bw = _prefs.bw * 1000; + memcpy(&out_frame[i], &bw, 4); + i += 4; + out_frame[i++] = _prefs.sf; + out_frame[i++] = _prefs.cr; + + int tlen = strlen(_prefs.node_name); // revisit: UTF_8 ?? + memcpy(&out_frame[i], _prefs.node_name, tlen); + i += tlen; + _serial->writeFrame(out_frame, i); + } + else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) + { + int i = 1; + uint8_t txt_type = cmd_frame[i++]; + uint8_t attempt = cmd_frame[i++]; + uint32_t msg_timestamp; + memcpy(&msg_timestamp, &cmd_frame[i], 4); + i += 4; + uint8_t *pub_key_prefix = &cmd_frame[i]; + i += 6; + ContactInfo *recipient = lookupContactByPubKey(pub_key_prefix, 6); + if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) + { + char *text = (char *)&cmd_frame[i]; + int tlen = len - i; + uint32_t est_timeout; + text[tlen] = 0; // ensure null + int result; + uint32_t expected_ack; + if (txt_type == TXT_TYPE_CLI_DATA) + { + result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); + expected_ack = 0; // no Ack expected + } + else + { + result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); + } + // TODO: add expected ACK to table + if (result == MSG_SEND_FAILED) + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + else + { + if (expected_ack) + { + expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table + expected_ack_table[next_ack_idx].ack = expected_ack; + next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; + } + + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &expected_ack, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } + else + { + writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* + } + } + else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) + { // send GroupChannel msg + int i = 1; + uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN + uint8_t channel_idx = cmd_frame[i++]; + uint32_t msg_timestamp; + memcpy(&msg_timestamp, &cmd_frame[i], 4); + i += 4; + const char *text = (char *)&cmd_frame[i]; + + if (txt_type != TXT_TYPE_PLAIN) + { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } + else + { + ChannelDetails channel; + bool success = getChannel(channel_idx, channel); + if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) + { + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx + } + } + } + else if (cmd_frame[0] == CMD_GET_CONTACTS) + { // get Contact list + if (_iter_started) + { + writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy + } + else + { + if (len >= 5) + { // has optional 'since' param + memcpy(&_iter_filter_since, &cmd_frame[1], 4); + } + else + { + _iter_filter_since = 0; + } + + uint8_t reply[5]; + reply[0] = RESP_CODE_CONTACTS_START; + uint32_t count = getNumContacts(); // total, NOT filtered count + memcpy(&reply[1], &count, 4); + _serial->writeFrame(reply, 5); + + // start iterator + _iter = startContactsIterator(); + _iter_started = true; + _most_recent_lastmod = 0; + } + } + else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) + { + int nlen = len - 1; + if (nlen > sizeof(_prefs.node_name) - 1) + nlen = sizeof(_prefs.node_name) - 1; // max len + memcpy(_prefs.node_name, &cmd_frame[1], nlen); + _prefs.node_name[nlen] = 0; // null terminator + savePrefs(); + writeOKFrame(); + } + else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) + { + int32_t lat, lon, alt = 0; + memcpy(&lat, &cmd_frame[1], 4); + memcpy(&lon, &cmd_frame[5], 4); + if (len >= 13) + { + memcpy(&alt, &cmd_frame[9], 4); // for FUTURE support + } + if (lat <= 90 * 1E6 && lat >= -90 * 1E6 && lon <= 180 * 1E6 && lon >= -180 * 1E6) + { + sensors.node_lat = ((double)lat) / 1000000.0; + sensors.node_lon = ((double)lon) / 1000000.0; + savePrefs(); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate + } + } + else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) + { + uint8_t reply[5]; + reply[0] = RESP_CODE_CURR_TIME; + uint32_t now = getRTCClock()->getCurrentTime(); + memcpy(&reply[1], &now, 4); + _serial->writeFrame(reply, 5); + } + else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) + { + uint32_t secs; + memcpy(&secs, &cmd_frame[1], 4); + uint32_t curr = getRTCClock()->getCurrentTime(); + if (secs >= curr) + { + getRTCClock()->setCurrentTime(secs); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) + { + auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); + if (pkt) + { + if (len >= 2 && cmd_frame[1] == 1) + { // optional param (1 = flood, 0 = zero hop) + sendFlood(pkt); + } + else + { + sendZeroHop(pkt); + } + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + } + else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) + { + recipient->out_path_len = -1; + // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact + } + } + else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) + { + updateContactFromFrame(*recipient, cmd_frame, len); + // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + writeOKFrame(); + } + else + { + ContactInfo contact; + updateContactFromFrame(contact, cmd_frame, len); + contact.lastmod = getRTCClock()->getCurrentTime(); + contact.sync_since = 0; + if (addContact(contact)) + { + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + } + } + else if (cmd_frame[0] == CMD_REMOVE_CONTACT) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient && removeContact(*recipient)) + { + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove + } + } + else if (cmd_frame[0] == CMD_SHARE_CONTACT) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) + { + if (shareContactZeroHop(*recipient)) + { + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send + } + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); + } + } + else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (contact) + { + writeContactRespFrame(RESP_CODE_CONTACT, *contact); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // not found + } + } + else if (cmd_frame[0] == CMD_EXPORT_CONTACT) + { + if (len < 1 + PUB_KEY_SIZE) + { + // export SELF + auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); + if (pkt) + { + pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode + + out_frame[0] = RESP_CODE_EXPORT_CONTACT; + uint8_t out_len = pkt->writeTo(&out_frame[1]); + releasePacket(pkt); // undo the obtainNewPacket() + _serial->writeFrame(out_frame, out_len + 1); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); // Error + } + } + else + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + uint8_t out_len; + if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) + { + out_frame[0] = RESP_CODE_EXPORT_CONTACT; + _serial->writeFrame(out_frame, out_len + 1); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // not found + } + } + } + else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) + { + if (importContact(&cmd_frame[1], len - 1)) + { + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) + { + int out_len; + if ((out_len = getFromOfflineQueue(out_frame)) > 0) + { + _serial->writeFrame(out_frame, out_len); +#ifdef DISPLAY_CLASS + ui_task.msgRead(offline_queue_len); +#endif + } + else + { + out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; + _serial->writeFrame(out_frame, 1); + } + } + else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) + { + int i = 1; + uint32_t freq; + memcpy(&freq, &cmd_frame[i], 4); + i += 4; + uint32_t bw; + memcpy(&bw, &cmd_frame[i], 4); + i += 4; + uint8_t sf = cmd_frame[i++]; + uint8_t cr = cmd_frame[i++]; + + if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) + { + _prefs.sf = sf; + _prefs.cr = cr; + _prefs.freq = (float)freq / 1000.0; + _prefs.bw = (float)bw / 1000.0; + savePrefs(); + + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + + writeOKFrame(); + } + else + { + MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) + { + if (cmd_frame[1] > MAX_LORA_TX_POWER) + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + else + { + _prefs.tx_power_dbm = cmd_frame[1]; + savePrefs(); + radio_set_tx_power(_prefs.tx_power_dbm); + writeOKFrame(); + } + } + else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) + { + int i = 1; + uint32_t rx, af; + memcpy(&rx, &cmd_frame[i], 4); + i += 4; + memcpy(&af, &cmd_frame[i], 4); + i += 4; + _prefs.rx_delay_base = ((float)rx) / 1000.0f; + _prefs.airtime_factor = ((float)af) / 1000.0f; + savePrefs(); + writeOKFrame(); + } + else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) + { + _prefs.manual_add_contacts = cmd_frame[1]; + if (len >= 3) + { + _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ + _prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03; + _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03; + } + savePrefs(); + writeOKFrame(); + } + else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) + { + if (dirty_contacts_expiry) + { // is there are pending dirty contacts write needed? + saveContacts(); + } + board.reboot(); + } + else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) + { + uint8_t reply[3]; + reply[0] = RESP_CODE_BATTERY_VOLTAGE; + uint16_t battery_millivolts = board.getBattMilliVolts(); + memcpy(&reply[1], &battery_millivolts, 2); + _serial->writeFrame(reply, 3); + } + else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) + { +#if ENABLE_PRIVATE_KEY_EXPORT + uint8_t reply[65]; + reply[0] = RESP_CODE_PRIVATE_KEY; + self_id.writeTo(&reply[1], 64); + _serial->writeFrame(reply, 65); +#else + writeDisabledFrame(); +#endif + } + else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) + { +#if ENABLE_PRIVATE_KEY_IMPORT + mesh::LocalIdentity identity; + identity.readFrom(&cmd_frame[1], 64); + if (saveMainIdentity(identity)) + { + self_id = identity; + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_FILE_IO_ERROR); + } +#else + writeDisabledFrame(); +#endif + } + else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) + { + int i = 1; + int8_t path_len = cmd_frame[i++]; + if (path_len >= 0 && i + path_len + 4 <= len) + { // minimum 4 byte payload + uint8_t *path = &cmd_frame[i]; + i += path_len; + auto pkt = createRawData(&cmd_frame[i], len - i); + if (pkt) + { + sendDirect(pkt, path, path_len); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + } + else + { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) + } + } + else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + char *password = (char *)&cmd_frame[1 + PUB_KEY_SIZE]; + cmd_frame[len] = 0; // ensure null terminator in password + if (recipient) + { + uint32_t est_timeout; + int result = sendLogin(*recipient, password, est_timeout); + if (result == MSG_SEND_FAILED) + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + else + { + pending_telemetry = pending_status = 0; + memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &pending_login, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found + } + } + else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) + { + uint8_t *pub_key = &cmd_frame[1]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) + { + uint32_t tag, est_timeout; + int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); + if (result == MSG_SEND_FAILED) + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + else + { + pending_telemetry = pending_login = 0; + // FUTURE: pending_status = tag; // match this in onContactResponse() + memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &tag, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found + } + } + else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) + { + uint8_t *pub_key = &cmd_frame[4]; + ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) + { + uint32_t tag, est_timeout; + int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); + if (result == MSG_SEND_FAILED) + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + else + { + pending_status = pending_login = 0; + pending_telemetry = tag; // match this in onContactResponse() + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &tag, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found + } + } + else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) + { + uint8_t *pub_key = &cmd_frame[1]; + if (hasConnectionTo(pub_key)) + { + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); + } + } + else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) + { + uint8_t *pub_key = &cmd_frame[1]; + stopConnection(pub_key); + writeOKFrame(); + } + else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) + { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + if (getChannel(channel_idx, channel)) + { + int i = 0; + out_frame[i++] = RESP_CODE_CHANNEL_INFO; + out_frame[i++] = channel_idx; + strcpy((char *)&out_frame[i], channel.name); + i += 32; + memcpy(&out_frame[i], channel.channel.secret, 16); + i += 16; // NOTE: only 128-bit supported + _serial->writeFrame(out_frame, i); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); + } + } + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) + { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) + } + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) + { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32); + memset(channel.channel.secret, 0, sizeof(channel.channel.secret)); + memcpy(channel.channel.secret, &cmd_frame[2 + 32], 16); // NOTE: only 128-bit supported + if (setChannel(channel_idx, channel)) + { + saveChannels(); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx + } + } + else if (cmd_frame[0] == CMD_SIGN_START) + { + out_frame[0] = RESP_CODE_SIGN_START; + out_frame[1] = 0; // reserved + uint32_t len = MAX_SIGN_DATA_LEN; + memcpy(&out_frame[2], &len, 4); + _serial->writeFrame(out_frame, 6); + + if (sign_data) + { + free(sign_data); + } + sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); + sign_data_len = 0; + } + else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) + { + if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) + { + writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long + } + else + { + memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); + sign_data_len += (len - 1); + writeOKFrame(); + } + } + else if (cmd_frame[0] == CMD_SIGN_FINISH) + { + if (sign_data) + { + self_id.sign(&out_frame[1], sign_data, sign_data_len); + + free(sign_data); // don't need sign_data now + sign_data = NULL; + + out_frame[0] = RESP_CODE_SIGNATURE; + _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); + } + else + { + writeErrFrame(ERR_CODE_BAD_STATE); + } + } + else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) + { + uint32_t tag, auth; + memcpy(&tag, &cmd_frame[1], 4); + memcpy(&auth, &cmd_frame[5], 4); + auto pkt = createTrace(tag, auth, cmd_frame[9]); + if (pkt) + { + uint8_t path_len = len - 10; + sendDirect(pkt, &cmd_frame[10], path_len); + + uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2); + uint32_t est_timeout = calcDirectTimeoutMillisFor(t, path_len); + + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = 0; + memcpy(&out_frame[2], &tag, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); + } + } + else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) + { + + // get pin from command frame + uint32_t pin; + memcpy(&pin, &cmd_frame[1], 4); + + // ensure pin is zero, or a valid 6 digit pin + if (pin == 0 || (pin >= 100000 && pin <= 999999)) + { + _prefs.ble_pin = pin; + savePrefs(); + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) + { + out_frame[0] = RESP_CODE_CUSTOM_VARS; + char *dp = (char *)&out_frame[1]; + for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) + { + if (i > 0) + { + *dp++ = ','; + } + strcpy(dp, sensors.getSettingName(i)); + dp = strchr(dp, 0); + *dp++ = ':'; + strcpy(dp, sensors.getSettingValue(i)); + dp = strchr(dp, 0); + } + _serial->writeFrame(out_frame, dp - (char *)out_frame); + } + else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) + { + cmd_frame[len] = 0; + char *sp = (char *)&cmd_frame[1]; + char *np = strchr(sp, ':'); // look for separator char + if (np) + { + *np++ = 0; // modify 'cmd_frame', replace ':' with null + bool success = sensors.setSettingValue(sp, np); + if (success) + { + writeOKFrame(); + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else + { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } + } + else + { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); + } +} + +void MyMesh::loop() +{ + BaseChatMesh::loop(); + + size_t len = _serial->checkRecvFrame(cmd_frame); + if (len > 0) + { + handleCmdFrame(len); + } + else if (_iter_started // check if our ContactsIterator is 'running' + && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! + ) + { + ContactInfo contact; + if (_iter.hasNext(this, contact)) + { + if (contact.lastmod > _iter_filter_since) + { // apply the 'since' filter + writeContactRespFrame(RESP_CODE_CONTACT, contact); + if (contact.lastmod > _most_recent_lastmod) + { + _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame + } + } + } + else + { // EOF + out_frame[0] = RESP_CODE_END_OF_CONTACTS; + memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' + _serial->writeFrame(out_frame, 5); + _iter_started = false; + } + } + else if (!_serial->isWriteBusy()) + { + checkConnections(); + } + + // is there are pending dirty contacts write needed? + if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) + { + saveContacts(); + dirty_contacts_expiry = 0; + } + +#ifdef DISPLAY_CLASS + ui_task.setHasConnection(_serial->isConnected()); + ui_task.loop(); +#endif +} + +bool MyMesh::advert() +{ + auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); + if (pkt) + { + sendZeroHop(pkt); + writeOKFrame(); + return true; + } + else + { + writeErrFrame(ERR_CODE_TABLE_FULL); + return false; + } +} \ No newline at end of file diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h new file mode 100644 index 00000000..a4794f1f --- /dev/null +++ b/examples/companion_radio/MyMesh.h @@ -0,0 +1,283 @@ +#ifndef MYMESH_H +#define MYMESH_H + +#include +#include +#ifdef DISPLAY_CLASS + #include "UITask.h" +#endif + +/*------------ Frame Protocol --------------*/ +#define FIRMWARE_VER_CODE 5 + +#ifndef FIRMWARE_BUILD_DATE + #define FIRMWARE_BUILD_DATE "24 May 2025" +#endif + +#ifndef FIRMWARE_VERSION + #define FIRMWARE_VERSION "v1.6.2" +#endif + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) +#include +#elif defined(RP2040_PLATFORM) +#include +#elif defined(ESP32) +#include +#endif + +#include +#include +#include +#include +#include +#include "NodePrefs.h" +#include +#include + +/* ---------------------------------- CONFIGURATION ------------------------------------- */ + +#ifndef LORA_FREQ + #define LORA_FREQ 915.0 +#endif +#ifndef LORA_BW + #define LORA_BW 250 +#endif +#ifndef LORA_SF + #define LORA_SF 10 +#endif +#ifndef LORA_CR + #define LORA_CR 5 +#endif +#ifndef LORA_TX_POWER + #define LORA_TX_POWER 20 +#endif +#ifndef MAX_LORA_TX_POWER + #define MAX_LORA_TX_POWER LORA_TX_POWER +#endif + +#ifndef MAX_CONTACTS + #define MAX_CONTACTS 100 +#endif + +#ifndef OFFLINE_QUEUE_SIZE + #define OFFLINE_QUEUE_SIZE 16 +#endif + +#ifndef BLE_NAME_PREFIX + #define BLE_NAME_PREFIX "MeshCore-" +#endif + +#include + +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define LAZY_CONTACTS_WRITE_DELAY 5000 + +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" + +#define CMD_APP_START 1 +#define CMD_SEND_TXT_MSG 2 +#define CMD_SEND_CHANNEL_TXT_MSG 3 +#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) +#define CMD_GET_DEVICE_TIME 5 +#define CMD_SET_DEVICE_TIME 6 +#define CMD_SEND_SELF_ADVERT 7 +#define CMD_SET_ADVERT_NAME 8 +#define CMD_ADD_UPDATE_CONTACT 9 +#define CMD_SYNC_NEXT_MESSAGE 10 +#define CMD_SET_RADIO_PARAMS 11 +#define CMD_SET_RADIO_TX_POWER 12 +#define CMD_RESET_PATH 13 +#define CMD_SET_ADVERT_LATLON 14 +#define CMD_REMOVE_CONTACT 15 +#define CMD_SHARE_CONTACT 16 +#define CMD_EXPORT_CONTACT 17 +#define CMD_IMPORT_CONTACT 18 +#define CMD_REBOOT 19 +#define CMD_GET_BATTERY_VOLTAGE 20 +#define CMD_SET_TUNING_PARAMS 21 +#define CMD_DEVICE_QEURY 22 +#define CMD_EXPORT_PRIVATE_KEY 23 +#define CMD_IMPORT_PRIVATE_KEY 24 +#define CMD_SEND_RAW_DATA 25 +#define CMD_SEND_LOGIN 26 +#define CMD_SEND_STATUS_REQ 27 +#define CMD_HAS_CONNECTION 28 +#define CMD_LOGOUT 29 // 'Disconnect' +#define CMD_GET_CONTACT_BY_KEY 30 +#define CMD_GET_CHANNEL 31 +#define CMD_SET_CHANNEL 32 +#define CMD_SIGN_START 33 +#define CMD_SIGN_DATA 34 +#define CMD_SIGN_FINISH 35 +#define CMD_SEND_TRACE_PATH 36 +#define CMD_SET_DEVICE_PIN 37 +#define CMD_SET_OTHER_PARAMS 38 +#define CMD_SEND_TELEMETRY_REQ 39 +#define CMD_GET_CUSTOM_VARS 40 +#define CMD_SET_CUSTOM_VAR 41 + +#define RESP_CODE_OK 0 +#define RESP_CODE_ERR 1 +#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS +#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) +#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS +#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START +#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG +#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME +#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE +#define RESP_CODE_EXPORT_CONTACT 11 +#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE +#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY +#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY +#define RESP_CODE_DISABLED 15 +#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL +#define RESP_CODE_SIGN_START 19 +#define RESP_CODE_SIGNATURE 20 +#define RESP_CODE_CUSTOM_VARS 21 + +// these are _pushed_ to client app at any time +#define PUSH_CODE_ADVERT 0x80 +#define PUSH_CODE_PATH_UPDATED 0x81 +#define PUSH_CODE_SEND_CONFIRMED 0x82 +#define PUSH_CODE_MSG_WAITING 0x83 +#define PUSH_CODE_RAW_DATA 0x84 +#define PUSH_CODE_LOGIN_SUCCESS 0x85 +#define PUSH_CODE_LOGIN_FAIL 0x86 +#define PUSH_CODE_STATUS_RESPONSE 0x87 +#define PUSH_CODE_LOG_RX_DATA 0x88 +#define PUSH_CODE_TRACE_DATA 0x89 +#define PUSH_CODE_NEW_ADVERT 0x8A +#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B + +#define ERR_CODE_UNSUPPORTED_CMD 1 +#define ERR_CODE_NOT_FOUND 2 +#define ERR_CODE_TABLE_FULL 3 +#define ERR_CODE_BAD_STATE 4 +#define ERR_CODE_FILE_IO_ERROR 5 +#define ERR_CODE_ILLEGAL_ARG 6 + +/* -------------------------------------------------------------------------------------- */ + +#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS +#define REQ_TYPE_KEEP_ALIVE 0x02 +#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 + +#define MAX_SIGN_DATA_LEN (8*1024) // 8K + +class MyMesh : public BaseChatMesh { +public: + MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables); + + void begin(FILESYSTEM& fs, bool has_display); + void startInterface(BaseSerialInterface& serial); + void loadPrefsInt(const char* filename); + void savePrefs(); + + const char* getNodeName(); + NodePrefs* getNodePrefs(); + uint32_t getBLEPin(); + + void loop(); + void handleCmdFrame(size_t len); + bool advert(); + +protected: + float getAirtimeBudgetFactor() const override; + int getInterferenceThreshold() const override; + int calcRxDelay(float score, uint32_t air_time) const override; + + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; + bool isAutoAddEnabled() const override; + void onDiscoveredContact(ContactInfo& contact, bool is_new) override; + void onContactPathUpdated(const ContactInfo& contact) override; + bool processAck(const uint8_t *data) override; + void queueMessage(const ContactInfo& from, uint8_t txt_type, mesh::Packet* pkt, + uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text); + + void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; + void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; + void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) override; + void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override; + + 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 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; + + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; + uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; + void onSendTimeout() override; + +private: + void writeOKFrame(); + void writeErrFrame(uint8_t err_code); + void writeDisabledFrame(); + void writeContactRespFrame(uint8_t code, const ContactInfo& contact); + void updateContactFromFrame(ContactInfo& contact, const uint8_t* frame, int len); + void addToOfflineQueue(const uint8_t frame[], int len); + int getFromOfflineQueue(uint8_t frame[]); + void loadMainIdentity(); + bool saveMainIdentity(const mesh::LocalIdentity& identity); + void loadContacts(); + void saveContacts(); + void loadChannels(); + void saveChannels(); + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + +private: + FILESYSTEM* _fs; + IdentityStore* _identity_store; + NodePrefs _prefs; + uint32_t pending_login; + uint32_t pending_status; + uint32_t pending_telemetry; + BaseSerialInterface* _serial; + + ContactsIterator _iter; + uint32_t _iter_filter_since; + uint32_t _most_recent_lastmod; + uint32_t _active_ble_pin; + bool _iter_started; + uint8_t app_target_ver; + uint8_t* sign_data; + uint32_t sign_data_len; + unsigned long dirty_contacts_expiry; + + uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; + uint8_t out_frame[MAX_FRAME_SIZE + 1]; + CayenneLPP telemetry; + + struct Frame { + uint8_t len; + uint8_t buf[MAX_FRAME_SIZE]; + }; + int offline_queue_len; + Frame offline_queue[OFFLINE_QUEUE_SIZE]; + + struct AckTableEntry { + unsigned long msg_sent; + uint32_t ack; + }; + #define EXPECTED_ACK_TABLE_SIZE 8 + AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table + int next_ack_idx; +}; + +extern StdRNG fast_rng; +extern SimpleMeshTables tables; +extern MyMesh the_mesh; +#ifdef DISPLAY_CLASS + extern UITask ui_task; +#endif +#endif // MYMESH_H \ No newline at end of file diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 5ff5f140..eabfd8f7 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -2,6 +2,7 @@ #include #include #include "NodePrefs.h" +#include "MyMesh.h" #define AUTO_OFF_MILLIS 15000 // 15 seconds #define BOOT_SCREEN_MILLIS 4000 // 4 seconds @@ -340,8 +341,14 @@ void UITask::handleButtonShortPress() { } void UITask::handleButtonDoublePress() { - MESH_DEBUG_PRINTLN("UITask: double press triggered"); - // Not implemented. TODO: possibly send an advert here? + MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); + // ADVERT + if(the_mesh.advert()) { + MESH_DEBUG_PRINTLN("Advert sent!"); + } + else { + MESH_DEBUG_PRINTLN("Advert failed!"); + } } void UITask::handleButtonTriplePress() { diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index acf5237e..6d7e1eb3 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef UI_TASK_H +#define UI_TASK_H #include #include @@ -72,3 +73,4 @@ public: void shutdown(bool restart = false); void loop(); }; +#endif //UI_TASK_H \ No newline at end of file diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 30a1c9cc..6677d49b 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -1,71 +1,6 @@ #include // needed for PlatformIO #include - -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #include -#elif defined(RP2040_PLATFORM) - #include -#elif defined(ESP32) - #include -#endif - -#include -#include -#include -#include -#include -#include "NodePrefs.h" -#include -#include - -/* ---------------------------------- CONFIGURATION ------------------------------------- */ - -#ifndef LORA_FREQ - #define LORA_FREQ 915.0 -#endif -#ifndef LORA_BW - #define LORA_BW 250 -#endif -#ifndef LORA_SF - #define LORA_SF 10 -#endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif -#ifndef LORA_TX_POWER - #define LORA_TX_POWER 20 -#endif -#ifndef MAX_LORA_TX_POWER - #define MAX_LORA_TX_POWER LORA_TX_POWER -#endif - -#ifndef MAX_CONTACTS - #define MAX_CONTACTS 100 -#endif - -#ifndef OFFLINE_QUEUE_SIZE - #define OFFLINE_QUEUE_SIZE 16 -#endif - -#ifndef BLE_NAME_PREFIX - #define BLE_NAME_PREFIX "MeshCore-" -#endif - -#include - -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define LAZY_CONTACTS_WRITE_DELAY 5000 - -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" - -#ifdef DISPLAY_CLASS - #include "UITask.h" - - static UITask ui_task(&board); -#endif +#include "MyMesh.h" // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -77,1518 +12,13 @@ static uint32_t _atoi(const char* sp) { return n; } -/*------------ Frame Protocol --------------*/ - -#define FIRMWARE_VER_CODE 5 - -#ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "24 May 2025" -#endif - -#ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.6.2" -#endif - -#define CMD_APP_START 1 -#define CMD_SEND_TXT_MSG 2 -#define CMD_SEND_CHANNEL_TXT_MSG 3 -#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) -#define CMD_GET_DEVICE_TIME 5 -#define CMD_SET_DEVICE_TIME 6 -#define CMD_SEND_SELF_ADVERT 7 -#define CMD_SET_ADVERT_NAME 8 -#define CMD_ADD_UPDATE_CONTACT 9 -#define CMD_SYNC_NEXT_MESSAGE 10 -#define CMD_SET_RADIO_PARAMS 11 -#define CMD_SET_RADIO_TX_POWER 12 -#define CMD_RESET_PATH 13 -#define CMD_SET_ADVERT_LATLON 14 -#define CMD_REMOVE_CONTACT 15 -#define CMD_SHARE_CONTACT 16 -#define CMD_EXPORT_CONTACT 17 -#define CMD_IMPORT_CONTACT 18 -#define CMD_REBOOT 19 -#define CMD_GET_BATTERY_VOLTAGE 20 -#define CMD_SET_TUNING_PARAMS 21 -#define CMD_DEVICE_QEURY 22 -#define CMD_EXPORT_PRIVATE_KEY 23 -#define CMD_IMPORT_PRIVATE_KEY 24 -#define CMD_SEND_RAW_DATA 25 -#define CMD_SEND_LOGIN 26 -#define CMD_SEND_STATUS_REQ 27 -#define CMD_HAS_CONNECTION 28 -#define CMD_LOGOUT 29 // 'Disconnect' -#define CMD_GET_CONTACT_BY_KEY 30 -#define CMD_GET_CHANNEL 31 -#define CMD_SET_CHANNEL 32 -#define CMD_SIGN_START 33 -#define CMD_SIGN_DATA 34 -#define CMD_SIGN_FINISH 35 -#define CMD_SEND_TRACE_PATH 36 -#define CMD_SET_DEVICE_PIN 37 -#define CMD_SET_OTHER_PARAMS 38 -#define CMD_SEND_TELEMETRY_REQ 39 -#define CMD_GET_CUSTOM_VARS 40 -#define CMD_SET_CUSTOM_VAR 41 - -#define RESP_CODE_OK 0 -#define RESP_CODE_ERR 1 -#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS -#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) -#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS -#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START -#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG -#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME -#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE -#define RESP_CODE_EXPORT_CONTACT 11 -#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE -#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY -#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY -#define RESP_CODE_DISABLED 15 -#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL -#define RESP_CODE_SIGN_START 19 -#define RESP_CODE_SIGNATURE 20 -#define RESP_CODE_CUSTOM_VARS 21 - -// these are _pushed_ to client app at any time -#define PUSH_CODE_ADVERT 0x80 -#define PUSH_CODE_PATH_UPDATED 0x81 -#define PUSH_CODE_SEND_CONFIRMED 0x82 -#define PUSH_CODE_MSG_WAITING 0x83 -#define PUSH_CODE_RAW_DATA 0x84 -#define PUSH_CODE_LOGIN_SUCCESS 0x85 -#define PUSH_CODE_LOGIN_FAIL 0x86 -#define PUSH_CODE_STATUS_RESPONSE 0x87 -#define PUSH_CODE_LOG_RX_DATA 0x88 -#define PUSH_CODE_TRACE_DATA 0x89 -#define PUSH_CODE_NEW_ADVERT 0x8A -#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B - -#define ERR_CODE_UNSUPPORTED_CMD 1 -#define ERR_CODE_NOT_FOUND 2 -#define ERR_CODE_TABLE_FULL 3 -#define ERR_CODE_BAD_STATE 4 -#define ERR_CODE_FILE_IO_ERROR 5 -#define ERR_CODE_ILLEGAL_ARG 6 - -/* -------------------------------------------------------------------------------------- */ - -#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS -#define REQ_TYPE_KEEP_ALIVE 0x02 -#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 - -#define MAX_SIGN_DATA_LEN (8*1024) // 8K - -class MyMesh : public BaseChatMesh { - FILESYSTEM* _fs; - IdentityStore* _identity_store; - NodePrefs _prefs; - uint32_t pending_login; - uint32_t pending_status; - uint32_t pending_telemetry; - BaseSerialInterface* _serial; - ContactsIterator _iter; - uint32_t _iter_filter_since; - uint32_t _most_recent_lastmod; - uint32_t _active_ble_pin; - bool _iter_started; - uint8_t app_target_ver; - uint8_t* sign_data; - uint32_t sign_data_len; - unsigned long dirty_contacts_expiry; - uint8_t cmd_frame[MAX_FRAME_SIZE+1]; - uint8_t out_frame[MAX_FRAME_SIZE+1]; - CayenneLPP telemetry; - - struct Frame { - uint8_t len; - uint8_t buf[MAX_FRAME_SIZE]; - }; - int offline_queue_len; - Frame offline_queue[OFFLINE_QUEUE_SIZE]; - - struct AckTableEntry { - unsigned long msg_sent; - uint32_t ack; - }; - #define EXPECTED_ACK_TABLE_SIZE 8 - AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table - int next_ack_idx; - - void loadMainIdentity() { - if (!_identity_store->load("_main", self_id)) { - self_id = radio_new_identity(); // create new random identity - int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes - self_id = radio_new_identity(); count++; - } - saveMainIdentity(self_id); - } - } - - bool saveMainIdentity(const mesh::LocalIdentity& identity) { - return _identity_store->save("_main", identity); - } - - void loadContacts() { - if (_fs->exists("/contacts3")) { - #if defined(RP2040_PLATFORM) - File file = _fs->open("/contacts3", "r"); - #else - File file = _fs->open("/contacts3"); - #endif - if (file) { - bool full = false; - while (!full) { - ContactInfo c; - uint8_t pub_key[32]; - uint8_t unused; - - bool success = (file.read(pub_key, 32) == 32); - success = success && (file.read((uint8_t *) &c.name, 32) == 32); - success = success && (file.read(&c.type, 1) == 1); - success = success && (file.read(&c.flags, 1) == 1); - success = success && (file.read(&unused, 1) == 1); - success = success && (file.read((uint8_t *) &c.sync_since, 4) == 4); // was 'reserved' - success = success && (file.read((uint8_t *) &c.out_path_len, 1) == 1); - success = success && (file.read((uint8_t *) &c.last_advert_timestamp, 4) == 4); - success = success && (file.read(c.out_path, 64) == 64); - success = success && (file.read((uint8_t *) &c.lastmod, 4) == 4); - success = success && (file.read((uint8_t *) &c.gps_lat, 4) == 4); - success = success && (file.read((uint8_t *) &c.gps_lon, 4) == 4); - - if (!success) break; // EOF - - c.id = mesh::Identity(pub_key); - if (!addContact(c)) full = true; - } - file.close(); - } - } - } - - void saveContacts() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/contacts3"); - File file = _fs->open("/contacts3", FILE_O_WRITE); + #include #elif defined(RP2040_PLATFORM) - File file = _fs->open("/contacts3", "w"); -#else - File file = _fs->open("/contacts3", "w", true); + #include +#elif defined(ESP32) + #include #endif - if (file) { - ContactsIterator iter; - ContactInfo c; - uint8_t unused = 0; - - while (iter.hasNext(this, c)) { - bool success = (file.write(c.id.pub_key, 32) == 32); - success = success && (file.write((uint8_t *) &c.name, 32) == 32); - success = success && (file.write(&c.type, 1) == 1); - success = success && (file.write(&c.flags, 1) == 1); - success = success && (file.write(&unused, 1) == 1); - success = success && (file.write((uint8_t *) &c.sync_since, 4) == 4); - success = success && (file.write((uint8_t *) &c.out_path_len, 1) == 1); - success = success && (file.write((uint8_t *) &c.last_advert_timestamp, 4) == 4); - success = success && (file.write(c.out_path, 64) == 64); - success = success && (file.write((uint8_t *) &c.lastmod, 4) == 4); - success = success && (file.write((uint8_t *) &c.gps_lat, 4) == 4); - success = success && (file.write((uint8_t *) &c.gps_lon, 4) == 4); - - if (!success) break; // write failed - } - file.close(); - } - } - - void loadChannels() { - if (_fs->exists("/channels2")) { - #if defined(RP2040_PLATFORM) - File file = _fs->open("/channels2", "r"); - #else - File file = _fs->open("/channels2"); - #endif - if (file) { - bool full = false; - uint8_t channel_idx = 0; - while (!full) { - ChannelDetails ch; - uint8_t unused[4]; - - bool success = (file.read(unused, 4) == 4); - success = success && (file.read((uint8_t *) ch.name, 32) == 32); - success = success && (file.read((uint8_t *) ch.channel.secret, 32) == 32); - - if (!success) break; // EOF - - if (setChannel(channel_idx, ch)) { - channel_idx++; - } else { - full = true; - } - } - file.close(); - } - } - } - - void saveChannels() { - #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/channels2"); - File file = _fs->open("/channels2", FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) - File file = _fs->open("/channels2", "w"); - #else - File file = _fs->open("/channels2", "w", true); - #endif - if (file) { - uint8_t channel_idx = 0; - ChannelDetails ch; - uint8_t unused[4]; - memset(unused, 0, 4); - - while (getChannel(channel_idx, ch)) { - bool success = (file.write(unused, 4) == 4); - success = success && (file.write((uint8_t *) ch.name, 32) == 32); - success = success && (file.write((uint8_t *) ch.channel.secret, 32) == 32); - - if (!success) break; // write failed - channel_idx++; - } - file.close(); - } - } - - int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { - char path[64]; - char fname[18]; - - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) - mesh::Utils::toHex(fname, key, key_len); - 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 - if (f) { - int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! - f.close(); - return len; - } - } - return 0; // not found - } - - bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override { - char path[64]; - char fname[18]; - - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) - mesh::Utils::toHex(fname, key, key_len); - sprintf(path, "/bl/%s", fname); - - #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(path); - File f = _fs->open(path, FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) - File f = _fs->open(path, "w"); - #else - File f = _fs->open(path, "w", true); - #endif - if (f) { - int n = f.write(src_buf, len); - f.close(); - if (n == len) return true; // success! - - _fs->remove(path); // blob was only partially written! - } - return false; // error - } - - void writeOKFrame() { - uint8_t buf[1]; - buf[0] = RESP_CODE_OK; - _serial->writeFrame(buf, 1); - } - void writeErrFrame(uint8_t err_code) { - uint8_t buf[2]; - buf[0] = RESP_CODE_ERR; - buf[1] = err_code; - _serial->writeFrame(buf, 2); - } - - void writeDisabledFrame() { - uint8_t buf[1]; - buf[0] = RESP_CODE_DISABLED; - _serial->writeFrame(buf, 1); - } - - void writeContactRespFrame(uint8_t code, const ContactInfo& contact) { - int i = 0; - out_frame[i++] = code; - memcpy(&out_frame[i], contact.id.pub_key, PUB_KEY_SIZE); i += PUB_KEY_SIZE; - out_frame[i++] = contact.type; - out_frame[i++] = contact.flags; - out_frame[i++] = contact.out_path_len; - memcpy(&out_frame[i], contact.out_path, MAX_PATH_SIZE); i += MAX_PATH_SIZE; - StrHelper::strzcpy((char *) &out_frame[i], contact.name, 32); i += 32; - memcpy(&out_frame[i], &contact.last_advert_timestamp, 4); i += 4; - memcpy(&out_frame[i], &contact.gps_lat, 4); i += 4; - memcpy(&out_frame[i], &contact.gps_lon, 4); i += 4; - memcpy(&out_frame[i], &contact.lastmod, 4); i += 4; - _serial->writeFrame(out_frame, i); - } - - void updateContactFromFrame(ContactInfo& contact, const uint8_t* frame, int len) { - int i = 0; - uint8_t code = frame[i++]; // eg. CMD_ADD_UPDATE_CONTACT - memcpy(contact.id.pub_key, &frame[i], PUB_KEY_SIZE); i += PUB_KEY_SIZE; - contact.type = frame[i++]; - contact.flags = frame[i++]; - contact.out_path_len = frame[i++]; - memcpy(contact.out_path, &frame[i], MAX_PATH_SIZE); i += MAX_PATH_SIZE; - memcpy(contact.name, &frame[i], 32); i += 32; - memcpy(&contact.last_advert_timestamp, &frame[i], 4); i += 4; - if (i + 8 >= len) { // optional fields - memcpy(&contact.gps_lat, &frame[i], 4); i += 4; - memcpy(&contact.gps_lon, &frame[i], 4); i += 4; - } - } - - void addToOfflineQueue(const uint8_t frame[], int len) { - if (offline_queue_len >= OFFLINE_QUEUE_SIZE) { - MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); - } else { - offline_queue[offline_queue_len].len = len; - memcpy(offline_queue[offline_queue_len].buf, frame, len); - offline_queue_len++; - } - } - int getFromOfflineQueue(uint8_t frame[]) { - if (offline_queue_len > 0) { // check offline queue - size_t len = offline_queue[0].len; // take from top of queue - memcpy(frame, offline_queue[0].buf, len); - - offline_queue_len--; - for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue - offline_queue[i] = offline_queue[i + 1]; - } - return len; - } - return 0; // queue is empty - } - -protected: - float getAirtimeBudgetFactor() const override { - return _prefs.airtime_factor; - } - - int getInterferenceThreshold() const override { - return 14; // hard-coded for now - } - - int calcRxDelay(float score, uint32_t air_time) const override { - if (_prefs.rx_delay_base <= 0.0f) return 0; - return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); - } - - void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override { - if (_serial->isConnected() && len+3 <= MAX_FRAME_SIZE) { - int i = 0; - out_frame[i++] = PUSH_CODE_LOG_RX_DATA; - out_frame[i++] = (int8_t)(snr * 4); - out_frame[i++] = (int8_t)(rssi); - memcpy(&out_frame[i], raw, len); i += len; - - _serial->writeFrame(out_frame, i); - } - } - - bool isAutoAddEnabled() const override { - return (_prefs.manual_add_contacts & 1) == 0; - } - - void onDiscoveredContact(ContactInfo& contact, bool is_new) override { - if (_serial->isConnected()) { - if (!isAutoAddEnabled() && is_new) { - writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); - } else { - out_frame[0] = PUSH_CODE_ADVERT; - memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); - _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); - } - } else { - #ifdef DISPLAY_CLASS - ui_task.soundBuzzer(UIEventType::newContactMessage); - #endif - } - - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - } - - void onContactPathUpdated(const ContactInfo& contact) override { - out_frame[0] = PUSH_CODE_PATH_UPDATED; - memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); - _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected - - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - } - - bool processAck(const uint8_t *data) override { - // see if matches any in a table - for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) { - if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient - out_frame[0] = PUSH_CODE_SEND_CONFIRMED; - memcpy(&out_frame[1], data, 4); - uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent; - memcpy(&out_frame[5], &trip_time, 4); - _serial->writeFrame(out_frame, 9); - - // NOTE: the same ACK can be received multiple times! - expected_ack_table[i].ack = 0; // clear expected hash, now that we have received ACK - return true; - } - } - return checkConnectionsAck(data); - } - - void queueMessage(const ContactInfo& from, uint8_t txt_type, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text) { - int i = 0; - if (app_target_ver >= 3) { - out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; - out_frame[i++] = (int8_t)(pkt->getSNR() * 4); - out_frame[i++] = 0; // reserved1 - out_frame[i++] = 0; // reserved2 - } else { - out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; - } - memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix - uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; - out_frame[i++] = txt_type; - memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; - if (extra_len > 0) { - memcpy(&out_frame[i], extra, extra_len); i += extra_len; - } - int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) { - tlen = MAX_FRAME_SIZE - i; - } - memcpy(&out_frame[i], text, tlen); i += tlen; - addToOfflineQueue(out_frame, i); - - if (_serial->isConnected()) { - uint8_t frame[1]; - frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' - _serial->writeFrame(frame, 1); - } else { - #ifdef DISPLAY_CLASS - ui_task.soundBuzzer(UIEventType::contactMessage); - #endif - } - #ifdef DISPLAY_CLASS - ui_task.newMsg(path_len, from.name, text, offline_queue_len); - #endif - } - - void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { - markConnectionActive(from); // in case this is from a server, and we have a connection - queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); - } - - void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { - markConnectionActive(from); // in case this is from a server, and we have a connection - queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); - } - - void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { - markConnectionActive(from); - // from.sync_since change needs to be persisted - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text); - } - - void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override { - int i = 0; - if (app_target_ver >= 3) { - out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; - out_frame[i++] = (int8_t)(pkt->getSNR() * 4); - out_frame[i++] = 0; // reserved1 - out_frame[i++] = 0; // reserved2 - } else { - out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; - } - - out_frame[i++] = findChannelIdx(channel); - uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; - - out_frame[i++] = TXT_TYPE_PLAIN; - memcpy(&out_frame[i], ×tamp, 4); i += 4; - int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) { - tlen = MAX_FRAME_SIZE - i; - } - memcpy(&out_frame[i], text, tlen); i += tlen; - addToOfflineQueue(out_frame, i); - - if (_serial->isConnected()) { - uint8_t frame[1]; - frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' - _serial->writeFrame(frame, 1); - } else { - #ifdef DISPLAY_CLASS - ui_task.soundBuzzer(UIEventType::channelMessage); - #endif - } - #ifdef DISPLAY_CLASS - ui_task.newMsg(path_len, "Public", text, offline_queue_len); - #endif - } - - uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) override { - if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { - uint8_t permissions = 0; - uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) - - if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) { - permissions = TELEM_PERM_BASE; - } else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { - permissions = cp & TELEM_PERM_BASE; - } - - if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) { - permissions |= TELEM_PERM_LOCATION; - } else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { - permissions |= cp & TELEM_PERM_LOCATION; - } - - if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { - permissions |= TELEM_PERM_ENVIRONMENT; - } else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { - permissions |= cp & TELEM_PERM_ENVIRONMENT; - } - - if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set - telemetry.reset(); - telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); - // query other sensors -- target specific - sensors.querySensors(permissions, telemetry); - - memcpy(reply, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') - - uint8_t tlen = telemetry.getSize(); - memcpy(&reply[4], telemetry.getBuffer(), tlen); - return 4 + tlen; - } - } - return 0; // unknown - } - - void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { - uint32_t tag; - memcpy(&tag, data, 4); - - if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) { // check for login response - // yes, is response to pending sendLogin() - pending_login = 0; - - int i = 0; - if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response - out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; - out_frame[i++] = 0; // legacy: is_admin = false - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix - } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response - uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; - if (keep_alive_secs > 0) { - startConnection(contact, keep_alive_secs); - } - out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; - out_frame[i++] = data[6]; // permissions (eg. is_admin) - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix - memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp - } else { - out_frame[i++] = PUSH_CODE_LOGIN_FAIL; - out_frame[i++] = 0; // reserved - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix - } - _serial->writeFrame(out_frame, i); - } else if (len > 4 && // check for status response - pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme - // FUTURE: tag == pending_status - ) { - pending_status = 0; - - int i = 0; - out_frame[i++] = PUSH_CODE_STATUS_RESPONSE; - out_frame[i++] = 0; // reserved - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix - memcpy(&out_frame[i], &data[4], len - 4); i += (len - 4); - _serial->writeFrame(out_frame, i); - } else if (len > 4 && tag == pending_telemetry) { // check for telemetry response - pending_telemetry = 0; - - int i = 0; - out_frame[i++] = PUSH_CODE_TELEMETRY_RESPONSE; - out_frame[i++] = 0; // reserved - memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix - memcpy(&out_frame[i], &data[4], len - 4); i += (len - 4); - _serial->writeFrame(out_frame, i); - } - } - - void onRawDataRecv(mesh::Packet* packet) override { - if (packet->payload_len + 4 > sizeof(out_frame)) { - MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len); - return; - } - int i = 0; - out_frame[i++] = PUSH_CODE_RAW_DATA; - out_frame[i++] = (int8_t)(_radio->getLastSNR() * 4); - out_frame[i++] = (int8_t)(_radio->getLastRSSI()); - out_frame[i++] = 0xFF; // reserved (possibly path_len in future) - 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("onRawDataRecv(), data received while app offline"); - } - } - - 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 { - int i = 0; - out_frame[i++] = PUSH_CODE_TRACE_DATA; - out_frame[i++] = 0; // reserved - out_frame[i++] = path_len; - out_frame[i++] = flags; - memcpy(&out_frame[i], &tag, 4); i += 4; - memcpy(&out_frame[i], &auth_code, 4); i += 4; - memcpy(&out_frame[i], path_hashes, path_len); i += path_len; - memcpy(&out_frame[i], path_snrs, path_len); i += path_len; - out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node) - - if (_serial->isConnected()) { - _serial->writeFrame(out_frame, i); - } else { - MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); - } - } - - uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override { - return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); - } - uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override { - return SEND_TIMEOUT_BASE_MILLIS + - ( (pkt_airtime_millis*DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); - } - - void onSendTimeout() override { - } - -public: - - MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables) - : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), - telemetry(MAX_PACKET_PAYLOAD - 4) - { - _iter_started = false; - offline_queue_len = 0; - app_target_ver = 0; - _identity_store = NULL; - pending_login = pending_status = pending_telemetry = 0; - next_ack_idx = 0; - sign_data = NULL; - dirty_contacts_expiry = 0; - - // defaults - memset(&_prefs, 0, sizeof(_prefs)); - _prefs.airtime_factor = 1.0; // one half - strcpy(_prefs.node_name, "NONAME"); - _prefs.freq = LORA_FREQ; - _prefs.sf = LORA_SF; - _prefs.bw = LORA_BW; - _prefs.cr = LORA_CR; - _prefs.tx_power_dbm = LORA_TX_POWER; - //_prefs.rx_delay_base = 10.0f; enable once new algo fixed - } - - void loadPrefsInt(const char* filename) { -#if defined(RP2040_PLATFORM) - File file = _fs->open(filename, "r"); -#else - File file = _fs->open(filename); -#endif - if (file) { - uint8_t pad[8]; - - file.read((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0 - file.read((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4 - file.read(pad, 4); // 36 - file.read((uint8_t *) &sensors.node_lat, sizeof(sensors.node_lat)); // 40 - file.read((uint8_t *) &sensors.node_lon, sizeof(sensors.node_lon)); // 48 - file.read((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56 - file.read((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60 - file.read((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61 - file.read((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 - file.read((uint8_t *) &_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 - file.read((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64 - file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 - file.read((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 - file.read((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.read((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 - file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 - file.read(pad, 4); // 76 - file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 - - // sanitise bad pref values - _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.cr = constrain(_prefs.cr, 5, 8); - _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); - - file.close(); - } - } - - void begin(FILESYSTEM& fs, bool has_display) { - _fs = &fs; - - BaseChatMesh::begin(); - - #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _identity_store = new IdentityStore(fs, ""); - #elif defined(RP2040_PLATFORM) - _identity_store = new IdentityStore(fs, "/identity"); - _identity_store->begin(); - #else - _identity_store = new IdentityStore(fs, "/identity"); - #endif - - loadMainIdentity(); - - // use hex of first 4 bytes of identity public key as default node name - char pub_key_hex[10]; - mesh::Utils::toHex(pub_key_hex, self_id.pub_key, 4); - strcpy(_prefs.node_name, pub_key_hex); - - // if name is provided as a build flag, use that as default node name instead - #ifdef ADVERT_NAME - strcpy(_prefs.node_name, ADVERT_NAME); - #endif - - // load persisted prefs - if (_fs->exists("/new_prefs")) { - loadPrefsInt("/new_prefs"); // new filename - } else if (_fs->exists("/node_prefs")) { - loadPrefsInt("/node_prefs"); - savePrefs(); // save to new filename - _fs->remove("/node_prefs"); // remove old - } - - #ifdef BLE_PIN_CODE - if (_prefs.ble_pin == 0) { - #ifdef DISPLAY_CLASS - if (has_display) { - StdRNG rng; - _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session - } else { - _active_ble_pin = BLE_PIN_CODE; // otherwise static pin - } - #else - _active_ble_pin = BLE_PIN_CODE; // otherwise static pin - #endif - } else { - _active_ble_pin = _prefs.ble_pin; - } - #else - _active_ble_pin = 0; - #endif - - // init 'blob store' support - _fs->mkdir("/bl"); - - loadContacts(); - addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel - loadChannels(); - - radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - radio_set_tx_power(_prefs.tx_power_dbm); - } - - const char* getNodeName() { return _prefs.node_name; } - NodePrefs* getNodePrefs() { - return &_prefs; - } - uint32_t getBLEPin() { return _active_ble_pin; } - - void startInterface(BaseSerialInterface& serial) { - _serial = &serial; - serial.enable(); - } - - void savePrefs() { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/new_prefs"); - File file = _fs->open("/new_prefs", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/new_prefs", "w"); -#else - File file = _fs->open("/new_prefs", "w", true); -#endif - if (file) { - uint8_t pad[8]; - memset(pad, 0, sizeof(pad)); - - file.write((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0 - file.write((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4 - file.write(pad, 4); // 36 - file.write((uint8_t *) &sensors.node_lat, sizeof(sensors.node_lat)); // 40 - file.write((uint8_t *) &sensors.node_lon, sizeof(sensors.node_lon)); // 48 - file.write((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56 - file.write((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60 - file.write((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61 - file.write((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 - file.write((uint8_t *) &_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 - file.write((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64 - file.write((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 - file.write((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 - file.write((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.write((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 - file.write((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 - file.write(pad, 4); // 76 - file.write((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 - - file.close(); - } - } - - void handleCmdFrame(size_t len) { - if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection - app_target_ver = cmd_frame[1]; // which version of protocol does app understand - - int i = 0; - out_frame[i++] = RESP_CODE_DEVICE_INFO; - out_frame[i++] = FIRMWARE_VER_CODE; - out_frame[i++] = MAX_CONTACTS / 2; // v3+ - out_frame[i++] = MAX_GROUP_CHANNELS; // v3+ - memcpy(&out_frame[i], &_prefs.ble_pin, 4); i += 4; - memset(&out_frame[i], 0, 12); - strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12; - StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40; - StrHelper::strzcpy((char *) &out_frame[i], FIRMWARE_VERSION, 20); i += 20; - _serial->writeFrame(out_frame, i); - } else if (cmd_frame[0] == CMD_APP_START && len >= 8) { // sent when app establishes connection, respond with node ID - // cmd_frame[1..7] reserved future - char* app_name = (char *) &cmd_frame[8]; - cmd_frame[len] = 0; // make app_name null terminated - MESH_DEBUG_PRINTLN("App %s connected", app_name); - - _iter_started = false; // stop any left-over ContactsIterator - int i = 0; - out_frame[i++] = RESP_CODE_SELF_INFO; - out_frame[i++] = ADV_TYPE_CHAT; // what this node Advert identifies as (maybe node's pronouns too?? :-) - out_frame[i++] = _prefs.tx_power_dbm; - out_frame[i++] = MAX_LORA_TX_POWER; - memcpy(&out_frame[i], self_id.pub_key, PUB_KEY_SIZE); i += PUB_KEY_SIZE; - - int32_t lat, lon; - lat = (sensors.node_lat * 1000000.0); - lon = (sensors.node_lon * 1000000.0); - memcpy(&out_frame[i], &lat, 4); i += 4; - memcpy(&out_frame[i], &lon, 4); i += 4; - out_frame[i++] = 0; // reserved - out_frame[i++] = 0; // reserved - out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ - out_frame[i++] = _prefs.manual_add_contacts; - - uint32_t freq = _prefs.freq * 1000; - memcpy(&out_frame[i], &freq, 4); i += 4; - uint32_t bw = _prefs.bw*1000; - memcpy(&out_frame[i], &bw, 4); i += 4; - out_frame[i++] = _prefs.sf; - out_frame[i++] = _prefs.cr; - - int tlen = strlen(_prefs.node_name); // revisit: UTF_8 ?? - memcpy(&out_frame[i], _prefs.node_name, tlen); i += tlen; - _serial->writeFrame(out_frame, i); - } else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { - int i = 1; - uint8_t txt_type = cmd_frame[i++]; - uint8_t attempt = cmd_frame[i++]; - uint32_t msg_timestamp; - memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4; - uint8_t* pub_key_prefix = &cmd_frame[i]; i += 6; - ContactInfo* recipient = lookupContactByPubKey(pub_key_prefix, 6); - if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) { - char *text = (char *) &cmd_frame[i]; - int tlen = len - i; - uint32_t est_timeout; - text[tlen] = 0; // ensure null - int result; - uint32_t expected_ack; - if (txt_type == TXT_TYPE_CLI_DATA) { - result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); - expected_ack = 0; // no Ack expected - } else { - result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); - } - // TODO: add expected ACK to table - if (result == MSG_SEND_FAILED) { - writeErrFrame(ERR_CODE_TABLE_FULL); - } else { - if (expected_ack) { - expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table - expected_ack_table[next_ack_idx].ack = expected_ack; - next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; - } - - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; - memcpy(&out_frame[2], &expected_ack, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - } - } else { - writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* - } - } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg - int i = 1; - uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN - uint8_t channel_idx = cmd_frame[i++]; - uint32_t msg_timestamp; - memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4; - const char *text = (char *) &cmd_frame[i]; - - if (txt_type != TXT_TYPE_PLAIN) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } else { - ChannelDetails channel; - bool success = getChannel(channel_idx, channel); - if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx - } - } - } else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list - if (_iter_started) { - writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy - } else { - if (len >= 5) { // has optional 'since' param - memcpy(&_iter_filter_since, &cmd_frame[1], 4); - } else { - _iter_filter_since = 0; - } - - uint8_t reply[5]; - reply[0] = RESP_CODE_CONTACTS_START; - uint32_t count = getNumContacts(); // total, NOT filtered count - memcpy(&reply[1], &count, 4); - _serial->writeFrame(reply, 5); - - // start iterator - _iter = startContactsIterator(); - _iter_started = true; - _most_recent_lastmod = 0; - } - } else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { - int nlen = len - 1; - if (nlen > sizeof(_prefs.node_name)-1) nlen = sizeof(_prefs.node_name)-1; // max len - memcpy(_prefs.node_name, &cmd_frame[1], nlen); - _prefs.node_name[nlen] = 0; // null terminator - savePrefs(); - writeOKFrame(); - } else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { - int32_t lat, lon, alt = 0; - memcpy(&lat, &cmd_frame[1], 4); - memcpy(&lon, &cmd_frame[5], 4); - if (len >= 13) { - memcpy(&alt, &cmd_frame[9], 4); // for FUTURE support - } - if (lat <= 90*1E6 && lat >= -90*1E6 && lon <= 180*1E6 && lon >= -180*1E6) { - sensors.node_lat = ((double)lat) / 1000000.0; - sensors.node_lon = ((double)lon) / 1000000.0; - savePrefs(); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate - } - } else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { - uint8_t reply[5]; - reply[0] = RESP_CODE_CURR_TIME; - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(&reply[1], &now, 4); - _serial->writeFrame(reply, 5); - } else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { - uint32_t secs; - memcpy(&secs, &cmd_frame[1], 4); - uint32_t curr = getRTCClock()->getCurrentTime(); - if (secs >= curr) { - getRTCClock()->setCurrentTime(secs); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - } else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { - auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) { - if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop) - sendFlood(pkt); - } else { - sendZeroHop(pkt); - } - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); - } - } else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1+32) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) { - recipient->out_path_len = -1; - //recipient->lastmod = ?? shouldn't be needed, app already has this version of contact - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact - } - } else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1+32+2+1) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) { - updateContactFromFrame(*recipient, cmd_frame, len); - //recipient->lastmod = ?? shouldn't be needed, app already has this version of contact - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - writeOKFrame(); - } else { - ContactInfo contact; - updateContactFromFrame(contact, cmd_frame, len); - contact.lastmod = getRTCClock()->getCurrentTime(); - contact.sync_since = 0; - if (addContact(contact)) { - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); - } - } - } else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient && removeContact(*recipient)) { - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove - } - } else if (cmd_frame[0] == CMD_SHARE_CONTACT) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) { - if (shareContactZeroHop(*recipient)) { - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send - } - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); - } - } else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (contact) { - writeContactRespFrame(RESP_CODE_CONTACT, *contact); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // not found - } - } else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { - if (len < 1 + PUB_KEY_SIZE) { - // export SELF - auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) { - pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode - - out_frame[0] = RESP_CODE_EXPORT_CONTACT; - uint8_t out_len = pkt->writeTo(&out_frame[1]); - releasePacket(pkt); // undo the obtainNewPacket() - _serial->writeFrame(out_frame, out_len + 1); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); // Error - } - } else { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - uint8_t out_len; - if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) { - out_frame[0] = RESP_CODE_EXPORT_CONTACT; - _serial->writeFrame(out_frame, out_len + 1); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // not found - } - } - } else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2+32+64) { - if (importContact(&cmd_frame[1], len - 1)) { - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - } else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { - int out_len; - if ((out_len = getFromOfflineQueue(out_frame)) > 0) { - _serial->writeFrame(out_frame, out_len); - #ifdef DISPLAY_CLASS - ui_task.msgRead(offline_queue_len); - #endif - } else { - out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; - _serial->writeFrame(out_frame, 1); - } - } else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { - int i = 1; - uint32_t freq; - memcpy(&freq, &cmd_frame[i], 4); i += 4; - uint32_t bw; - memcpy(&bw, &cmd_frame[i], 4); i += 4; - uint8_t sf = cmd_frame[i++]; - uint8_t cr = cmd_frame[i++]; - - if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) { - _prefs.sf = sf; - _prefs.cr = cr; - _prefs.freq = (float)freq / 1000.0; - _prefs.bw = (float)bw / 1000.0; - savePrefs(); - - radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); - - writeOKFrame(); - } else { - MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - } else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { - if (cmd_frame[1] > MAX_LORA_TX_POWER) { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } else { - _prefs.tx_power_dbm = cmd_frame[1]; - savePrefs(); - radio_set_tx_power(_prefs.tx_power_dbm); - writeOKFrame(); - } - } else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { - int i = 1; - uint32_t rx, af; - memcpy(&rx, &cmd_frame[i], 4); i += 4; - memcpy(&af, &cmd_frame[i], 4); i += 4; - _prefs.rx_delay_base = ((float)rx) / 1000.0f; - _prefs.airtime_factor = ((float)af) / 1000.0f; - savePrefs(); - writeOKFrame(); - } else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { - _prefs.manual_add_contacts = cmd_frame[1]; - if (len >= 3) { - _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ - _prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03; - _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03; - } - savePrefs(); - writeOKFrame(); - } else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { - if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? - saveContacts(); - } - board.reboot(); - } else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { - uint8_t reply[3]; - reply[0] = RESP_CODE_BATTERY_VOLTAGE; - uint16_t battery_millivolts = board.getBattMilliVolts(); - memcpy(&reply[1], &battery_millivolts, 2); - _serial->writeFrame(reply, 3); - } else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { - #if ENABLE_PRIVATE_KEY_EXPORT - uint8_t reply[65]; - reply[0] = RESP_CODE_PRIVATE_KEY; - self_id.writeTo(&reply[1], 64); - _serial->writeFrame(reply, 65); - #else - writeDisabledFrame(); - #endif - } else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { - #if ENABLE_PRIVATE_KEY_IMPORT - mesh::LocalIdentity identity; - identity.readFrom(&cmd_frame[1], 64); - if (saveMainIdentity(identity)) { - self_id = identity; - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_FILE_IO_ERROR); - } - #else - writeDisabledFrame(); - #endif - } else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { - int i = 1; - int8_t path_len = cmd_frame[i++]; - if (path_len >= 0 && i + path_len + 4 <= len) { // minimum 4 byte payload - uint8_t* path = &cmd_frame[i]; i += path_len; - auto pkt = createRawData(&cmd_frame[i], len - i); - if (pkt) { - sendDirect(pkt, path, path_len); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); - } - } else { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) - } - } else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1+PUB_KEY_SIZE) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - char *password = (char *) &cmd_frame[1+PUB_KEY_SIZE]; - cmd_frame[len] = 0; // ensure null terminator in password - if (recipient) { - uint32_t est_timeout; - int result = sendLogin(*recipient, password, est_timeout); - if (result == MSG_SEND_FAILED) { - writeErrFrame(ERR_CODE_TABLE_FULL); - } else { - pending_telemetry = pending_status = 0; - memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; - memcpy(&out_frame[2], &pending_login, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - } - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found - } - } else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1+PUB_KEY_SIZE) { - uint8_t* pub_key = &cmd_frame[1]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) { - uint32_t tag, est_timeout; - int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); - if (result == MSG_SEND_FAILED) { - writeErrFrame(ERR_CODE_TABLE_FULL); - } else { - pending_telemetry = pending_login = 0; - // FUTURE: pending_status = tag; // match this in onContactResponse() - memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; - memcpy(&out_frame[2], &tag, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - } - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found - } - } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4+PUB_KEY_SIZE) { - uint8_t* pub_key = &cmd_frame[4]; - ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) { - uint32_t tag, est_timeout; - int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); - if (result == MSG_SEND_FAILED) { - writeErrFrame(ERR_CODE_TABLE_FULL); - } else { - pending_status = pending_login = 0; - pending_telemetry = tag; // match this in onContactResponse() - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; - memcpy(&out_frame[2], &tag, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - } - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found - } - } else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1+PUB_KEY_SIZE) { - uint8_t* pub_key = &cmd_frame[1]; - if (hasConnectionTo(pub_key)) { - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); - } - } else if (cmd_frame[0] == CMD_LOGOUT && len >= 1+PUB_KEY_SIZE) { - uint8_t* pub_key = &cmd_frame[1]; - stopConnection(pub_key); - writeOKFrame(); - } else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { - uint8_t channel_idx = cmd_frame[1]; - ChannelDetails channel; - if (getChannel(channel_idx, channel)) { - int i = 0; - out_frame[i++] = RESP_CODE_CHANNEL_INFO; - out_frame[i++] = channel_idx; - strcpy((char *)&out_frame[i], channel.name); i += 32; - memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported - _serial->writeFrame(out_frame, i); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); - } - } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+32) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) - } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+16) { - uint8_t channel_idx = cmd_frame[1]; - ChannelDetails channel; - StrHelper::strncpy(channel.name, (char *) &cmd_frame[2], 32); - memset(channel.channel.secret, 0, sizeof(channel.channel.secret)); - memcpy(channel.channel.secret, &cmd_frame[2+32], 16); // NOTE: only 128-bit supported - if (setChannel(channel_idx, channel)) { - saveChannels(); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx - } - } else if (cmd_frame[0] == CMD_SIGN_START) { - out_frame[0] = RESP_CODE_SIGN_START; - out_frame[1] = 0; // reserved - uint32_t len = MAX_SIGN_DATA_LEN; - memcpy(&out_frame[2], &len, 4); - _serial->writeFrame(out_frame, 6); - - if (sign_data) { - free(sign_data); - } - sign_data = (uint8_t *) malloc(MAX_SIGN_DATA_LEN); - sign_data_len = 0; - } else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { - if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { - writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long - } else { - memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); - sign_data_len += (len - 1); - writeOKFrame(); - } - } else if (cmd_frame[0] == CMD_SIGN_FINISH) { - if (sign_data) { - self_id.sign(&out_frame[1], sign_data, sign_data_len); - - free(sign_data); // don't need sign_data now - sign_data = NULL; - - out_frame[0] = RESP_CODE_SIGNATURE; - _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); - } else { - writeErrFrame(ERR_CODE_BAD_STATE); - } - } else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { - uint32_t tag, auth; - memcpy(&tag, &cmd_frame[1], 4); - memcpy(&auth, &cmd_frame[5], 4); - auto pkt = createTrace(tag, auth, cmd_frame[9]); - if (pkt) { - uint8_t path_len = len - 10; - sendDirect(pkt, &cmd_frame[10], path_len); - - uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2); - uint32_t est_timeout = calcDirectTimeoutMillisFor(t, path_len); - - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = 0; - memcpy(&out_frame[2], &tag, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - } else { - writeErrFrame(ERR_CODE_TABLE_FULL); - } - } else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { - - // get pin from command frame - uint32_t pin; - memcpy(&pin, &cmd_frame[1], 4); - - // ensure pin is zero, or a valid 6 digit pin - if(pin == 0 || (pin >= 100000 && pin <= 999999)){ - _prefs.ble_pin = pin; - savePrefs(); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - - } else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { - out_frame[0] = RESP_CODE_CUSTOM_VARS; - char* dp = (char *) &out_frame[1]; - for (int i = 0; i < sensors.getNumSettings() && dp - (char *) &out_frame[1] < 140; i++) { - if (i > 0) { *dp++ = ','; } - strcpy(dp, sensors.getSettingName(i)); dp = strchr(dp, 0); - *dp++ = ':'; - strcpy(dp, sensors.getSettingValue(i)); dp = strchr(dp, 0); - } - _serial->writeFrame(out_frame, dp - (char *)out_frame); - } else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { - cmd_frame[len] = 0; - char* sp = (char *) &cmd_frame[1]; - char* np = strchr(sp, ':'); // look for separator char - if (np) { - *np++ = 0; // modify 'cmd_frame', replace ':' with null - bool success = sensors.setSettingValue(sp, np); - if (success) { - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - } else { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); - } - } - - void loop() { - BaseChatMesh::loop(); - - size_t len = _serial->checkRecvFrame(cmd_frame); - if (len > 0) { - handleCmdFrame(len); - } else if (_iter_started // check if our ContactsIterator is 'running' - && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! - ) { - ContactInfo contact; - if (_iter.hasNext(this, contact)) { - if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter - writeContactRespFrame(RESP_CODE_CONTACT, contact); - if (contact.lastmod > _most_recent_lastmod) { - _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame - } - } - } else { // EOF - out_frame[0] = RESP_CODE_END_OF_CONTACTS; - memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' - _serial->writeFrame(out_frame, 5); - _iter_started = false; - } - } else if (!_serial->isWriteBusy()) { - checkConnections(); - } - - // is there are pending dirty contacts write needed? - if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { - saveContacts(); - dirty_contacts_expiry = 0; - } - - #ifdef DISPLAY_CLASS - ui_task.setHasConnection(_serial->isConnected()); - ui_task.loop(); - #endif - } -}; #ifdef ESP32 #ifdef WIFI_SSID @@ -1641,10 +71,18 @@ public: #error "need to define a serial interface" #endif +/* GLOBAL OBJECTS */ StdRNG fast_rng; SimpleMeshTables tables; MyMesh the_mesh(radio_driver, fast_rng, *new VolatileRTCClock(), tables); // TODO: test with 'rtc_clock' in target.cpp +#ifdef DISPLAY_CLASS + #include "UITask.h" + UITask ui_task(&board); +#endif +/* END GLOBAL OBJECTS */ + + void halt() { while (1) ; } diff --git a/variants/generic_espnow/platformio.ini b/variants/generic_espnow/platformio.ini index 8a033a62..b3ae7e45 100644 --- a/variants/generic_espnow/platformio.ini +++ b/variants/generic_espnow/platformio.ini @@ -62,7 +62,7 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 ; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1 build_src_filter = ${Generic_ESPNOW.build_src_filter} - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Generic_ESPNOW.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 11f73d81..49771624 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -186,7 +186,7 @@ build_flags = ${ProMicroLLCC68.build_flags} ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${ProMicroLLCC68.build_src_filter} - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${ProMicroLLCC68.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 @@ -205,7 +205,7 @@ build_flags = ${ProMicroLLCC68.build_flags} ; -D MESH_DEBUG=1 build_src_filter = ${ProMicroLLCC68.build_src_filter} + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${ProMicroLLCC68.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index 37e31e6f..c4569cf8 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -75,7 +75,7 @@ build_flags = ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 + -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} + + @@ -102,7 +102,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 2f468f4f..45d28740 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -68,7 +68,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Xiao_nrf52.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -84,7 +84,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Xiao_nrf52.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 841a50c3..cfc8e774 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -108,7 +108,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_S3_WIO.build_src_filter} + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 From ecd2b0be89e5532d6e9b6f368a769b2c6aba006b Mon Sep 17 00:00:00 2001 From: JQ Date: Sat, 31 May 2025 20:55:53 -0700 Subject: [PATCH 051/103] fixing channel name display --- examples/companion_radio/main.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 30a1c9cc..590c764d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -625,7 +625,8 @@ protected: out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } - out_frame[i++] = findChannelIdx(channel); + uint8_t channel_idx = findChannelIdx(channel); + out_frame[i++] = channel_idx; uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; out_frame[i++] = TXT_TYPE_PLAIN; @@ -647,7 +648,13 @@ protected: #endif } #ifdef DISPLAY_CLASS - ui_task.newMsg(path_len, "Public", text, offline_queue_len); + // Get the channel name from the channel index + const char* channel_name = "Unknown"; + ChannelDetails channel_details; + if (getChannel(channel_idx, channel_details)) { + channel_name = channel_details.name; + } + ui_task.newMsg(path_len, channel_name, text, offline_queue_len); #endif } From 4c6f146b8b913f4036aad3219f32efc98276ab2b Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 08:30:53 +0200 Subject: [PATCH 052/103] wioe5: integrate sensor in sensor mgr --- src/helpers/stm32/InternalFileSystem.cpp | 7 +++++-- variants/wio-e5-mini/target.cpp | 9 ++++----- variants/wio-e5-mini/target.h | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/helpers/stm32/InternalFileSystem.cpp b/src/helpers/stm32/InternalFileSystem.cpp index 2714ec6b..dc032eb9 100644 --- a/src/helpers/stm32/InternalFileSystem.cpp +++ b/src/helpers/stm32/InternalFileSystem.cpp @@ -126,11 +126,14 @@ InternalFileSystem::InternalFileSystem(void) bool InternalFileSystem::begin(void) { + volatile bool format_fs; #ifdef FORMAT_FS - this->format(); + format_fs = true; + #else + format_fs = false; // you can always use debugger to force formatting ;) #endif // failed to mount, erase all sector then format and mount again - if ( !Adafruit_LittleFS::begin() ) + if ( format_fs || !Adafruit_LittleFS::begin() ) { // lfs format this->format(); diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index 7a2b0d39..e3dd2ec7 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,8 +18,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -BME280I2C bme; -WIOE5SensorManager sensors(bme); +WIOE5SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 @@ -73,9 +72,9 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); - BME280::PresUnit presUnit(BME280::PresUnit_bar); + BME280::PresUnit presUnit(BME280::PresUnit_hPa); - _bme->read(pres, temp, hum, tempUnit, presUnit); + bme.read(pres, temp, hum, tempUnit, presUnit); telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); @@ -85,7 +84,7 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } bool WIOE5SensorManager::begin() { - has_bme = _bme->begin(); + has_bme = bme.begin(); return has_bme; } \ No newline at end of file diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index daf7e174..4b510d56 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -25,11 +25,11 @@ public: }; class WIOE5SensorManager : public SensorManager { - BME280I2C* _bme; + BME280I2C bme; bool has_bme = false; public: - WIOE5SensorManager(BME280I2C& bme) : _bme(&bme) {} + WIOE5SensorManager() {} bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; }; From c42e414a0962c5f9fb053467af4db1e04884e5f7 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 14:12:22 +0200 Subject: [PATCH 053/103] micronmea: was using global rtc_clock to sync instead of _clock --- src/helpers/sensors/MicroNMEALocationProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index ee6e43eb..a4a2f5d6 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -98,7 +98,7 @@ public : next_check = millis() + 1000; if (_time_sync_needed && time_valid > 2) { if (_clock != NULL) { - rtc_clock.setCurrentTime(getTimestamp()); + _clock.setCurrentTime(getTimestamp()); _time_sync_needed = false; } } From 1ac03f5592635875abdf1e53cadcdf307a876ddf Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 15:32:02 +0200 Subject: [PATCH 054/103] t1000: remove sync custom var --- .../sensors/MicroNMEALocationProvider.h | 2 +- variants/t1000-e/target.cpp | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index a4a2f5d6..e8b09d9a 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -98,7 +98,7 @@ public : next_check = millis() + 1000; if (_time_sync_needed && time_valid > 2) { if (_clock != NULL) { - _clock.setCurrentTime(getTimestamp()); + _clock->setCurrentTime(getTimestamp()); _time_sync_needed = false; } } diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 296551e1..29ca1acd 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -179,26 +179,14 @@ void T1000SensorManager::loop() { } } -int T1000SensorManager::getNumSettings() const { return 2; } // just one supported: "gps" (power switch) +int T1000SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) const char* T1000SensorManager::getSettingName(int i) const { - switch (i) { - case 0: - return "gps"; - break; - case 1: - return "sync"; - break; - default: - return NULL; - break; - } + return i == 0 ? "gps" : NULL; } const char* T1000SensorManager::getSettingValue(int i) const { if (i == 0) { return gps_active ? "1" : "0"; - } else if (i == 1) { - return _nmea->waitingTimeSync() ? "1" : "0"; } return NULL; } @@ -210,9 +198,6 @@ bool T1000SensorManager::setSettingValue(const char* name, const char* value) { start_gps(); } return true; - } else if (strcmp(name, "sync") == 0) { - _nmea->syncTime(); // whatever the value ... - return true; } return false; // not supported } From 6481ab1e31ca4c3f65e69c288cdd8c982209887b Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 1 Jun 2025 23:55:57 +1000 Subject: [PATCH 055/103] * MicroNMEALocationProvider: clock param now NULL by default --- src/helpers/sensors/MicroNMEALocationProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index e8b09d9a..5a2c59d3 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -27,7 +27,7 @@ class MicroNMEALocationProvider : public LocationProvider { long time_valid = 0; public : - MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = &rtc_clock, int pin_reset = GPS_RESET, int pin_en = GPS_EN) : + 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) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); From 053aa0b3d690dced2628b58bea5ad48c77846632 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 09:24:10 -0700 Subject: [PATCH 056/103] Adding clang-format --- .clang-format | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..80c7a10a --- /dev/null +++ b/.clang-format @@ -0,0 +1,84 @@ +# .clang-format +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Stroustrup +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 110 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +IncludeBlocks: Regroup +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 100000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 2 +UseTab: Never +AlignEscapedNewlines: LeftWithLastLine \ No newline at end of file From 5bf58127553b0fe0ba4d9f8355124f2764c60293 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 09:24:32 -0700 Subject: [PATCH 057/103] Removing debug mode --- variants/t114/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index c4569cf8..fd9d6f34 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -75,7 +75,7 @@ build_flags = ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 - -D MESH_DEBUG=1 +; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} + + From f7f96ad372727e7a981480398ca43952bf9e5065 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 09:25:17 -0700 Subject: [PATCH 058/103] Reformatting code --- examples/companion_radio/Button.cpp | 229 ++++---- examples/companion_radio/Button.h | 112 ++-- examples/companion_radio/MyMesh.cpp | 790 +++++++++++---------------- examples/companion_radio/MyMesh.h | 336 +++++------- examples/companion_radio/NodePrefs.h | 16 +- examples/companion_radio/UITask.h | 47 +- examples/simple_secure_chat/main.cpp | 428 +++++++++------ 7 files changed, 919 insertions(+), 1039 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index ec1f0f69..5de4b702 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -1,125 +1,142 @@ #include "Button.h" -Button::Button(uint8_t pin, bool activeState) - : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) { - _currentState = false; // Initialize as not pressed - _lastState = _currentState; +Button::Button(uint8_t pin, bool activeState) + : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) +{ + _currentState = false; // Initialize as not pressed + _lastState = _currentState; } Button::Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold) - : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) { - _currentState = false; // Initialize as not pressed - _lastState = _currentState; + : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) +{ + _currentState = false; // Initialize as not pressed + _lastState = _currentState; } -void Button::begin() { - _currentState = readButton(); - _lastState = _currentState; +void Button::begin() +{ + _currentState = readButton(); + _lastState = _currentState; } -void Button::update() { - uint32_t now = millis(); - - // Read button at specified interval - if (now - _lastReadTime < BUTTON_READ_INTERVAL_MS) { - return; +void Button::update() +{ + uint32_t now = millis(); + + // Read button at specified interval + if (now - _lastReadTime < BUTTON_READ_INTERVAL_MS) { + return; + } + _lastReadTime = now; + + bool newState = readButton(); + + // Check if state has changed + if (newState != _lastState) { + _stateChangeTime = now; + } + + // Debounce check + if ((now - _stateChangeTime) > BUTTON_DEBOUNCE_TIME_MS) { + if (newState != _currentState) { + _currentState = newState; + handleStateChange(); } - _lastReadTime = now; - - bool newState = readButton(); - - // Check if state has changed - if (newState != _lastState) { - _stateChangeTime = now; + } + + _lastState = newState; + + // Handle multi-click timeout + if (_state == WAITING_FOR_MULTI_CLICK && (now - _releaseTime) > BUTTON_CLICK_TIMEOUT_MS) { + // Timeout reached, process the clicks + if (_clickCount == 1) { + triggerEvent(SHORT_PRESS); } - - // Debounce check - if ((now - _stateChangeTime) > BUTTON_DEBOUNCE_TIME_MS) { - if (newState != _currentState) { - _currentState = newState; - handleStateChange(); - } + else if (_clickCount == 2) { + triggerEvent(DOUBLE_PRESS); } - - _lastState = newState; - - // Handle multi-click timeout - if (_state == WAITING_FOR_MULTI_CLICK && (now - _releaseTime) > BUTTON_CLICK_TIMEOUT_MS) { - // Timeout reached, process the clicks - if (_clickCount == 1) { - triggerEvent(SHORT_PRESS); - } else if (_clickCount == 2) { - triggerEvent(DOUBLE_PRESS); - } else if (_clickCount >= 3) { - triggerEvent(TRIPLE_PRESS); - } - _clickCount = 0; + else if (_clickCount >= 3) { + triggerEvent(TRIPLE_PRESS); + } + _clickCount = 0; + _state = IDLE; + } + + // Handle long press while button is held + if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { + triggerEvent(LONG_PRESS); + _state = IDLE; // Prevent multiple press events + _clickCount = 0; + } +} + +bool Button::readButton() +{ + if (_isAnalog) { + return (analogRead(_pin) < _analogThreshold); + } + else { + return (digitalRead(_pin) == _activeState); + } +} + +void Button::handleStateChange() +{ + uint32_t now = millis(); + + if (_currentState) { + // Button pressed + _pressTime = now; + _state = PRESSED; + triggerEvent(ANY_PRESS); + } + else { + // Button released + if (_state == PRESSED) { + uint32_t pressDuration = now - _pressTime; + + if (pressDuration < BUTTON_LONG_PRESS_TIME_MS) { + // Short press detected + _clickCount++; + _releaseTime = now; + _state = WAITING_FOR_MULTI_CLICK; + } + else { + // Long press already handled in update() _state = IDLE; - } - - // Handle long press while button is held - if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { - triggerEvent(LONG_PRESS); - _state = IDLE; // Prevent multiple press events _clickCount = 0; + } } + } } -bool Button::readButton() { - if (_isAnalog) { - return (analogRead(_pin) < _analogThreshold); - } else { - return (digitalRead(_pin) == _activeState); - } -} +void Button::triggerEvent(EventType event) +{ + _lastEvent = event; -void Button::handleStateChange() { - uint32_t now = millis(); - - if (_currentState) { - // Button pressed - _pressTime = now; - _state = PRESSED; - triggerEvent(ANY_PRESS); - } else { - // Button released - if (_state == PRESSED) { - uint32_t pressDuration = now - _pressTime; - - if (pressDuration < BUTTON_LONG_PRESS_TIME_MS) { - // Short press detected - _clickCount++; - _releaseTime = now; - _state = WAITING_FOR_MULTI_CLICK; - } else { - // Long press already handled in update() - _state = IDLE; - _clickCount = 0; - } - } - } -} - -void Button::triggerEvent(EventType event) { - _lastEvent = event; - - switch (event) { - case ANY_PRESS: - if (_onAnyPress) _onAnyPress(); - break; - case SHORT_PRESS: - if (_onShortPress) _onShortPress(); - break; - case DOUBLE_PRESS: - if (_onDoublePress) _onDoublePress(); - break; - case TRIPLE_PRESS: - if (_onTriplePress) _onTriplePress(); - break; - case LONG_PRESS: - if (_onLongPress) _onLongPress(); - break; - default: - break; - } + switch (event) { + case ANY_PRESS: + if (_onAnyPress) + _onAnyPress(); + break; + case SHORT_PRESS: + if (_onShortPress) + _onShortPress(); + break; + case DOUBLE_PRESS: + if (_onDoublePress) + _onDoublePress(); + break; + case TRIPLE_PRESS: + if (_onTriplePress) + _onTriplePress(); + break; + case LONG_PRESS: + if (_onLongPress) + _onLongPress(); + break; + default: + break; + } } \ No newline at end of file diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h index 47c792bd..85564593 100644 --- a/examples/companion_radio/Button.h +++ b/examples/companion_radio/Button.h @@ -4,74 +4,62 @@ #include // Button timing configuration -#define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms -#define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click -#define BUTTON_LONG_PRESS_TIME_MS 3000 // Time to trigger long press (3 seconds) -#define BUTTON_READ_INTERVAL_MS 10 // How often to read the button +#define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms +#define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click +#define BUTTON_LONG_PRESS_TIME_MS 3000 // Time to trigger long press (3 seconds) +#define BUTTON_READ_INTERVAL_MS 10 // How often to read the button class Button { public: - enum EventType { - NONE, - SHORT_PRESS, - DOUBLE_PRESS, - TRIPLE_PRESS, - LONG_PRESS, - ANY_PRESS - }; + enum EventType { NONE, SHORT_PRESS, DOUBLE_PRESS, TRIPLE_PRESS, LONG_PRESS, ANY_PRESS }; - using EventCallback = std::function; + using EventCallback = std::function; - Button(uint8_t pin, bool activeState = LOW); - Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold = 20); - - void begin(); - void update(); - - // Set callbacks for different events - void onShortPress(EventCallback callback) { _onShortPress = callback; } - void onDoublePress(EventCallback callback) { _onDoublePress = callback; } - void onTriplePress(EventCallback callback) { _onTriplePress = callback; } - void onLongPress(EventCallback callback) { _onLongPress = callback; } - void onAnyPress(EventCallback callback) { _onAnyPress = callback; } - - // State getters - bool isPressed() const { return _currentState; } - EventType getLastEvent() const { return _lastEvent; } + Button(uint8_t pin, bool activeState = LOW); + Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold = 20); + + void begin(); + void update(); + + // Set callbacks for different events + void onShortPress(EventCallback callback) { _onShortPress = callback; } + void onDoublePress(EventCallback callback) { _onDoublePress = callback; } + void onTriplePress(EventCallback callback) { _onTriplePress = callback; } + void onLongPress(EventCallback callback) { _onLongPress = callback; } + void onAnyPress(EventCallback callback) { _onAnyPress = callback; } + + // State getters + bool isPressed() const { return _currentState; } + EventType getLastEvent() const { return _lastEvent; } private: - enum State { - IDLE, - PRESSED, - RELEASED, - WAITING_FOR_MULTI_CLICK - }; + enum State { IDLE, PRESSED, RELEASED, WAITING_FOR_MULTI_CLICK }; - uint8_t _pin; - bool _activeState; - bool _isAnalog; - uint16_t _analogThreshold; - - State _state = IDLE; - bool _currentState; - bool _lastState; - - uint32_t _stateChangeTime = 0; - uint32_t _pressTime = 0; - uint32_t _releaseTime = 0; - uint32_t _lastReadTime = 0; - - uint8_t _clickCount = 0; - EventType _lastEvent = NONE; - - // Callbacks - EventCallback _onShortPress = nullptr; - EventCallback _onDoublePress = nullptr; - EventCallback _onTriplePress = nullptr; - EventCallback _onLongPress = nullptr; - EventCallback _onAnyPress = nullptr; - - bool readButton(); - void handleStateChange(); - void triggerEvent(EventType event); + uint8_t _pin; + bool _activeState; + bool _isAnalog; + uint16_t _analogThreshold; + + State _state = IDLE; + bool _currentState; + bool _lastState; + + uint32_t _stateChangeTime = 0; + uint32_t _pressTime = 0; + uint32_t _releaseTime = 0; + uint32_t _lastReadTime = 0; + + uint8_t _clickCount = 0; + EventType _lastEvent = NONE; + + // Callbacks + EventCallback _onShortPress = nullptr; + EventCallback _onDoublePress = nullptr; + EventCallback _onTriplePress = nullptr; + EventCallback _onLongPress = nullptr; + EventCallback _onAnyPress = nullptr; + + bool readButton(); + void handleStateChange(); + void triggerEvent(EventType event); }; \ No newline at end of file diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6e9688a5..db40169f 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1,19 +1,83 @@ -#include // needed for PlatformIO -#include #include "MyMesh.h" +#include // needed for PlatformIO +#include + +#define CMD_APP_START 1 +#define CMD_SEND_TXT_MSG 2 +#define CMD_SEND_CHANNEL_TXT_MSG 3 +#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) +#define CMD_GET_DEVICE_TIME 5 +#define CMD_SET_DEVICE_TIME 6 +#define CMD_SEND_SELF_ADVERT 7 +#define CMD_SET_ADVERT_NAME 8 +#define CMD_ADD_UPDATE_CONTACT 9 +#define CMD_SYNC_NEXT_MESSAGE 10 +#define CMD_SET_RADIO_PARAMS 11 +#define CMD_SET_RADIO_TX_POWER 12 +#define CMD_RESET_PATH 13 +#define CMD_SET_ADVERT_LATLON 14 +#define CMD_REMOVE_CONTACT 15 +#define CMD_SHARE_CONTACT 16 +#define CMD_EXPORT_CONTACT 17 +#define CMD_IMPORT_CONTACT 18 +#define CMD_REBOOT 19 +#define CMD_GET_BATTERY_VOLTAGE 20 +#define CMD_SET_TUNING_PARAMS 21 +#define CMD_DEVICE_QEURY 22 +#define CMD_EXPORT_PRIVATE_KEY 23 +#define CMD_IMPORT_PRIVATE_KEY 24 +#define CMD_SEND_RAW_DATA 25 +#define CMD_SEND_LOGIN 26 +#define CMD_SEND_STATUS_REQ 27 +#define CMD_HAS_CONNECTION 28 +#define CMD_LOGOUT 29 // 'Disconnect' +#define CMD_GET_CONTACT_BY_KEY 30 +#define CMD_GET_CHANNEL 31 +#define CMD_SET_CHANNEL 32 +#define CMD_SIGN_START 33 +#define CMD_SIGN_DATA 34 +#define CMD_SIGN_FINISH 35 +#define CMD_SEND_TRACE_PATH 36 +#define CMD_SET_DEVICE_PIN 37 +#define CMD_SET_OTHER_PARAMS 38 +#define CMD_SEND_TELEMETRY_REQ 39 +#define CMD_GET_CUSTOM_VARS 40 +#define CMD_SET_CUSTOM_VAR 41 + +#define RESP_CODE_OK 0 +#define RESP_CODE_ERR 1 +#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS +#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) +#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS +#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START +#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG +#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME +#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE +#define RESP_CODE_EXPORT_CONTACT 11 +#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE +#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY +#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY +#define RESP_CODE_DISABLED 15 +#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL +#define RESP_CODE_SIGN_START 19 +#define RESP_CODE_SIGNATURE 20 +#define RESP_CODE_CUSTOM_VARS 21 + #ifdef DISPLAY_CLASS - #include "UITask.h" +#include "UITask.h" #endif void MyMesh::loadMainIdentity() { - if (!_identity_store->load("_main", self_id)) - { + if (!_identity_store->load("_main", self_id)) { self_id = radio_new_identity(); // create new random identity int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) - { // reserved id hashes + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes self_id = radio_new_identity(); count++; } @@ -28,18 +92,15 @@ bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) void MyMesh::loadContacts() { - if (_fs->exists("/contacts3")) - { + if (_fs->exists("/contacts3")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/contacts3", "r"); #else File file = _fs->open("/contacts3"); #endif - if (file) - { + if (file) { bool full = false; - while (!full) - { + while (!full) { ContactInfo c; uint8_t pub_key[32]; uint8_t unused; @@ -79,14 +140,12 @@ void MyMesh::saveContacts() #else File file = _fs->open("/contacts3", "w", true); #endif - if (file) - { + if (file) { ContactsIterator iter; ContactInfo c; uint8_t unused = 0; - while (iter.hasNext(this, c)) - { + while (iter.hasNext(this, c)) { bool success = (file.write(c.id.pub_key, 32) == 32); success = success && (file.write((uint8_t *)&c.name, 32) == 32); success = success && (file.write(&c.type, 1) == 1); @@ -109,19 +168,16 @@ void MyMesh::saveContacts() void MyMesh::loadChannels() { - if (_fs->exists("/channels2")) - { + if (_fs->exists("/channels2")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "r"); #else File file = _fs->open("/channels2"); #endif - if (file) - { + if (file) { bool full = false; uint8_t channel_idx = 0; - while (!full) - { + while (!full) { ChannelDetails ch; uint8_t unused[4]; @@ -132,12 +188,10 @@ void MyMesh::loadChannels() if (!success) break; // EOF - if (setChannel(channel_idx, ch)) - { + if (setChannel(channel_idx, ch)) { channel_idx++; } - else - { + else { full = true; } } @@ -156,15 +210,13 @@ void MyMesh::saveChannels() #else File file = _fs->open("/channels2", "w", true); #endif - if (file) - { + if (file) { uint8_t channel_idx = 0; ChannelDetails ch; uint8_t unused[4]; memset(unused, 0, 4); - while (getChannel(channel_idx, ch)) - { + while (getChannel(channel_idx, ch)) { bool success = (file.write(unused, 4) == 4); success = success && (file.write((uint8_t *)ch.name, 32) == 32); success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); @@ -187,15 +239,13 @@ int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); - if (_fs->exists(path)) - { + if (_fs->exists(path)) { #if defined(RP2040_PLATFORM) File f = _fs->open(path, "r"); #else File f = _fs->open(path); #endif - if (f) - { + if (f) { int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! f.close(); return len; @@ -222,8 +272,7 @@ bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_bu #else File f = _fs->open(path, "w", true); #endif - if (f) - { + if (f) { int n = f.write(src_buf, len); f.close(); if (n == len) @@ -294,8 +343,7 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, i += 32; memcpy(&contact.last_advert_timestamp, &frame[i], 4); i += 4; - if (i + 8 >= len) - { // optional fields + if (i + 8 >= len) { // optional fields memcpy(&contact.gps_lat, &frame[i], 4); i += 4; memcpy(&contact.gps_lon, &frame[i], 4); @@ -305,12 +353,10 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { - if (offline_queue_len >= OFFLINE_QUEUE_SIZE) - { + if (offline_queue_len >= OFFLINE_QUEUE_SIZE) { MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); } - else - { + else { offline_queue[offline_queue_len].len = len; memcpy(offline_queue[offline_queue_len].buf, frame, len); offline_queue_len++; @@ -318,14 +364,12 @@ void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) } int MyMesh::getFromOfflineQueue(uint8_t frame[]) { - if (offline_queue_len > 0) - { // check offline queue + if (offline_queue_len > 0) { // check offline queue size_t len = offline_queue[0].len; // take from top of queue memcpy(frame, offline_queue[0].buf, len); offline_queue_len--; - for (int i = 0; i < offline_queue_len; i++) - { // delete top item from queue + for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue offline_queue[i] = offline_queue[i + 1]; } return len; @@ -352,8 +396,7 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { - if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) - { + if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; out_frame[i++] = PUSH_CODE_LOG_RX_DATA; out_frame[i++] = (int8_t)(snr * 4); @@ -372,21 +415,17 @@ bool MyMesh::isAutoAddEnabled() const void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) { - if (_serial->isConnected()) - { - if (!isAutoAddEnabled() && is_new) - { + if (_serial->isConnected()) { + if (!isAutoAddEnabled() && is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); } - else - { + else { out_frame[0] = PUSH_CODE_ADVERT; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::newContactMessage); #endif @@ -407,10 +446,8 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) bool MyMesh::processAck(const uint8_t *data) { // see if matches any in a table - for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) - { - if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) - { // got an ACK from recipient + for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) { + if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient out_frame[0] = PUSH_CODE_SEND_CONFIRMED; memcpy(&out_frame[1], data, 4); uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent; @@ -425,18 +462,17 @@ bool MyMesh::processAck(const uint8_t *data) return checkConnectionsAck(data); } -void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) +void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, + uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) { int i = 0; - if (app_target_ver >= 3) - { + if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 } - else - { + else { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; } memcpy(&out_frame[i], from.id.pub_key, 6); @@ -445,28 +481,24 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe out_frame[i++] = txt_type; memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; - if (extra_len > 0) - { + if (extra_len > 0) { memcpy(&out_frame[i], extra, extra_len); i += extra_len; } int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) - { + if (i + tlen > MAX_FRAME_SIZE) { tlen = MAX_FRAME_SIZE - i; } memcpy(&out_frame[i], text, tlen); i += tlen; addToOfflineQueue(out_frame, i); - if (_serial->isConnected()) - { + if (_serial->isConnected()) { uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::contactMessage); #endif @@ -476,19 +508,22 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #endif } -void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const char *text) +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 queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); } -void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const char *text) +void MyMesh::onCommandDataRecv(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 queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); } -void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) +void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) { markConnectionActive(from); // from.sync_since change needs to be persisted @@ -496,18 +531,17 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text); } -void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, const char *text) +void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, + const char *text) { int i = 0; - if (app_target_ver >= 3) - { + if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 } - else - { + else { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } @@ -518,22 +552,19 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe memcpy(&out_frame[i], ×tamp, 4); i += 4; int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) - { + if (i + tlen > MAX_FRAME_SIZE) { tlen = MAX_FRAME_SIZE - i; } memcpy(&out_frame[i], text, tlen); i += tlen; addToOfflineQueue(out_frame, i); - if (_serial->isConnected()) - { + if (_serial->isConnected()) { uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::channelMessage); #endif @@ -543,48 +574,42 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe #endif } -uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) +uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, + uint8_t len, uint8_t *reply) { - if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) - { + if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { uint8_t permissions = 0; uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) - if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) { permissions = TELEM_PERM_BASE; } - else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { permissions = cp & TELEM_PERM_BASE; } - if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_LOCATION; } - else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_LOCATION; } - if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_ENVIRONMENT; } - else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_ENVIRONMENT; } - if (permissions & TELEM_PERM_BASE) - { // only respond if base permission bit is set + if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific sensors.querySensors(permissions, telemetry); - memcpy(reply, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') + memcpy(reply, &sender_timestamp, + 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') uint8_t tlen = telemetry.getSize(); memcpy(&reply[4], telemetry.getBuffer(), tlen); @@ -599,24 +624,20 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint32_t tag; memcpy(&tag, data, 4); - if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) - { // check for login response + if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) { // check for login response // yes, is response to pending sendLogin() pending_login = 0; int i = 0; - if (memcmp(&data[4], "OK", 2) == 0) - { // legacy Repeater login OK response + if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } - else if (data[4] == RESP_SERVER_LOGIN_OK) - { // new login response + else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; - if (keep_alive_secs > 0) - { + if (keep_alive_secs > 0) { startConnection(contact, keep_alive_secs); } out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; @@ -626,8 +647,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp } - else - { + else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved memcpy(&out_frame[i], contact.id.pub_key, 6); @@ -635,11 +655,11 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, } _serial->writeFrame(out_frame, i); } - else if (len > 4 && // check for status response - pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme - // FUTURE: tag == pending_status - ) - { + else if (len > 4 && // check for status response + pending_status && + memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme + // FUTURE: tag == pending_status + ) { pending_status = 0; int i = 0; @@ -651,8 +671,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, i += (len - 4); _serial->writeFrame(out_frame, i); } - else if (len > 4 && tag == pending_telemetry) - { // check for telemetry response + else if (len > 4 && tag == pending_telemetry) { // check for telemetry response pending_telemetry = 0; int i = 0; @@ -668,8 +687,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, void MyMesh::onRawDataRecv(mesh::Packet *packet) { - if (packet->payload_len + 4 > sizeof(out_frame)) - { + if (packet->payload_len + 4 > sizeof(out_frame)) { MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len); return; } @@ -681,17 +699,16 @@ void MyMesh::onRawDataRecv(mesh::Packet *packet) memcpy(&out_frame[i], packet->payload, packet->payload_len); i += packet->payload_len; - if (_serial->isConnected()) - { + if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); } - else - { + else { MESH_DEBUG_PRINTLN("onRawDataRecv(), data received while app offline"); } } -void MyMesh::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) +void MyMesh::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) { int i = 0; out_frame[i++] = PUSH_CODE_TRACE_DATA; @@ -708,12 +725,10 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, i += path_len; out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node) - if (_serial->isConnected()) - { + if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); } - else - { + else { MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); } } @@ -725,16 +740,15 @@ uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const { return SEND_TIMEOUT_BASE_MILLIS + - ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * + (path_len + 1)); } -void MyMesh::onSendTimeout() -{ -} +void MyMesh::onSendTimeout() {} MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) - : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), - telemetry(MAX_PACKET_PAYLOAD - 4) + : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; offline_queue_len = 0; @@ -764,8 +778,7 @@ void MyMesh::loadPrefsInt(const char *filename) #else File file = _fs->open(filename); #endif - if (file) - { + if (file) { uint8_t pad[8]; file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 @@ -828,36 +841,30 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) #endif // load persisted prefs - if (_fs->exists("/new_prefs")) - { + if (_fs->exists("/new_prefs")) { loadPrefsInt("/new_prefs"); // new filename } - else if (_fs->exists("/node_prefs")) - { + else if (_fs->exists("/node_prefs")) { loadPrefsInt("/node_prefs"); savePrefs(); // save to new filename _fs->remove("/node_prefs"); // remove old } #ifdef BLE_PIN_CODE - if (_prefs.ble_pin == 0) - { + if (_prefs.ble_pin == 0) { #ifdef DISPLAY_CLASS - if (has_display) - { + if (has_display) { StdRNG rng; _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session } - else - { + else { _active_ble_pin = BLE_PIN_CODE; // otherwise static pin } #else _active_ble_pin = BLE_PIN_CODE; // otherwise static pin #endif } - else - { + else { _active_ble_pin = _prefs.ble_pin; } #else @@ -875,12 +882,18 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) radio_set_tx_power(_prefs.tx_power_dbm); } -const char *MyMesh::getNodeName() { return _prefs.node_name; } +const char *MyMesh::getNodeName() +{ + return _prefs.node_name; +} NodePrefs *MyMesh::getNodePrefs() { return &_prefs; } -uint32_t MyMesh::getBLEPin() { return _active_ble_pin; } +uint32_t MyMesh::getBLEPin() +{ + return _active_ble_pin; +} void MyMesh::startInterface(BaseSerialInterface &serial) { @@ -898,8 +911,7 @@ void MyMesh::savePrefs() #else File file = _fs->open("/new_prefs", "w", true); #endif - if (file) - { + if (file) { uint8_t pad[8]; memset(pad, 0, sizeof(pad)); @@ -928,9 +940,8 @@ void MyMesh::savePrefs() void MyMesh::handleCmdFrame(size_t len) { - if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) - { // sent when app establishes connection - app_target_ver = cmd_frame[1]; // which version of protocol does app understand + if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection + app_target_ver = cmd_frame[1]; // which version of protocol does app understand int i = 0; out_frame[i++] = RESP_CODE_DEVICE_INFO; @@ -948,8 +959,8 @@ void MyMesh::handleCmdFrame(size_t len) i += 20; _serial->writeFrame(out_frame, i); } - else if (cmd_frame[0] == CMD_APP_START && len >= 8) - { // sent when app establishes connection, respond with node ID + else if (cmd_frame[0] == CMD_APP_START && + len >= 8) { // sent when app establishes connection, respond with node ID // cmd_frame[1..7] reserved future char *app_name = (char *)&cmd_frame[8]; cmd_frame[len] = 0; // make app_name null terminated @@ -971,9 +982,10 @@ void MyMesh::handleCmdFrame(size_t len) i += 4; memcpy(&out_frame[i], &lon, 4); i += 4; - out_frame[i++] = 0; // reserved - out_frame[i++] = 0; // reserved - out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ + out_frame[i++] = 0; // reserved + out_frame[i++] = 0; // reserved + out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | + (_prefs.telemetry_mode_base); // v5+ out_frame[i++] = _prefs.manual_add_contacts; uint32_t freq = _prefs.freq * 1000; @@ -990,8 +1002,7 @@ void MyMesh::handleCmdFrame(size_t len) i += tlen; _serial->writeFrame(out_frame, i); } - else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) - { + else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { int i = 1; uint8_t txt_type = cmd_frame[i++]; uint8_t attempt = cmd_frame[i++]; @@ -1001,32 +1012,26 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t *pub_key_prefix = &cmd_frame[i]; i += 6; ContactInfo *recipient = lookupContactByPubKey(pub_key_prefix, 6); - if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) - { + if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) { char *text = (char *)&cmd_frame[i]; int tlen = len - i; uint32_t est_timeout; text[tlen] = 0; // ensure null int result; uint32_t expected_ack; - if (txt_type == TXT_TYPE_CLI_DATA) - { + if (txt_type == TXT_TYPE_CLI_DATA) { result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); expected_ack = 0; // no Ack expected } - else - { + else { result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); } // TODO: add expected ACK to table - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { - if (expected_ack) - { + else { + if (expected_ack) { expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table expected_ack_table[next_ack_idx].ack = expected_ack; next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; @@ -1039,13 +1044,13 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { - writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* + else { + writeErrFrame(recipient == NULL + ? ERR_CODE_NOT_FOUND + : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* } } - else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) - { // send GroupChannel msg + else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1054,38 +1059,29 @@ void MyMesh::handleCmdFrame(size_t len) i += 4; const char *text = (char *)&cmd_frame[i]; - if (txt_type != TXT_TYPE_PLAIN) - { + if (txt_type != TXT_TYPE_PLAIN) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); } - else - { + else { ChannelDetails channel; bool success = getChannel(channel_idx, channel); - if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) - { + if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } } - else if (cmd_frame[0] == CMD_GET_CONTACTS) - { // get Contact list - if (_iter_started) - { + else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list + if (_iter_started) { writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy } - else - { - if (len >= 5) - { // has optional 'since' param + else { + if (len >= 5) { // has optional 'since' param memcpy(&_iter_filter_since, &cmd_frame[1], 4); } - else - { + else { _iter_filter_since = 0; } @@ -1101,8 +1097,7 @@ void MyMesh::handleCmdFrame(size_t len) _most_recent_lastmod = 0; } } - else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) - { + else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { int nlen = len - 1; if (nlen > sizeof(_prefs.node_name) - 1) nlen = sizeof(_prefs.node_name) - 1; // max len @@ -1111,169 +1106,134 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) - { + else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { int32_t lat, lon, alt = 0; memcpy(&lat, &cmd_frame[1], 4); memcpy(&lon, &cmd_frame[5], 4); - if (len >= 13) - { + if (len >= 13) { memcpy(&alt, &cmd_frame[9], 4); // for FUTURE support } - if (lat <= 90 * 1E6 && lat >= -90 * 1E6 && lon <= 180 * 1E6 && lon >= -180 * 1E6) - { + if (lat <= 90 * 1E6 && lat >= -90 * 1E6 && lon <= 180 * 1E6 && lon >= -180 * 1E6) { sensors.node_lat = ((double)lat) / 1000000.0; sensors.node_lon = ((double)lon) / 1000000.0; savePrefs(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate } } - else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) - { + else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { uint8_t reply[5]; reply[0] = RESP_CODE_CURR_TIME; uint32_t now = getRTCClock()->getCurrentTime(); memcpy(&reply[1], &now, 4); _serial->writeFrame(reply, 5); } - else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) - { + else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { uint32_t secs; memcpy(&secs, &cmd_frame[1], 4); uint32_t curr = getRTCClock()->getCurrentTime(); - if (secs >= curr) - { + if (secs >= curr) { getRTCClock()->setCurrentTime(secs); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) - { + else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { - if (len >= 2 && cmd_frame[1] == 1) - { // optional param (1 = flood, 0 = zero hop) + if (pkt) { + if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop) sendFlood(pkt); } - else - { + else { sendZeroHop(pkt); } writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) - { + else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { recipient->out_path_len = -1; // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact } } - else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) - { + else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { updateContactFromFrame(*recipient, cmd_frame, len); // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { ContactInfo contact; updateContactFromFrame(contact, cmd_frame, len); contact.lastmod = getRTCClock()->getCurrentTime(); contact.sync_since = 0; - if (addContact(contact)) - { + if (addContact(contact)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } } - else if (cmd_frame[0] == CMD_REMOVE_CONTACT) - { + else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient && removeContact(*recipient)) - { + if (recipient && removeContact(*recipient)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove } } - else if (cmd_frame[0] == CMD_SHARE_CONTACT) - { + else if (cmd_frame[0] == CMD_SHARE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { - if (shareContactZeroHop(*recipient)) - { + if (recipient) { + if (shareContactZeroHop(*recipient)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) - { + else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (contact) - { + if (contact) { writeContactRespFrame(RESP_CODE_CONTACT, *contact); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } - else if (cmd_frame[0] == CMD_EXPORT_CONTACT) - { - if (len < 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { + if (len < 1 + PUB_KEY_SIZE) { // export SELF auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { + if (pkt) { pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode out_frame[0] = RESP_CODE_EXPORT_CONTACT; @@ -1281,56 +1241,45 @@ void MyMesh::handleCmdFrame(size_t len) releasePacket(pkt); // undo the obtainNewPacket() _serial->writeFrame(out_frame, out_len + 1); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); // Error } } - else - { + else { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); uint8_t out_len; - if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) - { + if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) { out_frame[0] = RESP_CODE_EXPORT_CONTACT; _serial->writeFrame(out_frame, out_len + 1); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } } - else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) - { - if (importContact(&cmd_frame[1], len - 1)) - { + else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { + if (importContact(&cmd_frame[1], len - 1)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) - { + else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { int out_len; - if ((out_len = getFromOfflineQueue(out_frame)) > 0) - { + if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); #ifdef DISPLAY_CLASS ui_task.msgRead(offline_queue_len); #endif } - else - { + else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); } } - else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { int i = 1; uint32_t freq; memcpy(&freq, &cmd_frame[i], 4); @@ -1341,8 +1290,8 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t sf = cmd_frame[i++]; uint8_t cr = cmd_frame[i++]; - if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) - { + if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && + bw <= 500000) { _prefs.sf = sf; _prefs.cr = cr; _prefs.freq = (float)freq / 1000.0; @@ -1350,32 +1299,29 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, + (uint32_t)cr); writeOKFrame(); } - else - { - MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + else { + MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, + (uint32_t)cr); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) - { - if (cmd_frame[1] > MAX_LORA_TX_POWER) - { + else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { + if (cmd_frame[1] > MAX_LORA_TX_POWER) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - else - { + else { _prefs.tx_power_dbm = cmd_frame[1]; savePrefs(); radio_set_tx_power(_prefs.tx_power_dbm); writeOKFrame(); } } - else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { int i = 1; uint32_t rx, af; memcpy(&rx, &cmd_frame[i], 4); @@ -1387,11 +1333,9 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { _prefs.manual_add_contacts = cmd_frame[1]; - if (len >= 3) - { + if (len >= 3) { _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ _prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03; _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03; @@ -1399,24 +1343,20 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) - { - if (dirty_contacts_expiry) - { // is there are pending dirty contacts write needed? + else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { + if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? saveContacts(); } board.reboot(); } - else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) - { + else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { uint8_t reply[3]; reply[0] = RESP_CODE_BATTERY_VOLTAGE; uint16_t battery_millivolts = board.getBattMilliVolts(); memcpy(&reply[1], &battery_millivolts, 2); _serial->writeFrame(reply, 3); } - else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) - { + else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { #if ENABLE_PRIVATE_KEY_EXPORT uint8_t reply[65]; reply[0] = RESP_CODE_PRIVATE_KEY; @@ -1426,64 +1366,52 @@ void MyMesh::handleCmdFrame(size_t len) writeDisabledFrame(); #endif } - else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) - { + else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { #if ENABLE_PRIVATE_KEY_IMPORT mesh::LocalIdentity identity; identity.readFrom(&cmd_frame[1], 64); - if (saveMainIdentity(identity)) - { + if (saveMainIdentity(identity)) { self_id = identity; writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_FILE_IO_ERROR); } #else writeDisabledFrame(); #endif } - else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) - { + else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { int i = 1; int8_t path_len = cmd_frame[i++]; - if (path_len >= 0 && i + path_len + 4 <= len) - { // minimum 4 byte payload + if (path_len >= 0 && i + path_len + 4 <= len) { // minimum 4 byte payload uint8_t *path = &cmd_frame[i]; i += path_len; auto pkt = createRawData(&cmd_frame[i], len - i); - if (pkt) - { + if (pkt) { sendDirect(pkt, path, path_len); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else - { + else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) } } - else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); char *password = (char *)&cmd_frame[1 + PUB_KEY_SIZE]; cmd_frame[len] = 0; // ensure null terminator in password - if (recipient) - { + if (recipient) { uint32_t est_timeout; int result = sendLogin(*recipient, password, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_telemetry = pending_status = 0; memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1493,25 +1421,20 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { uint32_t tag, est_timeout; int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_telemetry = pending_login = 0; // FUTURE: pending_status = tag; // match this in onContactResponse() memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme @@ -1522,25 +1445,20 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[4]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { uint32_t tag, est_timeout; int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_status = pending_login = 0; pending_telemetry = tag; // match this in onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1550,35 +1468,28 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; - if (hasConnectionTo(pub_key)) - { + if (hasConnectionTo(pub_key)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; stopConnection(pub_key); writeOKFrame(); } - else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) - { + else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; - if (getChannel(channel_idx, channel)) - { + if (getChannel(channel_idx, channel)) { int i = 0; out_frame[i++] = RESP_CODE_CHANNEL_INFO; out_frame[i++] = channel_idx; @@ -1588,64 +1499,52 @@ void MyMesh::handleCmdFrame(size_t len) i += 16; // NOTE: only 128-bit supported _serial->writeFrame(out_frame, i); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) - { + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) - { + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32); memset(channel.channel.secret, 0, sizeof(channel.channel.secret)); memcpy(channel.channel.secret, &cmd_frame[2 + 32], 16); // NOTE: only 128-bit supported - if (setChannel(channel_idx, channel)) - { + if (setChannel(channel_idx, channel)) { saveChannels(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } - else if (cmd_frame[0] == CMD_SIGN_START) - { + else if (cmd_frame[0] == CMD_SIGN_START) { out_frame[0] = RESP_CODE_SIGN_START; out_frame[1] = 0; // reserved uint32_t len = MAX_SIGN_DATA_LEN; memcpy(&out_frame[2], &len, 4); _serial->writeFrame(out_frame, 6); - if (sign_data) - { + if (sign_data) { free(sign_data); } sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); sign_data_len = 0; } - else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) - { - if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) - { + else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { + if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long } - else - { + else { memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); sign_data_len += (len - 1); writeOKFrame(); } } - else if (cmd_frame[0] == CMD_SIGN_FINISH) - { - if (sign_data) - { + else if (cmd_frame[0] == CMD_SIGN_FINISH) { + if (sign_data) { self_id.sign(&out_frame[1], sign_data, sign_data_len); free(sign_data); // don't need sign_data now @@ -1654,19 +1553,16 @@ void MyMesh::handleCmdFrame(size_t len) out_frame[0] = RESP_CODE_SIGNATURE; _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); } - else - { + else { writeErrFrame(ERR_CODE_BAD_STATE); } } - else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { uint32_t tag, auth; memcpy(&tag, &cmd_frame[1], 4); memcpy(&auth, &cmd_frame[5], 4); auto pkt = createTrace(tag, auth, cmd_frame[9]); - if (pkt) - { + if (pkt) { uint8_t path_len = len - 10; sendDirect(pkt, &cmd_frame[10], path_len); @@ -1679,38 +1575,31 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) - { + else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { // get pin from command frame uint32_t pin; memcpy(&pin, &cmd_frame[1], 4); // ensure pin is zero, or a valid 6 digit pin - if (pin == 0 || (pin >= 100000 && pin <= 999999)) - { + if (pin == 0 || (pin >= 100000 && pin <= 999999)) { _prefs.ble_pin = pin; savePrefs(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) - { + else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { out_frame[0] = RESP_CODE_CUSTOM_VARS; char *dp = (char *)&out_frame[1]; - for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) - { - if (i > 0) - { + for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) { + if (i > 0) { *dp++ = ','; } strcpy(dp, sensors.getSettingName(i)); @@ -1721,31 +1610,25 @@ void MyMesh::handleCmdFrame(size_t len) } _serial->writeFrame(out_frame, dp - (char *)out_frame); } - else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) - { + else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { cmd_frame[len] = 0; char *sp = (char *)&cmd_frame[1]; char *np = strchr(sp, ':'); // look for separator char - if (np) - { + if (np) { *np++ = 0; // modify 'cmd_frame', replace ':' with null bool success = sensors.setSettingValue(sp, np); - if (success) - { + if (success) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else - { + else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); } @@ -1756,42 +1639,35 @@ void MyMesh::loop() BaseChatMesh::loop(); size_t len = _serial->checkRecvFrame(cmd_frame); - if (len > 0) - { + if (len > 0) { handleCmdFrame(len); } else if (_iter_started // check if our ContactsIterator is 'running' && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! - ) - { + ) { ContactInfo contact; - if (_iter.hasNext(this, contact)) - { - if (contact.lastmod > _iter_filter_since) - { // apply the 'since' filter + if (_iter.hasNext(this, contact)) { + if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter writeContactRespFrame(RESP_CODE_CONTACT, contact); - if (contact.lastmod > _most_recent_lastmod) - { + if (contact.lastmod > _most_recent_lastmod) { _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame } } } - else - { // EOF + else { // EOF out_frame[0] = RESP_CODE_END_OF_CONTACTS; - memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' + memcpy(&out_frame[1], &_most_recent_lastmod, + 4); // include the most recent lastmod, so app can update their 'since' _serial->writeFrame(out_frame, 5); _iter_started = false; } } - else if (!_serial->isWriteBusy()) - { + else if (!_serial->isWriteBusy()) { checkConnections(); } // is there are pending dirty contacts write needed? - if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) - { + if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { saveContacts(); dirty_contacts_expiry = 0; } @@ -1805,14 +1681,12 @@ void MyMesh::loop() bool MyMesh::advert() { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { + if (pkt) { sendZeroHop(pkt); writeOKFrame(); return true; } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); return false; } diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index a4794f1f..574b9878 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -1,21 +1,20 @@ -#ifndef MYMESH_H -#define MYMESH_H +#pragma once #include #include #ifdef DISPLAY_CLASS - #include "UITask.h" +#include "UITask.h" #endif /*------------ Frame Protocol --------------*/ -#define FIRMWARE_VER_CODE 5 +#define FIRMWARE_VER_CODE 5 #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "24 May 2025" +#define FIRMWARE_BUILD_DATE "24 May 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.6.2" +#define FIRMWARE_VERSION "v1.6.2" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -26,258 +25,197 @@ #include #endif -#include -#include -#include -#include -#include #include "NodePrefs.h" + #include +#include +#include +#include +#include +#include #include /* ---------------------------------- CONFIGURATION ------------------------------------- */ #ifndef LORA_FREQ - #define LORA_FREQ 915.0 +#define LORA_FREQ 915.0 #endif #ifndef LORA_BW - #define LORA_BW 250 +#define LORA_BW 250 #endif #ifndef LORA_SF - #define LORA_SF 10 +#define LORA_SF 10 #endif #ifndef LORA_CR - #define LORA_CR 5 +#define LORA_CR 5 #endif #ifndef LORA_TX_POWER - #define LORA_TX_POWER 20 +#define LORA_TX_POWER 20 #endif #ifndef MAX_LORA_TX_POWER - #define MAX_LORA_TX_POWER LORA_TX_POWER +#define MAX_LORA_TX_POWER LORA_TX_POWER #endif #ifndef MAX_CONTACTS - #define MAX_CONTACTS 100 +#define MAX_CONTACTS 100 #endif #ifndef OFFLINE_QUEUE_SIZE - #define OFFLINE_QUEUE_SIZE 16 +#define OFFLINE_QUEUE_SIZE 16 #endif #ifndef BLE_NAME_PREFIX - #define BLE_NAME_PREFIX "MeshCore-" +#define BLE_NAME_PREFIX "MeshCore-" #endif #include -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define LAZY_CONTACTS_WRITE_DELAY 5000 +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define LAZY_CONTACTS_WRITE_DELAY 5000 -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" - -#define CMD_APP_START 1 -#define CMD_SEND_TXT_MSG 2 -#define CMD_SEND_CHANNEL_TXT_MSG 3 -#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) -#define CMD_GET_DEVICE_TIME 5 -#define CMD_SET_DEVICE_TIME 6 -#define CMD_SEND_SELF_ADVERT 7 -#define CMD_SET_ADVERT_NAME 8 -#define CMD_ADD_UPDATE_CONTACT 9 -#define CMD_SYNC_NEXT_MESSAGE 10 -#define CMD_SET_RADIO_PARAMS 11 -#define CMD_SET_RADIO_TX_POWER 12 -#define CMD_RESET_PATH 13 -#define CMD_SET_ADVERT_LATLON 14 -#define CMD_REMOVE_CONTACT 15 -#define CMD_SHARE_CONTACT 16 -#define CMD_EXPORT_CONTACT 17 -#define CMD_IMPORT_CONTACT 18 -#define CMD_REBOOT 19 -#define CMD_GET_BATTERY_VOLTAGE 20 -#define CMD_SET_TUNING_PARAMS 21 -#define CMD_DEVICE_QEURY 22 -#define CMD_EXPORT_PRIVATE_KEY 23 -#define CMD_IMPORT_PRIVATE_KEY 24 -#define CMD_SEND_RAW_DATA 25 -#define CMD_SEND_LOGIN 26 -#define CMD_SEND_STATUS_REQ 27 -#define CMD_HAS_CONNECTION 28 -#define CMD_LOGOUT 29 // 'Disconnect' -#define CMD_GET_CONTACT_BY_KEY 30 -#define CMD_GET_CHANNEL 31 -#define CMD_SET_CHANNEL 32 -#define CMD_SIGN_START 33 -#define CMD_SIGN_DATA 34 -#define CMD_SIGN_FINISH 35 -#define CMD_SEND_TRACE_PATH 36 -#define CMD_SET_DEVICE_PIN 37 -#define CMD_SET_OTHER_PARAMS 38 -#define CMD_SEND_TELEMETRY_REQ 39 -#define CMD_GET_CUSTOM_VARS 40 -#define CMD_SET_CUSTOM_VAR 41 - -#define RESP_CODE_OK 0 -#define RESP_CODE_ERR 1 -#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS -#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) -#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS -#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START -#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG -#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME -#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE -#define RESP_CODE_EXPORT_CONTACT 11 -#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE -#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY -#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY -#define RESP_CODE_DISABLED 15 -#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL -#define RESP_CODE_SIGN_START 19 -#define RESP_CODE_SIGNATURE 20 -#define RESP_CODE_CUSTOM_VARS 21 +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" // these are _pushed_ to client app at any time -#define PUSH_CODE_ADVERT 0x80 -#define PUSH_CODE_PATH_UPDATED 0x81 -#define PUSH_CODE_SEND_CONFIRMED 0x82 -#define PUSH_CODE_MSG_WAITING 0x83 -#define PUSH_CODE_RAW_DATA 0x84 -#define PUSH_CODE_LOGIN_SUCCESS 0x85 -#define PUSH_CODE_LOGIN_FAIL 0x86 -#define PUSH_CODE_STATUS_RESPONSE 0x87 -#define PUSH_CODE_LOG_RX_DATA 0x88 -#define PUSH_CODE_TRACE_DATA 0x89 -#define PUSH_CODE_NEW_ADVERT 0x8A -#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B +#define PUSH_CODE_ADVERT 0x80 +#define PUSH_CODE_PATH_UPDATED 0x81 +#define PUSH_CODE_SEND_CONFIRMED 0x82 +#define PUSH_CODE_MSG_WAITING 0x83 +#define PUSH_CODE_RAW_DATA 0x84 +#define PUSH_CODE_LOGIN_SUCCESS 0x85 +#define PUSH_CODE_LOGIN_FAIL 0x86 +#define PUSH_CODE_STATUS_RESPONSE 0x87 +#define PUSH_CODE_LOG_RX_DATA 0x88 +#define PUSH_CODE_TRACE_DATA 0x89 +#define PUSH_CODE_NEW_ADVERT 0x8A +#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B -#define ERR_CODE_UNSUPPORTED_CMD 1 -#define ERR_CODE_NOT_FOUND 2 -#define ERR_CODE_TABLE_FULL 3 -#define ERR_CODE_BAD_STATE 4 -#define ERR_CODE_FILE_IO_ERROR 5 -#define ERR_CODE_ILLEGAL_ARG 6 +#define ERR_CODE_UNSUPPORTED_CMD 1 +#define ERR_CODE_NOT_FOUND 2 +#define ERR_CODE_TABLE_FULL 3 +#define ERR_CODE_BAD_STATE 4 +#define ERR_CODE_FILE_IO_ERROR 5 +#define ERR_CODE_ILLEGAL_ARG 6 /* -------------------------------------------------------------------------------------- */ -#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS -#define REQ_TYPE_KEEP_ALIVE 0x02 -#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 +#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS +#define REQ_TYPE_KEEP_ALIVE 0x02 +#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 -#define MAX_SIGN_DATA_LEN (8*1024) // 8K +#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K class MyMesh : public BaseChatMesh { public: - MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables); - - void begin(FILESYSTEM& fs, bool has_display); - void startInterface(BaseSerialInterface& serial); - void loadPrefsInt(const char* filename); - void savePrefs(); - - const char* getNodeName(); - NodePrefs* getNodePrefs(); - uint32_t getBLEPin(); + MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables); - void loop(); - void handleCmdFrame(size_t len); - bool advert(); + void begin(FILESYSTEM &fs, bool has_display); + void startInterface(BaseSerialInterface &serial); + void loadPrefsInt(const char *filename); + void savePrefs(); + + const char *getNodeName(); + NodePrefs *getNodePrefs(); + uint32_t getBLEPin(); + + void loop(); + void handleCmdFrame(size_t len); + bool advert(); protected: - float getAirtimeBudgetFactor() const override; - int getInterferenceThreshold() const override; - int calcRxDelay(float score, uint32_t air_time) const override; + float getAirtimeBudgetFactor() const override; + int getInterferenceThreshold() const override; + int calcRxDelay(float score, uint32_t air_time) const override; - void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; - bool isAutoAddEnabled() const override; - void onDiscoveredContact(ContactInfo& contact, bool is_new) override; - void onContactPathUpdated(const ContactInfo& contact) override; - bool processAck(const uint8_t *data) override; - void queueMessage(const ContactInfo& from, uint8_t txt_type, mesh::Packet* pkt, - uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text); + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; + bool isAutoAddEnabled() const override; + void onDiscoveredContact(ContactInfo &contact, bool is_new) override; + void onContactPathUpdated(const ContactInfo &contact) override; + bool processAck(const uint8_t *data) override; + void queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *extra, int extra_len, const char *text); - void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; - void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; - void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, - const uint8_t *sender_prefix, const char *text) override; - void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override; + void onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override; + void onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override; + void onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) override; + void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, + const char *text) override; - 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 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; + 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 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; - uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; - uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; - void onSendTimeout() override; + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; + uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; + void onSendTimeout() override; private: - void writeOKFrame(); - void writeErrFrame(uint8_t err_code); - void writeDisabledFrame(); - void writeContactRespFrame(uint8_t code, const ContactInfo& contact); - void updateContactFromFrame(ContactInfo& contact, const uint8_t* frame, int len); - void addToOfflineQueue(const uint8_t frame[], int len); - int getFromOfflineQueue(uint8_t frame[]); - void loadMainIdentity(); - bool saveMainIdentity(const mesh::LocalIdentity& identity); - void loadContacts(); - void saveContacts(); - void loadChannels(); - void saveChannels(); - int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; - bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + void writeOKFrame(); + void writeErrFrame(uint8_t err_code); + void writeDisabledFrame(); + void writeContactRespFrame(uint8_t code, const ContactInfo &contact); + void updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len); + void addToOfflineQueue(const uint8_t frame[], int len); + int getFromOfflineQueue(uint8_t frame[]); + void loadMainIdentity(); + bool saveMainIdentity(const mesh::LocalIdentity &identity); + void loadContacts(); + void saveContacts(); + void loadChannels(); + void saveChannels(); + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; private: - FILESYSTEM* _fs; - IdentityStore* _identity_store; - NodePrefs _prefs; - uint32_t pending_login; - uint32_t pending_status; - uint32_t pending_telemetry; - BaseSerialInterface* _serial; + FILESYSTEM *_fs; + IdentityStore *_identity_store; + NodePrefs _prefs; + uint32_t pending_login; + uint32_t pending_status; + uint32_t pending_telemetry; + BaseSerialInterface *_serial; - ContactsIterator _iter; - uint32_t _iter_filter_since; - uint32_t _most_recent_lastmod; - uint32_t _active_ble_pin; - bool _iter_started; - uint8_t app_target_ver; - uint8_t* sign_data; - uint32_t sign_data_len; - unsigned long dirty_contacts_expiry; + ContactsIterator _iter; + uint32_t _iter_filter_since; + uint32_t _most_recent_lastmod; + uint32_t _active_ble_pin; + bool _iter_started; + uint8_t app_target_ver; + uint8_t *sign_data; + uint32_t sign_data_len; + unsigned long dirty_contacts_expiry; - uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; - uint8_t out_frame[MAX_FRAME_SIZE + 1]; - CayenneLPP telemetry; + uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; + uint8_t out_frame[MAX_FRAME_SIZE + 1]; + CayenneLPP telemetry; - struct Frame { - uint8_t len; - uint8_t buf[MAX_FRAME_SIZE]; - }; - int offline_queue_len; - Frame offline_queue[OFFLINE_QUEUE_SIZE]; + struct Frame { + uint8_t len; + uint8_t buf[MAX_FRAME_SIZE]; + }; + int offline_queue_len; + Frame offline_queue[OFFLINE_QUEUE_SIZE]; - struct AckTableEntry { - unsigned long msg_sent; - uint32_t ack; - }; - #define EXPECTED_ACK_TABLE_SIZE 8 - AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table - int next_ack_idx; + struct AckTableEntry { + unsigned long msg_sent; + uint32_t ack; + }; +#define EXPECTED_ACK_TABLE_SIZE 8 + AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table + int next_ack_idx; }; extern StdRNG fast_rng; extern SimpleMeshTables tables; extern MyMesh the_mesh; #ifdef DISPLAY_CLASS - extern UITask ui_task; -#endif -#endif // MYMESH_H \ No newline at end of file +extern UITask ui_task; +#endif \ No newline at end of file diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 09d04266..44d7ecbf 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -1,13 +1,11 @@ -#ifndef NODE_PREFS_H -#define NODE_PREFS_H - +#pragma once #include // For uint8_t, uint32_t -#define TELEM_MODE_DENY 0 -#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags -#define TELEM_MODE_ALLOW_ALL 2 +#define TELEM_MODE_DENY 0 +#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags +#define TELEM_MODE_ALLOW_ALL 2 -struct NodePrefs { // persisted to file +struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; float freq; @@ -22,6 +20,4 @@ struct NodePrefs { // persisted to file uint8_t telemetry_mode_env; float rx_delay_base; uint32_t ble_pin; -}; - -#endif // NODE_PREFS_H \ No newline at end of file +}; \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 6d7e1eb3..f52c0961 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,53 +1,43 @@ -#ifndef UI_TASK_H -#define UI_TASK_H - +#pragma once #include #include #include #ifdef PIN_BUZZER - #include +#include #endif -#include "NodePrefs.h" #include "Button.h" +#include "NodePrefs.h" - enum class UIEventType -{ - none, - contactMessage, - channelMessage, - roomMessage, - newContactMessage, - ack -}; +enum class UIEventType { none, contactMessage, channelMessage, roomMessage, newContactMessage, ack }; class UITask { - DisplayDriver* _display; - mesh::MainBoard* _board; + DisplayDriver *_display; + mesh::MainBoard *_board; #ifdef PIN_BUZZER genericBuzzer buzzer; #endif unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; - NodePrefs* _node_prefs; + NodePrefs *_node_prefs; char _version_info[32]; char _origin[62]; char _msg[80]; int _msgcount; bool _need_refresh = true; - bool _displayWasOn = false; // Track display state before button press + bool _displayWasOn = false; // Track display state before button press // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) - Button* _userButton = nullptr; + Button *_userButton = nullptr; #endif void renderCurrScreen(); void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); - + // Button action handlers void handleButtonAnyPress(); void handleButtonShortPress(); @@ -55,22 +45,21 @@ class UITask { void handleButtonTriplePress(); void handleButtonLongPress(); - public: - - UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { - _next_refresh = 0; - _connected = false; + UITask(mesh::MainBoard *board) : _board(board), _display(NULL) + { + _next_refresh = 0; + _connected = false; } - void begin(DisplayDriver* display, NodePrefs* node_prefs, const char* build_date, const char* firmware_version, uint32_t pin_code); + void begin(DisplayDriver *display, NodePrefs *node_prefs, const char *build_date, + const char *firmware_version, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } bool hasDisplay() const { return _display != NULL; } void clearMsgPreview(); void msgRead(int msgcount); - void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); + void newMsg(uint8_t path_len, const char *from_name, const char *text, int msgcount); void soundBuzzer(UIEventType bet = UIEventType::none); void shutdown(bool restart = false); void loop(); -}; -#endif //UI_TASK_H \ No newline at end of file +}; \ No newline at end of file diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 63ff20da..2440b697 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -1,56 +1,57 @@ -#include // needed for PlatformIO +#include // needed for PlatformIO #include #if defined(NRF52_PLATFORM) - #include +#include #elif defined(RP2040_PLATFORM) - #include +#include #elif defined(ESP32) - #include +#include #endif -#include -#include -#include -#include #include +#include +#include +#include +#include #include /* ---------------------------------- CONFIGURATION ------------------------------------- */ -#define FIRMWARE_VER_TEXT "v2 (build: 4 Feb 2025)" +#define FIRMWARE_VER_TEXT "v2 (build: 4 Feb 2025)" #ifndef LORA_FREQ - #define LORA_FREQ 915.0 +#define LORA_FREQ 915.0 #endif #ifndef LORA_BW - #define LORA_BW 250 +#define LORA_BW 250 #endif #ifndef LORA_SF - #define LORA_SF 10 +#define LORA_SF 10 #endif #ifndef LORA_CR - #define LORA_CR 5 +#define LORA_CR 5 #endif #ifndef LORA_TX_POWER - #define LORA_TX_POWER 20 +#define LORA_TX_POWER 20 #endif #ifndef MAX_CONTACTS - #define MAX_CONTACTS 100 +#define MAX_CONTACTS 100 #endif #include -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" // Believe it or not, this std C function is busted on some platforms! -static uint32_t _atoi(const char* sp) { +static uint32_t _atoi(const char *sp) +{ uint32_t n = 0; while (*sp && *sp >= '0' && *sp <= '9') { n *= 10; @@ -61,7 +62,7 @@ static uint32_t _atoi(const char* sp) { /* -------------------------------------------------------------------------------------- */ -struct NodePrefs { // persisted to file +struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; double node_lat, node_lon; @@ -71,30 +72,35 @@ struct NodePrefs { // persisted to file }; class MyMesh : public BaseChatMesh, ContactVisitor { - FILESYSTEM* _fs; + FILESYSTEM *_fs; NodePrefs _prefs; uint32_t expected_ack_crc; - ChannelDetails* _public; + ChannelDetails *_public; unsigned long last_msg_sent; - ContactInfo* curr_recipient; - char command[512+10]; + ContactInfo *curr_recipient; + char command[512 + 10]; uint8_t tmp_buf[256]; char hex_buf[512]; - const char* getTypeName(uint8_t type) const { - if (type == ADV_TYPE_CHAT) return "Chat"; - if (type == ADV_TYPE_REPEATER) return "Repeater"; - if (type == ADV_TYPE_ROOM) return "Room"; - return "??"; // unknown + const char *getTypeName(uint8_t type) const + { + if (type == ADV_TYPE_CHAT) + return "Chat"; + if (type == ADV_TYPE_REPEATER) + return "Repeater"; + if (type == ADV_TYPE_ROOM) + return "Room"; + return "??"; // unknown } - void loadContacts() { + void loadContacts() + { if (_fs->exists("/contacts")) { - #if defined(RP2040_PLATFORM) +#if defined(RP2040_PLATFORM) File file = _fs->open("/contacts", "r"); - #else +#else File file = _fs->open("/contacts"); - #endif +#endif if (file) { bool full = false; while (!full) { @@ -104,28 +110,31 @@ class MyMesh : public BaseChatMesh, ContactVisitor { uint32_t reserved; bool success = (file.read(pub_key, 32) == 32); - success = success && (file.read((uint8_t *) &c.name, 32) == 32); + success = success && (file.read((uint8_t *)&c.name, 32) == 32); success = success && (file.read(&c.type, 1) == 1); success = success && (file.read(&c.flags, 1) == 1); success = success && (file.read(&unused, 1) == 1); - success = success && (file.read((uint8_t *) &reserved, 4) == 4); - success = success && (file.read((uint8_t *) &c.out_path_len, 1) == 1); - success = success && (file.read((uint8_t *) &c.last_advert_timestamp, 4) == 4); + success = success && (file.read((uint8_t *)&reserved, 4) == 4); + success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); success = success && (file.read(c.out_path, 64) == 64); - c.gps_lat = c.gps_lon = 0; // not yet supported + c.gps_lat = c.gps_lon = 0; // not yet supported - if (!success) break; // EOF + if (!success) + break; // EOF c.id = mesh::Identity(pub_key); c.lastmod = 0; - if (!addContact(c)) full = true; + if (!addContact(c)) + full = true; } file.close(); } } } - void saveContacts() { + void saveContacts() + { #if defined(NRF52_PLATFORM) _fs->remove("/contacts"); File file = _fs->open("/contacts", FILE_O_WRITE); @@ -142,44 +151,50 @@ class MyMesh : public BaseChatMesh, ContactVisitor { while (iter.hasNext(this, c)) { bool success = (file.write(c.id.pub_key, 32) == 32); - success = success && (file.write((uint8_t *) &c.name, 32) == 32); + success = success && (file.write((uint8_t *)&c.name, 32) == 32); success = success && (file.write(&c.type, 1) == 1); success = success && (file.write(&c.flags, 1) == 1); success = success && (file.write(&unused, 1) == 1); - success = success && (file.write((uint8_t *) &reserved, 4) == 4); - success = success && (file.write((uint8_t *) &c.out_path_len, 1) == 1); - success = success && (file.write((uint8_t *) &c.last_advert_timestamp, 4) == 4); + success = success && (file.write((uint8_t *)&reserved, 4) == 4); + success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); success = success && (file.write(c.out_path, 64) == 64); - if (!success) break; // write failed + if (!success) + break; // write failed } file.close(); } } - void setClock(uint32_t timestamp) { + void setClock(uint32_t timestamp) + { uint32_t curr = getRTCClock()->getCurrentTime(); if (timestamp > curr) { getRTCClock()->setCurrentTime(timestamp); Serial.println(" (OK - clock set!)"); - } else { + } + else { Serial.println(" (ERR: clock cannot go backwards)"); } } - void importCard(const char* command) { - while (*command == ' ') command++; // skip leading spaces + void importCard(const char *command) + { + while (*command == ' ') + command++; // skip leading spaces if (memcmp(command, "meshcore://", 11) == 0) { - command += 11; // skip the prefix - char *ep = strchr(command, 0); // find end of string + command += 11; // skip the prefix + char *ep = strchr(command, 0); // find end of string while (ep > command) { ep--; - if (mesh::Utils::isHexChar(*ep)) break; // found tail end of card - *ep = 0; // remove trailing spaces and other junk + if (mesh::Utils::isHexChar(*ep)) + break; // found tail end of card + *ep = 0; // remove trailing spaces and other junk } int len = strlen(command); if (len % 2 == 0) { - len >>= 1; // halve, for num bytes + len >>= 1; // halve, for num bytes if (mesh::Utils::fromHex(tmp_buf, len, command)) { importContact(tmp_buf, len); return; @@ -190,97 +205,112 @@ class MyMesh : public BaseChatMesh, ContactVisitor { } protected: - float getAirtimeBudgetFactor() const override { - return _prefs.airtime_factor; + float getAirtimeBudgetFactor() const override { return _prefs.airtime_factor; } + + int calcRxDelay(float score, uint32_t air_time) const override + { + return 0; // disable rxdelay } - int calcRxDelay(float score, uint32_t air_time) const override { - return 0; // disable rxdelay - } + bool allowPacketForward(const mesh::Packet *packet) override { return true; } - bool allowPacketForward(const mesh::Packet* packet) override { - return true; - } - - void onDiscoveredContact(ContactInfo& contact, bool is_new) override { + void onDiscoveredContact(ContactInfo &contact, bool is_new) override + { // TODO: if not in favs, prompt to add as fav(?) Serial.printf("ADVERT from -> %s\n", contact.name); Serial.printf(" type: %s\n", getTypeName(contact.type)); - Serial.print(" public key: "); mesh::Utils::printHex(Serial, contact.id.pub_key, PUB_KEY_SIZE); Serial.println(); + Serial.print(" public key: "); + mesh::Utils::printHex(Serial, contact.id.pub_key, PUB_KEY_SIZE); + Serial.println(); saveContacts(); } - void onContactPathUpdated(const ContactInfo& contact) override { - Serial.printf("PATH to: %s, path_len=%d\n", contact.name, (int32_t) contact.out_path_len); + void onContactPathUpdated(const ContactInfo &contact) override + { + Serial.printf("PATH to: %s, path_len=%d\n", contact.name, (int32_t)contact.out_path_len); saveContacts(); } - bool processAck(const uint8_t *data) override { - if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient + bool processAck(const uint8_t *data) override + { + if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient Serial.printf(" Got ACK! (round trip: %d millis)\n", _ms->getMillis() - last_msg_sent); // NOTE: the same ACK can be received multiple times! - expected_ack_crc = 0; // reset our expected hash, now that we have received ACK + expected_ack_crc = 0; // reset our expected hash, now that we have received ACK return true; } - //uint32_t crc; - //memcpy(&crc, data, 4); - //MESH_DEBUG_PRINTLN("unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc); + // uint32_t crc; + // memcpy(&crc, data, 4); + // MESH_DEBUG_PRINTLN("unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc); return false; } - void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { + void onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override + { Serial.printf("(%s) MSG -> from %s\n", pkt->isRouteDirect() ? "DIRECT" : "FLOOD", from.name); Serial.printf(" %s\n", text); - if (strcmp(text, "clock sync") == 0) { // special text command + if (strcmp(text, "clock sync") == 0) { // special text command setClock(sender_timestamp + 1); } } - void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { + void onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override + { } - void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { + void onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) override + { } - void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override { + void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, + const char *text) override + { if (pkt->isRouteDirect()) { Serial.printf("PUBLIC CHANNEL MSG -> (Direct!)\n"); - } else { + } + else { Serial.printf("PUBLIC CHANNEL MSG -> (Flood) hops %d\n", pkt->path_len); } Serial.printf(" %s\n", text); } - uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) override { - return 0; // unknown + uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, + uint8_t len, uint8_t *reply) override + { + return 0; // unknown } - void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { + void onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) override + { // not supported } - uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override { + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override + { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } - uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override { - return SEND_TIMEOUT_BASE_MILLIS + - ( (pkt_airtime_millis*DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); + uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override + { + return SEND_TIMEOUT_BASE_MILLIS + + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * + (path_len + 1)); } - void onSendTimeout() override { - Serial.println(" ERROR: timed out, no ACK."); - } + void onSendTimeout() override { Serial.println(" ERROR: timed out, no ACK."); } public: - MyMesh(mesh::Radio& radio, StdRNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables) - : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) + MyMesh(mesh::Radio &radio, StdRNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) + : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) { // defaults memset(&_prefs, 0, sizeof(_prefs)); - _prefs.airtime_factor = 2.0; // one third + _prefs.airtime_factor = 2.0; // one third strcpy(_prefs.node_name, "NONAME"); _prefs.freq = LORA_FREQ; _prefs.tx_power_dbm = LORA_TX_POWER; @@ -292,45 +322,49 @@ public: float getFreqPref() const { return _prefs.freq; } uint8_t getTxPowerPref() const { return _prefs.tx_power_dbm; } - void begin(FILESYSTEM& fs) { + void begin(FILESYSTEM &fs) + { _fs = &fs; BaseChatMesh::begin(); - #if defined(NRF52_PLATFORM) +#if defined(NRF52_PLATFORM) IdentityStore store(fs, ""); - #elif defined(RP2040_PLATFORM) +#elif defined(RP2040_PLATFORM) IdentityStore store(fs, "/identity"); store.begin(); - #else +#else IdentityStore store(fs, "/identity"); - #endif - if (!store.load("_main", self_id, _prefs.node_name, sizeof(_prefs.node_name))) { // legacy: node_name was from identity file +#endif + if (!store.load("_main", self_id, _prefs.node_name, + sizeof(_prefs.node_name))) { // legacy: node_name was from identity file // Need way to get some entropy to seed RNG Serial.println("Press ENTER to generate key:"); char c = 0; - while (c != '\n') { // wait for ENTER to be pressed - if (Serial.available()) c = Serial.read(); + while (c != '\n') { // wait for ENTER to be pressed + if (Serial.available()) + c = Serial.read(); } ((StdRNG *)getRNG())->begin(millis()); - self_id = mesh::LocalIdentity(getRNG()); // create new random identity + self_id = mesh::LocalIdentity(getRNG()); // create new random identity int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes - self_id = mesh::LocalIdentity(getRNG()); count++; + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes + self_id = mesh::LocalIdentity(getRNG()); + count++; } store.save("_main", self_id); } // load persisted prefs if (_fs->exists("/node_prefs")) { - #if defined(RP2040_PLATFORM) +#if defined(RP2040_PLATFORM) File file = _fs->open("/node_prefs", "r"); - #else +#else File file = _fs->open("/node_prefs"); - #endif +#endif if (file) { - file.read((uint8_t *) &_prefs, sizeof(_prefs)); + file.read((uint8_t *)&_prefs, sizeof(_prefs)); file.close(); } } @@ -339,7 +373,8 @@ public: _public = addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel } - void savePrefs() { + void savePrefs() + { #if defined(NRF52_PLATFORM) _fs->remove("/node_prefs"); File file = _fs->open("/node_prefs", FILE_O_WRITE); @@ -354,7 +389,8 @@ public: } } - void showWelcome() { + void showWelcome() + { Serial.println("===== MeshCore Chat Terminal ====="); Serial.println(); Serial.printf("WELCOME %s\n", _prefs.node_name); @@ -364,7 +400,8 @@ public: Serial.println(); } - void sendSelfAdvert(int delay_millis) { + void sendSelfAdvert(int delay_millis) + { auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { sendFlood(pkt, delay_millis); @@ -372,7 +409,8 @@ public: } // ContactVisitor - void onContactVisit(const ContactInfo& contact) override { + void onContactVisit(const ContactInfo &contact) override + { Serial.printf(" %s - ", contact.name); char tmp[40]; int32_t secs = contact.last_advert_timestamp - getRTCClock()->getCurrentTime(); @@ -380,129 +418,159 @@ public: Serial.println(tmp); } - void handleCommand(const char* command) { - while (*command == ' ') command++; // skip leading spaces + void handleCommand(const char *command) + { + while (*command == ' ') + command++; // skip leading spaces if (memcmp(command, "send ", 5) == 0) { if (curr_recipient) { const char *text = &command[5]; uint32_t est_timeout; - int result = sendMessage(*curr_recipient, getRTCClock()->getCurrentTime(), 0, text, expected_ack_crc, est_timeout); + int result = sendMessage(*curr_recipient, getRTCClock()->getCurrentTime(), 0, text, expected_ack_crc, + est_timeout); if (result == MSG_SEND_FAILED) { Serial.println(" ERROR: unable to send."); - } else { + } + else { last_msg_sent = _ms->getMillis(); Serial.printf(" (message sent - %s)\n", result == MSG_SEND_SENT_FLOOD ? "FLOOD" : "DIRECT"); } - } else { + } + else { Serial.println(" ERROR: no recipient selected (use 'to' cmd)."); } - } else if (memcmp(command, "public ", 7) == 0) { // send GroupChannel msg - uint8_t temp[5+MAX_TEXT_LEN+32]; + } + else if (memcmp(command, "public ", 7) == 0) { // send GroupChannel msg + uint8_t temp[5 + MAX_TEXT_LEN + 32]; uint32_t timestamp = getRTCClock()->getCurrentTime(); - memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique - temp[4] = 0; // attempt and flags + memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique + temp[4] = 0; // attempt and flags - sprintf((char *) &temp[5], "%s: %s", _prefs.node_name, &command[7]); // : - temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long + sprintf((char *)&temp[5], "%s: %s", _prefs.node_name, &command[7]); // : + temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long - int len = strlen((char *) &temp[5]); + int len = strlen((char *)&temp[5]); auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, _public->channel, temp, 5 + len); if (pkt) { sendFlood(pkt); Serial.println(" Sent."); - } else { + } + else { Serial.println(" ERROR: unable to send"); } - } else if (memcmp(command, "list", 4) == 0) { // show Contact list, by most recent + } + else if (memcmp(command, "list", 4) == 0) { // show Contact list, by most recent int n = 0; - if (command[4] == ' ') { // optional param, last 'N' + if (command[4] == ' ') { // optional param, last 'N' n = atoi(&command[5]); } scanRecentContacts(n, this); - } else if (strcmp(command, "clock") == 0) { // show current time + } + else if (strcmp(command, "clock") == 0) { // show current time uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); - Serial.printf( "%02d:%02d - %d/%d/%d UTC\n", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); - } else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) + Serial.printf("%02d:%02d - %d/%d/%d UTC\n", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); + } + else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) uint32_t secs = _atoi(&command[5]); setClock(secs); - } else if (memcmp(command, "to ", 3) == 0) { // set current recipient + } + else if (memcmp(command, "to ", 3) == 0) { // set current recipient curr_recipient = searchContactsByPrefix(&command[3]); if (curr_recipient) { Serial.printf(" Recipient %s now selected.\n", curr_recipient->name); - } else { + } + else { Serial.println(" Error: Name prefix not found."); } - } else if (strcmp(command, "to") == 0) { // show current recipient + } + else if (strcmp(command, "to") == 0) { // show current recipient if (curr_recipient) { - Serial.printf(" Current: %s\n", curr_recipient->name); - } else { - Serial.println(" Err: no recipient selected"); + Serial.printf(" Current: %s\n", curr_recipient->name); } - } else if (strcmp(command, "advert") == 0) { + else { + Serial.println(" Err: no recipient selected"); + } + } + else if (strcmp(command, "advert") == 0) { auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { sendZeroHop(pkt); Serial.println(" (advert sent, zero hop)."); - } else { + } + else { Serial.println(" ERR: unable to send"); } - } else if (strcmp(command, "reset path") == 0) { + } + else if (strcmp(command, "reset path") == 0) { if (curr_recipient) { resetPathTo(*curr_recipient); saveContacts(); Serial.println(" Done."); } - } else if (memcmp(command, "card", 4) == 0) { + } + else if (memcmp(command, "card", 4) == 0) { Serial.printf("Hello %s\n", _prefs.node_name); auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { - uint8_t len = pkt->writeTo(tmp_buf); - releasePacket(pkt); // undo the obtainNewPacket() + uint8_t len = pkt->writeTo(tmp_buf); + releasePacket(pkt); // undo the obtainNewPacket() mesh::Utils::toHex(hex_buf, tmp_buf, len); Serial.println("Your MeshCore biz card:"); - Serial.print("meshcore://"); Serial.println(hex_buf); + Serial.print("meshcore://"); + Serial.println(hex_buf); Serial.println(); - } else { + } + else { Serial.println(" Error"); } - } else if (memcmp(command, "import ", 7) == 0) { + } + else if (memcmp(command, "import ", 7) == 0) { importCard(&command[7]); - } else if (memcmp(command, "set ", 4) == 0) { - const char* config = &command[4]; + } + else if (memcmp(command, "set ", 4) == 0) { + const char *config = &command[4]; if (memcmp(config, "af ", 3) == 0) { _prefs.airtime_factor = atof(&config[3]); savePrefs(); Serial.println(" OK"); - } else if (memcmp(config, "name ", 5) == 0) { + } + else if (memcmp(config, "name ", 5) == 0) { StrHelper::strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)); savePrefs(); Serial.println(" OK"); - } else if (memcmp(config, "lat ", 4) == 0) { + } + else if (memcmp(config, "lat ", 4) == 0) { _prefs.node_lat = atof(&config[4]); savePrefs(); Serial.println(" OK"); - } else if (memcmp(config, "lon ", 4) == 0) { + } + else if (memcmp(config, "lon ", 4) == 0) { _prefs.node_lon = atof(&config[4]); savePrefs(); Serial.println(" OK"); - } else if (memcmp(config, "tx ", 3) == 0) { + } + else if (memcmp(config, "tx ", 3) == 0) { _prefs.tx_power_dbm = atoi(&config[3]); savePrefs(); Serial.println(" OK - reboot to apply"); - } else if (memcmp(config, "freq ", 5) == 0) { + } + else if (memcmp(config, "freq ", 5) == 0) { _prefs.freq = atof(&config[5]); savePrefs(); Serial.println(" OK - reboot to apply"); - } else { + } + else { Serial.printf(" ERROR: unknown config: %s\n", config); } - } else if (memcmp(command, "ver", 3) == 0) { + } + else if (memcmp(command, "ver", 3) == 0) { Serial.println(FIRMWARE_VER_TEXT); - } else if (memcmp(command, "help", 4) == 0) { + } + else if (memcmp(command, "help", 4) == 0) { Serial.println("Commands:"); Serial.println(" set {name|lat|lon|freq|tx|af} {value}"); Serial.println(" card"); @@ -516,50 +584,59 @@ public: Serial.println(" advert"); Serial.println(" reset path"); Serial.println(" public "); - } else { - Serial.print(" ERROR: unknown command: "); Serial.println(command); + } + else { + Serial.print(" ERROR: unknown command: "); + Serial.println(command); } } - void loop() { + void loop() + { BaseChatMesh::loop(); int len = strlen(command); - while (Serial.available() && len < sizeof(command)-1) { + while (Serial.available() && len < sizeof(command) - 1) { char c = Serial.read(); - if (c != '\n') { + if (c != '\n') { command[len++] = c; command[len] = 0; } Serial.print(c); } - if (len == sizeof(command)-1) { // command buffer full - command[sizeof(command)-1] = '\r'; + if (len == sizeof(command) - 1) { // command buffer full + command[sizeof(command) - 1] = '\r'; } - if (len > 0 && command[len - 1] == '\r') { // received complete line - command[len - 1] = 0; // replace newline with C string null terminator + if (len > 0 && command[len - 1] == '\r') { // received complete line + command[len - 1] = 0; // replace newline with C string null terminator handleCommand(command); - command[0] = 0; // reset command buffer + command[0] = 0; // reset command buffer } } }; 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, *new VolatileRTCClock(), + tables); // TODO: test with 'rtc_clock' in target.cpp -void halt() { - while (1) ; +void halt() +{ + while (1) + ; } -void setup() { +void setup() +{ Serial.begin(115200); board.begin(); - if (!radio_init()) { halt(); } + if (!radio_init()) { + halt(); + } fast_rng.begin(radio_get_rng_seed()); @@ -573,7 +650,7 @@ void setup() { SPIFFS.begin(true); the_mesh.begin(SPIFFS); #else - #error "need to define filesystem" +#error "need to define filesystem" #endif radio_set_params(the_mesh.getFreqPref(), LORA_BW, LORA_SF, LORA_CR); @@ -582,9 +659,10 @@ void setup() { the_mesh.showWelcome(); // send out initial Advertisement to the mesh - the_mesh.sendSelfAdvert(1200); // add slight delay + the_mesh.sendSelfAdvert(1200); // add slight delay } -void loop() { +void loop() +{ the_mesh.loop(); } From a987efeca1da43ffd10dbf7315f4f74a03d23090 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 12:54:40 +1000 Subject: [PATCH 059/103] * companion: disabling interference threshold for now --- examples/companion_radio/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 4cc85dba..84848d8b 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -491,7 +491,7 @@ protected: } int getInterferenceThreshold() const override { - return 14; // hard-coded for now + return 0; // disabled for now, until currentRSSI() problem is resolved } int calcRxDelay(float score, uint32_t air_time) const override { From 9959475c0d908f477d82a2fe166d49b905ead289 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 09:25:17 -0700 Subject: [PATCH 060/103] Reformatting code --- examples/companion_radio/MyMesh.cpp | 790 +++++++++++---------------- examples/companion_radio/MyMesh.h | 336 +++++------- examples/companion_radio/NodePrefs.h | 16 +- examples/companion_radio/UITask.h | 47 +- 4 files changed, 493 insertions(+), 696 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6e9688a5..db40169f 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1,19 +1,83 @@ -#include // needed for PlatformIO -#include #include "MyMesh.h" +#include // needed for PlatformIO +#include + +#define CMD_APP_START 1 +#define CMD_SEND_TXT_MSG 2 +#define CMD_SEND_CHANNEL_TXT_MSG 3 +#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) +#define CMD_GET_DEVICE_TIME 5 +#define CMD_SET_DEVICE_TIME 6 +#define CMD_SEND_SELF_ADVERT 7 +#define CMD_SET_ADVERT_NAME 8 +#define CMD_ADD_UPDATE_CONTACT 9 +#define CMD_SYNC_NEXT_MESSAGE 10 +#define CMD_SET_RADIO_PARAMS 11 +#define CMD_SET_RADIO_TX_POWER 12 +#define CMD_RESET_PATH 13 +#define CMD_SET_ADVERT_LATLON 14 +#define CMD_REMOVE_CONTACT 15 +#define CMD_SHARE_CONTACT 16 +#define CMD_EXPORT_CONTACT 17 +#define CMD_IMPORT_CONTACT 18 +#define CMD_REBOOT 19 +#define CMD_GET_BATTERY_VOLTAGE 20 +#define CMD_SET_TUNING_PARAMS 21 +#define CMD_DEVICE_QEURY 22 +#define CMD_EXPORT_PRIVATE_KEY 23 +#define CMD_IMPORT_PRIVATE_KEY 24 +#define CMD_SEND_RAW_DATA 25 +#define CMD_SEND_LOGIN 26 +#define CMD_SEND_STATUS_REQ 27 +#define CMD_HAS_CONNECTION 28 +#define CMD_LOGOUT 29 // 'Disconnect' +#define CMD_GET_CONTACT_BY_KEY 30 +#define CMD_GET_CHANNEL 31 +#define CMD_SET_CHANNEL 32 +#define CMD_SIGN_START 33 +#define CMD_SIGN_DATA 34 +#define CMD_SIGN_FINISH 35 +#define CMD_SEND_TRACE_PATH 36 +#define CMD_SET_DEVICE_PIN 37 +#define CMD_SET_OTHER_PARAMS 38 +#define CMD_SEND_TELEMETRY_REQ 39 +#define CMD_GET_CUSTOM_VARS 40 +#define CMD_SET_CUSTOM_VAR 41 + +#define RESP_CODE_OK 0 +#define RESP_CODE_ERR 1 +#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS +#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) +#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS +#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START +#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG +#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) +#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME +#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE +#define RESP_CODE_EXPORT_CONTACT 11 +#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE +#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY +#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY +#define RESP_CODE_DISABLED 15 +#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL +#define RESP_CODE_SIGN_START 19 +#define RESP_CODE_SIGNATURE 20 +#define RESP_CODE_CUSTOM_VARS 21 + #ifdef DISPLAY_CLASS - #include "UITask.h" +#include "UITask.h" #endif void MyMesh::loadMainIdentity() { - if (!_identity_store->load("_main", self_id)) - { + if (!_identity_store->load("_main", self_id)) { self_id = radio_new_identity(); // create new random identity int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) - { // reserved id hashes + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes self_id = radio_new_identity(); count++; } @@ -28,18 +92,15 @@ bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) void MyMesh::loadContacts() { - if (_fs->exists("/contacts3")) - { + if (_fs->exists("/contacts3")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/contacts3", "r"); #else File file = _fs->open("/contacts3"); #endif - if (file) - { + if (file) { bool full = false; - while (!full) - { + while (!full) { ContactInfo c; uint8_t pub_key[32]; uint8_t unused; @@ -79,14 +140,12 @@ void MyMesh::saveContacts() #else File file = _fs->open("/contacts3", "w", true); #endif - if (file) - { + if (file) { ContactsIterator iter; ContactInfo c; uint8_t unused = 0; - while (iter.hasNext(this, c)) - { + while (iter.hasNext(this, c)) { bool success = (file.write(c.id.pub_key, 32) == 32); success = success && (file.write((uint8_t *)&c.name, 32) == 32); success = success && (file.write(&c.type, 1) == 1); @@ -109,19 +168,16 @@ void MyMesh::saveContacts() void MyMesh::loadChannels() { - if (_fs->exists("/channels2")) - { + if (_fs->exists("/channels2")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "r"); #else File file = _fs->open("/channels2"); #endif - if (file) - { + if (file) { bool full = false; uint8_t channel_idx = 0; - while (!full) - { + while (!full) { ChannelDetails ch; uint8_t unused[4]; @@ -132,12 +188,10 @@ void MyMesh::loadChannels() if (!success) break; // EOF - if (setChannel(channel_idx, ch)) - { + if (setChannel(channel_idx, ch)) { channel_idx++; } - else - { + else { full = true; } } @@ -156,15 +210,13 @@ void MyMesh::saveChannels() #else File file = _fs->open("/channels2", "w", true); #endif - if (file) - { + if (file) { uint8_t channel_idx = 0; ChannelDetails ch; uint8_t unused[4]; memset(unused, 0, 4); - while (getChannel(channel_idx, ch)) - { + while (getChannel(channel_idx, ch)) { bool success = (file.write(unused, 4) == 4); success = success && (file.write((uint8_t *)ch.name, 32) == 32); success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); @@ -187,15 +239,13 @@ int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); - if (_fs->exists(path)) - { + if (_fs->exists(path)) { #if defined(RP2040_PLATFORM) File f = _fs->open(path, "r"); #else File f = _fs->open(path); #endif - if (f) - { + if (f) { int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! f.close(); return len; @@ -222,8 +272,7 @@ bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_bu #else File f = _fs->open(path, "w", true); #endif - if (f) - { + if (f) { int n = f.write(src_buf, len); f.close(); if (n == len) @@ -294,8 +343,7 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, i += 32; memcpy(&contact.last_advert_timestamp, &frame[i], 4); i += 4; - if (i + 8 >= len) - { // optional fields + if (i + 8 >= len) { // optional fields memcpy(&contact.gps_lat, &frame[i], 4); i += 4; memcpy(&contact.gps_lon, &frame[i], 4); @@ -305,12 +353,10 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { - if (offline_queue_len >= OFFLINE_QUEUE_SIZE) - { + if (offline_queue_len >= OFFLINE_QUEUE_SIZE) { MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); } - else - { + else { offline_queue[offline_queue_len].len = len; memcpy(offline_queue[offline_queue_len].buf, frame, len); offline_queue_len++; @@ -318,14 +364,12 @@ void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) } int MyMesh::getFromOfflineQueue(uint8_t frame[]) { - if (offline_queue_len > 0) - { // check offline queue + if (offline_queue_len > 0) { // check offline queue size_t len = offline_queue[0].len; // take from top of queue memcpy(frame, offline_queue[0].buf, len); offline_queue_len--; - for (int i = 0; i < offline_queue_len; i++) - { // delete top item from queue + for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue offline_queue[i] = offline_queue[i + 1]; } return len; @@ -352,8 +396,7 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { - if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) - { + if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; out_frame[i++] = PUSH_CODE_LOG_RX_DATA; out_frame[i++] = (int8_t)(snr * 4); @@ -372,21 +415,17 @@ bool MyMesh::isAutoAddEnabled() const void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) { - if (_serial->isConnected()) - { - if (!isAutoAddEnabled() && is_new) - { + if (_serial->isConnected()) { + if (!isAutoAddEnabled() && is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); } - else - { + else { out_frame[0] = PUSH_CODE_ADVERT; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::newContactMessage); #endif @@ -407,10 +446,8 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) bool MyMesh::processAck(const uint8_t *data) { // see if matches any in a table - for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) - { - if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) - { // got an ACK from recipient + for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) { + if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient out_frame[0] = PUSH_CODE_SEND_CONFIRMED; memcpy(&out_frame[1], data, 4); uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent; @@ -425,18 +462,17 @@ bool MyMesh::processAck(const uint8_t *data) return checkConnectionsAck(data); } -void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) +void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, + uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) { int i = 0; - if (app_target_ver >= 3) - { + if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 } - else - { + else { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; } memcpy(&out_frame[i], from.id.pub_key, 6); @@ -445,28 +481,24 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe out_frame[i++] = txt_type; memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; - if (extra_len > 0) - { + if (extra_len > 0) { memcpy(&out_frame[i], extra, extra_len); i += extra_len; } int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) - { + if (i + tlen > MAX_FRAME_SIZE) { tlen = MAX_FRAME_SIZE - i; } memcpy(&out_frame[i], text, tlen); i += tlen; addToOfflineQueue(out_frame, i); - if (_serial->isConnected()) - { + if (_serial->isConnected()) { uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::contactMessage); #endif @@ -476,19 +508,22 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #endif } -void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const char *text) +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 queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); } -void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const char *text) +void MyMesh::onCommandDataRecv(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 queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); } -void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) +void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) { markConnectionActive(from); // from.sync_since change needs to be persisted @@ -496,18 +531,17 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text); } -void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, const char *text) +void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, + const char *text) { int i = 0; - if (app_target_ver >= 3) - { + if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 } - else - { + else { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } @@ -518,22 +552,19 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe memcpy(&out_frame[i], ×tamp, 4); i += 4; int tlen = strlen(text); // TODO: UTF-8 ?? - if (i + tlen > MAX_FRAME_SIZE) - { + if (i + tlen > MAX_FRAME_SIZE) { tlen = MAX_FRAME_SIZE - i; } memcpy(&out_frame[i], text, tlen); i += tlen; addToOfflineQueue(out_frame, i); - if (_serial->isConnected()) - { + if (_serial->isConnected()) { uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } - else - { + else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::channelMessage); #endif @@ -543,48 +574,42 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe #endif } -uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) +uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, + uint8_t len, uint8_t *reply) { - if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) - { + if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { uint8_t permissions = 0; uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) - if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) { permissions = TELEM_PERM_BASE; } - else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { permissions = cp & TELEM_PERM_BASE; } - if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_LOCATION; } - else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_LOCATION; } - if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) - { + if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_ENVIRONMENT; } - else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) - { + else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_ENVIRONMENT; } - if (permissions & TELEM_PERM_BASE) - { // only respond if base permission bit is set + if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific sensors.querySensors(permissions, telemetry); - memcpy(reply, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') + memcpy(reply, &sender_timestamp, + 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') uint8_t tlen = telemetry.getSize(); memcpy(&reply[4], telemetry.getBuffer(), tlen); @@ -599,24 +624,20 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint32_t tag; memcpy(&tag, data, 4); - if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) - { // check for login response + if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) { // check for login response // yes, is response to pending sendLogin() pending_login = 0; int i = 0; - if (memcmp(&data[4], "OK", 2) == 0) - { // legacy Repeater login OK response + if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } - else if (data[4] == RESP_SERVER_LOGIN_OK) - { // new login response + else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; - if (keep_alive_secs > 0) - { + if (keep_alive_secs > 0) { startConnection(contact, keep_alive_secs); } out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; @@ -626,8 +647,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp } - else - { + else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved memcpy(&out_frame[i], contact.id.pub_key, 6); @@ -635,11 +655,11 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, } _serial->writeFrame(out_frame, i); } - else if (len > 4 && // check for status response - pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme - // FUTURE: tag == pending_status - ) - { + else if (len > 4 && // check for status response + pending_status && + memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme + // FUTURE: tag == pending_status + ) { pending_status = 0; int i = 0; @@ -651,8 +671,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, i += (len - 4); _serial->writeFrame(out_frame, i); } - else if (len > 4 && tag == pending_telemetry) - { // check for telemetry response + else if (len > 4 && tag == pending_telemetry) { // check for telemetry response pending_telemetry = 0; int i = 0; @@ -668,8 +687,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, void MyMesh::onRawDataRecv(mesh::Packet *packet) { - if (packet->payload_len + 4 > sizeof(out_frame)) - { + if (packet->payload_len + 4 > sizeof(out_frame)) { MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len); return; } @@ -681,17 +699,16 @@ void MyMesh::onRawDataRecv(mesh::Packet *packet) memcpy(&out_frame[i], packet->payload, packet->payload_len); i += packet->payload_len; - if (_serial->isConnected()) - { + if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); } - else - { + else { MESH_DEBUG_PRINTLN("onRawDataRecv(), data received while app offline"); } } -void MyMesh::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) +void MyMesh::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) { int i = 0; out_frame[i++] = PUSH_CODE_TRACE_DATA; @@ -708,12 +725,10 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, i += path_len; out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node) - if (_serial->isConnected()) - { + if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); } - else - { + else { MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); } } @@ -725,16 +740,15 @@ uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const { return SEND_TIMEOUT_BASE_MILLIS + - ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * + (path_len + 1)); } -void MyMesh::onSendTimeout() -{ -} +void MyMesh::onSendTimeout() {} MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) - : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), - telemetry(MAX_PACKET_PAYLOAD - 4) + : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; offline_queue_len = 0; @@ -764,8 +778,7 @@ void MyMesh::loadPrefsInt(const char *filename) #else File file = _fs->open(filename); #endif - if (file) - { + if (file) { uint8_t pad[8]; file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 @@ -828,36 +841,30 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) #endif // load persisted prefs - if (_fs->exists("/new_prefs")) - { + if (_fs->exists("/new_prefs")) { loadPrefsInt("/new_prefs"); // new filename } - else if (_fs->exists("/node_prefs")) - { + else if (_fs->exists("/node_prefs")) { loadPrefsInt("/node_prefs"); savePrefs(); // save to new filename _fs->remove("/node_prefs"); // remove old } #ifdef BLE_PIN_CODE - if (_prefs.ble_pin == 0) - { + if (_prefs.ble_pin == 0) { #ifdef DISPLAY_CLASS - if (has_display) - { + if (has_display) { StdRNG rng; _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session } - else - { + else { _active_ble_pin = BLE_PIN_CODE; // otherwise static pin } #else _active_ble_pin = BLE_PIN_CODE; // otherwise static pin #endif } - else - { + else { _active_ble_pin = _prefs.ble_pin; } #else @@ -875,12 +882,18 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) radio_set_tx_power(_prefs.tx_power_dbm); } -const char *MyMesh::getNodeName() { return _prefs.node_name; } +const char *MyMesh::getNodeName() +{ + return _prefs.node_name; +} NodePrefs *MyMesh::getNodePrefs() { return &_prefs; } -uint32_t MyMesh::getBLEPin() { return _active_ble_pin; } +uint32_t MyMesh::getBLEPin() +{ + return _active_ble_pin; +} void MyMesh::startInterface(BaseSerialInterface &serial) { @@ -898,8 +911,7 @@ void MyMesh::savePrefs() #else File file = _fs->open("/new_prefs", "w", true); #endif - if (file) - { + if (file) { uint8_t pad[8]; memset(pad, 0, sizeof(pad)); @@ -928,9 +940,8 @@ void MyMesh::savePrefs() void MyMesh::handleCmdFrame(size_t len) { - if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) - { // sent when app establishes connection - app_target_ver = cmd_frame[1]; // which version of protocol does app understand + if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection + app_target_ver = cmd_frame[1]; // which version of protocol does app understand int i = 0; out_frame[i++] = RESP_CODE_DEVICE_INFO; @@ -948,8 +959,8 @@ void MyMesh::handleCmdFrame(size_t len) i += 20; _serial->writeFrame(out_frame, i); } - else if (cmd_frame[0] == CMD_APP_START && len >= 8) - { // sent when app establishes connection, respond with node ID + else if (cmd_frame[0] == CMD_APP_START && + len >= 8) { // sent when app establishes connection, respond with node ID // cmd_frame[1..7] reserved future char *app_name = (char *)&cmd_frame[8]; cmd_frame[len] = 0; // make app_name null terminated @@ -971,9 +982,10 @@ void MyMesh::handleCmdFrame(size_t len) i += 4; memcpy(&out_frame[i], &lon, 4); i += 4; - out_frame[i++] = 0; // reserved - out_frame[i++] = 0; // reserved - out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ + out_frame[i++] = 0; // reserved + out_frame[i++] = 0; // reserved + out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | + (_prefs.telemetry_mode_base); // v5+ out_frame[i++] = _prefs.manual_add_contacts; uint32_t freq = _prefs.freq * 1000; @@ -990,8 +1002,7 @@ void MyMesh::handleCmdFrame(size_t len) i += tlen; _serial->writeFrame(out_frame, i); } - else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) - { + else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { int i = 1; uint8_t txt_type = cmd_frame[i++]; uint8_t attempt = cmd_frame[i++]; @@ -1001,32 +1012,26 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t *pub_key_prefix = &cmd_frame[i]; i += 6; ContactInfo *recipient = lookupContactByPubKey(pub_key_prefix, 6); - if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) - { + if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) { char *text = (char *)&cmd_frame[i]; int tlen = len - i; uint32_t est_timeout; text[tlen] = 0; // ensure null int result; uint32_t expected_ack; - if (txt_type == TXT_TYPE_CLI_DATA) - { + if (txt_type == TXT_TYPE_CLI_DATA) { result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); expected_ack = 0; // no Ack expected } - else - { + else { result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); } // TODO: add expected ACK to table - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { - if (expected_ack) - { + else { + if (expected_ack) { expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table expected_ack_table[next_ack_idx].ack = expected_ack; next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; @@ -1039,13 +1044,13 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { - writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* + else { + writeErrFrame(recipient == NULL + ? ERR_CODE_NOT_FOUND + : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* } } - else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) - { // send GroupChannel msg + else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1054,38 +1059,29 @@ void MyMesh::handleCmdFrame(size_t len) i += 4; const char *text = (char *)&cmd_frame[i]; - if (txt_type != TXT_TYPE_PLAIN) - { + if (txt_type != TXT_TYPE_PLAIN) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); } - else - { + else { ChannelDetails channel; bool success = getChannel(channel_idx, channel); - if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) - { + if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } } - else if (cmd_frame[0] == CMD_GET_CONTACTS) - { // get Contact list - if (_iter_started) - { + else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list + if (_iter_started) { writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy } - else - { - if (len >= 5) - { // has optional 'since' param + else { + if (len >= 5) { // has optional 'since' param memcpy(&_iter_filter_since, &cmd_frame[1], 4); } - else - { + else { _iter_filter_since = 0; } @@ -1101,8 +1097,7 @@ void MyMesh::handleCmdFrame(size_t len) _most_recent_lastmod = 0; } } - else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) - { + else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { int nlen = len - 1; if (nlen > sizeof(_prefs.node_name) - 1) nlen = sizeof(_prefs.node_name) - 1; // max len @@ -1111,169 +1106,134 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) - { + else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { int32_t lat, lon, alt = 0; memcpy(&lat, &cmd_frame[1], 4); memcpy(&lon, &cmd_frame[5], 4); - if (len >= 13) - { + if (len >= 13) { memcpy(&alt, &cmd_frame[9], 4); // for FUTURE support } - if (lat <= 90 * 1E6 && lat >= -90 * 1E6 && lon <= 180 * 1E6 && lon >= -180 * 1E6) - { + if (lat <= 90 * 1E6 && lat >= -90 * 1E6 && lon <= 180 * 1E6 && lon >= -180 * 1E6) { sensors.node_lat = ((double)lat) / 1000000.0; sensors.node_lon = ((double)lon) / 1000000.0; savePrefs(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate } } - else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) - { + else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { uint8_t reply[5]; reply[0] = RESP_CODE_CURR_TIME; uint32_t now = getRTCClock()->getCurrentTime(); memcpy(&reply[1], &now, 4); _serial->writeFrame(reply, 5); } - else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) - { + else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { uint32_t secs; memcpy(&secs, &cmd_frame[1], 4); uint32_t curr = getRTCClock()->getCurrentTime(); - if (secs >= curr) - { + if (secs >= curr) { getRTCClock()->setCurrentTime(secs); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) - { + else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { - if (len >= 2 && cmd_frame[1] == 1) - { // optional param (1 = flood, 0 = zero hop) + if (pkt) { + if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop) sendFlood(pkt); } - else - { + else { sendZeroHop(pkt); } writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) - { + else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { recipient->out_path_len = -1; // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact } } - else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) - { + else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { updateContactFromFrame(*recipient, cmd_frame, len); // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { ContactInfo contact; updateContactFromFrame(contact, cmd_frame, len); contact.lastmod = getRTCClock()->getCurrentTime(); contact.sync_since = 0; - if (addContact(contact)) - { + if (addContact(contact)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } } - else if (cmd_frame[0] == CMD_REMOVE_CONTACT) - { + else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient && removeContact(*recipient)) - { + if (recipient && removeContact(*recipient)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove } } - else if (cmd_frame[0] == CMD_SHARE_CONTACT) - { + else if (cmd_frame[0] == CMD_SHARE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { - if (shareContactZeroHop(*recipient)) - { + if (recipient) { + if (shareContactZeroHop(*recipient)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) - { + else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (contact) - { + if (contact) { writeContactRespFrame(RESP_CODE_CONTACT, *contact); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } - else if (cmd_frame[0] == CMD_EXPORT_CONTACT) - { - if (len < 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { + if (len < 1 + PUB_KEY_SIZE) { // export SELF auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { + if (pkt) { pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode out_frame[0] = RESP_CODE_EXPORT_CONTACT; @@ -1281,56 +1241,45 @@ void MyMesh::handleCmdFrame(size_t len) releasePacket(pkt); // undo the obtainNewPacket() _serial->writeFrame(out_frame, out_len + 1); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); // Error } } - else - { + else { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); uint8_t out_len; - if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) - { + if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) { out_frame[0] = RESP_CODE_EXPORT_CONTACT; _serial->writeFrame(out_frame, out_len + 1); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } } - else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) - { - if (importContact(&cmd_frame[1], len - 1)) - { + else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { + if (importContact(&cmd_frame[1], len - 1)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) - { + else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { int out_len; - if ((out_len = getFromOfflineQueue(out_frame)) > 0) - { + if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); #ifdef DISPLAY_CLASS ui_task.msgRead(offline_queue_len); #endif } - else - { + else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); } } - else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { int i = 1; uint32_t freq; memcpy(&freq, &cmd_frame[i], 4); @@ -1341,8 +1290,8 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t sf = cmd_frame[i++]; uint8_t cr = cmd_frame[i++]; - if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) - { + if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && + bw <= 500000) { _prefs.sf = sf; _prefs.cr = cr; _prefs.freq = (float)freq / 1000.0; @@ -1350,32 +1299,29 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, + (uint32_t)cr); writeOKFrame(); } - else - { - MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); + else { + MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, + (uint32_t)cr); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) - { - if (cmd_frame[1] > MAX_LORA_TX_POWER) - { + else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { + if (cmd_frame[1] > MAX_LORA_TX_POWER) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - else - { + else { _prefs.tx_power_dbm = cmd_frame[1]; savePrefs(); radio_set_tx_power(_prefs.tx_power_dbm); writeOKFrame(); } } - else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { int i = 1; uint32_t rx, af; memcpy(&rx, &cmd_frame[i], 4); @@ -1387,11 +1333,9 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) - { + else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { _prefs.manual_add_contacts = cmd_frame[1]; - if (len >= 3) - { + if (len >= 3) { _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ _prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03; _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03; @@ -1399,24 +1343,20 @@ void MyMesh::handleCmdFrame(size_t len) savePrefs(); writeOKFrame(); } - else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) - { - if (dirty_contacts_expiry) - { // is there are pending dirty contacts write needed? + else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { + if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? saveContacts(); } board.reboot(); } - else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) - { + else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { uint8_t reply[3]; reply[0] = RESP_CODE_BATTERY_VOLTAGE; uint16_t battery_millivolts = board.getBattMilliVolts(); memcpy(&reply[1], &battery_millivolts, 2); _serial->writeFrame(reply, 3); } - else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) - { + else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { #if ENABLE_PRIVATE_KEY_EXPORT uint8_t reply[65]; reply[0] = RESP_CODE_PRIVATE_KEY; @@ -1426,64 +1366,52 @@ void MyMesh::handleCmdFrame(size_t len) writeDisabledFrame(); #endif } - else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) - { + else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { #if ENABLE_PRIVATE_KEY_IMPORT mesh::LocalIdentity identity; identity.readFrom(&cmd_frame[1], 64); - if (saveMainIdentity(identity)) - { + if (saveMainIdentity(identity)) { self_id = identity; writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_FILE_IO_ERROR); } #else writeDisabledFrame(); #endif } - else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) - { + else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { int i = 1; int8_t path_len = cmd_frame[i++]; - if (path_len >= 0 && i + path_len + 4 <= len) - { // minimum 4 byte payload + if (path_len >= 0 && i + path_len + 4 <= len) { // minimum 4 byte payload uint8_t *path = &cmd_frame[i]; i += path_len; auto pkt = createRawData(&cmd_frame[i], len - i); - if (pkt) - { + if (pkt) { sendDirect(pkt, path, path_len); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else - { + else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) } } - else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); char *password = (char *)&cmd_frame[1 + PUB_KEY_SIZE]; cmd_frame[len] = 0; // ensure null terminator in password - if (recipient) - { + if (recipient) { uint32_t est_timeout; int result = sendLogin(*recipient, password, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_telemetry = pending_status = 0; memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1493,25 +1421,20 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { uint32_t tag, est_timeout; int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_telemetry = pending_login = 0; // FUTURE: pending_status = tag; // match this in onContactResponse() memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme @@ -1522,25 +1445,20 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[4]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); - if (recipient) - { + if (recipient) { uint32_t tag, est_timeout; int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); - if (result == MSG_SEND_FAILED) - { + if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); } - else - { + else { pending_status = pending_login = 0; pending_telemetry = tag; // match this in onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1550,35 +1468,28 @@ void MyMesh::handleCmdFrame(size_t len) _serial->writeFrame(out_frame, 10); } } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } - else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; - if (hasConnectionTo(pub_key)) - { + if (hasConnectionTo(pub_key)) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) - { + else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; stopConnection(pub_key); writeOKFrame(); } - else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) - { + else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; - if (getChannel(channel_idx, channel)) - { + if (getChannel(channel_idx, channel)) { int i = 0; out_frame[i++] = RESP_CODE_CHANNEL_INFO; out_frame[i++] = channel_idx; @@ -1588,64 +1499,52 @@ void MyMesh::handleCmdFrame(size_t len) i += 16; // NOTE: only 128-bit supported _serial->writeFrame(out_frame, i); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); } } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) - { + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) - { + else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32); memset(channel.channel.secret, 0, sizeof(channel.channel.secret)); memcpy(channel.channel.secret, &cmd_frame[2 + 32], 16); // NOTE: only 128-bit supported - if (setChannel(channel_idx, channel)) - { + if (setChannel(channel_idx, channel)) { saveChannels(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } - else if (cmd_frame[0] == CMD_SIGN_START) - { + else if (cmd_frame[0] == CMD_SIGN_START) { out_frame[0] = RESP_CODE_SIGN_START; out_frame[1] = 0; // reserved uint32_t len = MAX_SIGN_DATA_LEN; memcpy(&out_frame[2], &len, 4); _serial->writeFrame(out_frame, 6); - if (sign_data) - { + if (sign_data) { free(sign_data); } sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); sign_data_len = 0; } - else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) - { - if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) - { + else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { + if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long } - else - { + else { memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); sign_data_len += (len - 1); writeOKFrame(); } } - else if (cmd_frame[0] == CMD_SIGN_FINISH) - { - if (sign_data) - { + else if (cmd_frame[0] == CMD_SIGN_FINISH) { + if (sign_data) { self_id.sign(&out_frame[1], sign_data, sign_data_len); free(sign_data); // don't need sign_data now @@ -1654,19 +1553,16 @@ void MyMesh::handleCmdFrame(size_t len) out_frame[0] = RESP_CODE_SIGNATURE; _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); } - else - { + else { writeErrFrame(ERR_CODE_BAD_STATE); } } - else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) - { + else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { uint32_t tag, auth; memcpy(&tag, &cmd_frame[1], 4); memcpy(&auth, &cmd_frame[5], 4); auto pkt = createTrace(tag, auth, cmd_frame[9]); - if (pkt) - { + if (pkt) { uint8_t path_len = len - 10; sendDirect(pkt, &cmd_frame[10], path_len); @@ -1679,38 +1575,31 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) - { + else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { // get pin from command frame uint32_t pin; memcpy(&pin, &cmd_frame[1], 4); // ensure pin is zero, or a valid 6 digit pin - if (pin == 0 || (pin >= 100000 && pin <= 999999)) - { + if (pin == 0 || (pin >= 100000 && pin <= 999999)) { _prefs.ble_pin = pin; savePrefs(); writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) - { + else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { out_frame[0] = RESP_CODE_CUSTOM_VARS; char *dp = (char *)&out_frame[1]; - for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) - { - if (i > 0) - { + for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) { + if (i > 0) { *dp++ = ','; } strcpy(dp, sensors.getSettingName(i)); @@ -1721,31 +1610,25 @@ void MyMesh::handleCmdFrame(size_t len) } _serial->writeFrame(out_frame, dp - (char *)out_frame); } - else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) - { + else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { cmd_frame[len] = 0; char *sp = (char *)&cmd_frame[1]; char *np = strchr(sp, ':'); // look for separator char - if (np) - { + if (np) { *np++ = 0; // modify 'cmd_frame', replace ':' with null bool success = sensors.setSettingValue(sp, np); - if (success) - { + if (success) { writeOKFrame(); } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else - { + else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } - else - { + else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); } @@ -1756,42 +1639,35 @@ void MyMesh::loop() BaseChatMesh::loop(); size_t len = _serial->checkRecvFrame(cmd_frame); - if (len > 0) - { + if (len > 0) { handleCmdFrame(len); } else if (_iter_started // check if our ContactsIterator is 'running' && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! - ) - { + ) { ContactInfo contact; - if (_iter.hasNext(this, contact)) - { - if (contact.lastmod > _iter_filter_since) - { // apply the 'since' filter + if (_iter.hasNext(this, contact)) { + if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter writeContactRespFrame(RESP_CODE_CONTACT, contact); - if (contact.lastmod > _most_recent_lastmod) - { + if (contact.lastmod > _most_recent_lastmod) { _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame } } } - else - { // EOF + else { // EOF out_frame[0] = RESP_CODE_END_OF_CONTACTS; - memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' + memcpy(&out_frame[1], &_most_recent_lastmod, + 4); // include the most recent lastmod, so app can update their 'since' _serial->writeFrame(out_frame, 5); _iter_started = false; } } - else if (!_serial->isWriteBusy()) - { + else if (!_serial->isWriteBusy()) { checkConnections(); } // is there are pending dirty contacts write needed? - if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) - { + if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { saveContacts(); dirty_contacts_expiry = 0; } @@ -1805,14 +1681,12 @@ void MyMesh::loop() bool MyMesh::advert() { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); - if (pkt) - { + if (pkt) { sendZeroHop(pkt); writeOKFrame(); return true; } - else - { + else { writeErrFrame(ERR_CODE_TABLE_FULL); return false; } diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index a4794f1f..574b9878 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -1,21 +1,20 @@ -#ifndef MYMESH_H -#define MYMESH_H +#pragma once #include #include #ifdef DISPLAY_CLASS - #include "UITask.h" +#include "UITask.h" #endif /*------------ Frame Protocol --------------*/ -#define FIRMWARE_VER_CODE 5 +#define FIRMWARE_VER_CODE 5 #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "24 May 2025" +#define FIRMWARE_BUILD_DATE "24 May 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.6.2" +#define FIRMWARE_VERSION "v1.6.2" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -26,258 +25,197 @@ #include #endif -#include -#include -#include -#include -#include #include "NodePrefs.h" + #include +#include +#include +#include +#include +#include #include /* ---------------------------------- CONFIGURATION ------------------------------------- */ #ifndef LORA_FREQ - #define LORA_FREQ 915.0 +#define LORA_FREQ 915.0 #endif #ifndef LORA_BW - #define LORA_BW 250 +#define LORA_BW 250 #endif #ifndef LORA_SF - #define LORA_SF 10 +#define LORA_SF 10 #endif #ifndef LORA_CR - #define LORA_CR 5 +#define LORA_CR 5 #endif #ifndef LORA_TX_POWER - #define LORA_TX_POWER 20 +#define LORA_TX_POWER 20 #endif #ifndef MAX_LORA_TX_POWER - #define MAX_LORA_TX_POWER LORA_TX_POWER +#define MAX_LORA_TX_POWER LORA_TX_POWER #endif #ifndef MAX_CONTACTS - #define MAX_CONTACTS 100 +#define MAX_CONTACTS 100 #endif #ifndef OFFLINE_QUEUE_SIZE - #define OFFLINE_QUEUE_SIZE 16 +#define OFFLINE_QUEUE_SIZE 16 #endif #ifndef BLE_NAME_PREFIX - #define BLE_NAME_PREFIX "MeshCore-" +#define BLE_NAME_PREFIX "MeshCore-" #endif #include -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define LAZY_CONTACTS_WRITE_DELAY 5000 +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define LAZY_CONTACTS_WRITE_DELAY 5000 -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" - -#define CMD_APP_START 1 -#define CMD_SEND_TXT_MSG 2 -#define CMD_SEND_CHANNEL_TXT_MSG 3 -#define CMD_GET_CONTACTS 4 // with optional 'since' (for efficient sync) -#define CMD_GET_DEVICE_TIME 5 -#define CMD_SET_DEVICE_TIME 6 -#define CMD_SEND_SELF_ADVERT 7 -#define CMD_SET_ADVERT_NAME 8 -#define CMD_ADD_UPDATE_CONTACT 9 -#define CMD_SYNC_NEXT_MESSAGE 10 -#define CMD_SET_RADIO_PARAMS 11 -#define CMD_SET_RADIO_TX_POWER 12 -#define CMD_RESET_PATH 13 -#define CMD_SET_ADVERT_LATLON 14 -#define CMD_REMOVE_CONTACT 15 -#define CMD_SHARE_CONTACT 16 -#define CMD_EXPORT_CONTACT 17 -#define CMD_IMPORT_CONTACT 18 -#define CMD_REBOOT 19 -#define CMD_GET_BATTERY_VOLTAGE 20 -#define CMD_SET_TUNING_PARAMS 21 -#define CMD_DEVICE_QEURY 22 -#define CMD_EXPORT_PRIVATE_KEY 23 -#define CMD_IMPORT_PRIVATE_KEY 24 -#define CMD_SEND_RAW_DATA 25 -#define CMD_SEND_LOGIN 26 -#define CMD_SEND_STATUS_REQ 27 -#define CMD_HAS_CONNECTION 28 -#define CMD_LOGOUT 29 // 'Disconnect' -#define CMD_GET_CONTACT_BY_KEY 30 -#define CMD_GET_CHANNEL 31 -#define CMD_SET_CHANNEL 32 -#define CMD_SIGN_START 33 -#define CMD_SIGN_DATA 34 -#define CMD_SIGN_FINISH 35 -#define CMD_SEND_TRACE_PATH 36 -#define CMD_SET_DEVICE_PIN 37 -#define CMD_SET_OTHER_PARAMS 38 -#define CMD_SEND_TELEMETRY_REQ 39 -#define CMD_GET_CUSTOM_VARS 40 -#define CMD_SET_CUSTOM_VAR 41 - -#define RESP_CODE_OK 0 -#define RESP_CODE_ERR 1 -#define RESP_CODE_CONTACTS_START 2 // first reply to CMD_GET_CONTACTS -#define RESP_CODE_CONTACT 3 // multiple of these (after CMD_GET_CONTACTS) -#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS -#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START -#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG -#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) -#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME -#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE -#define RESP_CODE_EXPORT_CONTACT 11 -#define RESP_CODE_BATTERY_VOLTAGE 12 // a reply to a CMD_GET_BATTERY_VOLTAGE -#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY -#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY -#define RESP_CODE_DISABLED 15 -#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) -#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL -#define RESP_CODE_SIGN_START 19 -#define RESP_CODE_SIGNATURE 20 -#define RESP_CODE_CUSTOM_VARS 21 +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" // these are _pushed_ to client app at any time -#define PUSH_CODE_ADVERT 0x80 -#define PUSH_CODE_PATH_UPDATED 0x81 -#define PUSH_CODE_SEND_CONFIRMED 0x82 -#define PUSH_CODE_MSG_WAITING 0x83 -#define PUSH_CODE_RAW_DATA 0x84 -#define PUSH_CODE_LOGIN_SUCCESS 0x85 -#define PUSH_CODE_LOGIN_FAIL 0x86 -#define PUSH_CODE_STATUS_RESPONSE 0x87 -#define PUSH_CODE_LOG_RX_DATA 0x88 -#define PUSH_CODE_TRACE_DATA 0x89 -#define PUSH_CODE_NEW_ADVERT 0x8A -#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B +#define PUSH_CODE_ADVERT 0x80 +#define PUSH_CODE_PATH_UPDATED 0x81 +#define PUSH_CODE_SEND_CONFIRMED 0x82 +#define PUSH_CODE_MSG_WAITING 0x83 +#define PUSH_CODE_RAW_DATA 0x84 +#define PUSH_CODE_LOGIN_SUCCESS 0x85 +#define PUSH_CODE_LOGIN_FAIL 0x86 +#define PUSH_CODE_STATUS_RESPONSE 0x87 +#define PUSH_CODE_LOG_RX_DATA 0x88 +#define PUSH_CODE_TRACE_DATA 0x89 +#define PUSH_CODE_NEW_ADVERT 0x8A +#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B -#define ERR_CODE_UNSUPPORTED_CMD 1 -#define ERR_CODE_NOT_FOUND 2 -#define ERR_CODE_TABLE_FULL 3 -#define ERR_CODE_BAD_STATE 4 -#define ERR_CODE_FILE_IO_ERROR 5 -#define ERR_CODE_ILLEGAL_ARG 6 +#define ERR_CODE_UNSUPPORTED_CMD 1 +#define ERR_CODE_NOT_FOUND 2 +#define ERR_CODE_TABLE_FULL 3 +#define ERR_CODE_BAD_STATE 4 +#define ERR_CODE_FILE_IO_ERROR 5 +#define ERR_CODE_ILLEGAL_ARG 6 /* -------------------------------------------------------------------------------------- */ -#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS -#define REQ_TYPE_KEEP_ALIVE 0x02 -#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 +#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS +#define REQ_TYPE_KEEP_ALIVE 0x02 +#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 -#define MAX_SIGN_DATA_LEN (8*1024) // 8K +#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K class MyMesh : public BaseChatMesh { public: - MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables); - - void begin(FILESYSTEM& fs, bool has_display); - void startInterface(BaseSerialInterface& serial); - void loadPrefsInt(const char* filename); - void savePrefs(); - - const char* getNodeName(); - NodePrefs* getNodePrefs(); - uint32_t getBLEPin(); + MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables); - void loop(); - void handleCmdFrame(size_t len); - bool advert(); + void begin(FILESYSTEM &fs, bool has_display); + void startInterface(BaseSerialInterface &serial); + void loadPrefsInt(const char *filename); + void savePrefs(); + + const char *getNodeName(); + NodePrefs *getNodePrefs(); + uint32_t getBLEPin(); + + void loop(); + void handleCmdFrame(size_t len); + bool advert(); protected: - float getAirtimeBudgetFactor() const override; - int getInterferenceThreshold() const override; - int calcRxDelay(float score, uint32_t air_time) const override; + float getAirtimeBudgetFactor() const override; + int getInterferenceThreshold() const override; + int calcRxDelay(float score, uint32_t air_time) const override; - void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; - bool isAutoAddEnabled() const override; - void onDiscoveredContact(ContactInfo& contact, bool is_new) override; - void onContactPathUpdated(const ContactInfo& contact) override; - bool processAck(const uint8_t *data) override; - void queueMessage(const ContactInfo& from, uint8_t txt_type, mesh::Packet* pkt, - uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text); + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; + bool isAutoAddEnabled() const override; + void onDiscoveredContact(ContactInfo &contact, bool is_new) override; + void onContactPathUpdated(const ContactInfo &contact) override; + bool processAck(const uint8_t *data) override; + void queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *extra, int extra_len, const char *text); - void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; - void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override; - void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, - const uint8_t *sender_prefix, const char *text) override; - void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override; + void onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override; + void onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const char *text) override; + void onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, + const uint8_t *sender_prefix, const char *text) override; + void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, + const char *text) override; - 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 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; + 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 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; - uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; - uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; - void onSendTimeout() override; + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; + uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; + void onSendTimeout() override; private: - void writeOKFrame(); - void writeErrFrame(uint8_t err_code); - void writeDisabledFrame(); - void writeContactRespFrame(uint8_t code, const ContactInfo& contact); - void updateContactFromFrame(ContactInfo& contact, const uint8_t* frame, int len); - void addToOfflineQueue(const uint8_t frame[], int len); - int getFromOfflineQueue(uint8_t frame[]); - void loadMainIdentity(); - bool saveMainIdentity(const mesh::LocalIdentity& identity); - void loadContacts(); - void saveContacts(); - void loadChannels(); - void saveChannels(); - int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; - bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + void writeOKFrame(); + void writeErrFrame(uint8_t err_code); + void writeDisabledFrame(); + void writeContactRespFrame(uint8_t code, const ContactInfo &contact); + void updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len); + void addToOfflineQueue(const uint8_t frame[], int len); + int getFromOfflineQueue(uint8_t frame[]); + void loadMainIdentity(); + bool saveMainIdentity(const mesh::LocalIdentity &identity); + void loadContacts(); + void saveContacts(); + void loadChannels(); + void saveChannels(); + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; private: - FILESYSTEM* _fs; - IdentityStore* _identity_store; - NodePrefs _prefs; - uint32_t pending_login; - uint32_t pending_status; - uint32_t pending_telemetry; - BaseSerialInterface* _serial; + FILESYSTEM *_fs; + IdentityStore *_identity_store; + NodePrefs _prefs; + uint32_t pending_login; + uint32_t pending_status; + uint32_t pending_telemetry; + BaseSerialInterface *_serial; - ContactsIterator _iter; - uint32_t _iter_filter_since; - uint32_t _most_recent_lastmod; - uint32_t _active_ble_pin; - bool _iter_started; - uint8_t app_target_ver; - uint8_t* sign_data; - uint32_t sign_data_len; - unsigned long dirty_contacts_expiry; + ContactsIterator _iter; + uint32_t _iter_filter_since; + uint32_t _most_recent_lastmod; + uint32_t _active_ble_pin; + bool _iter_started; + uint8_t app_target_ver; + uint8_t *sign_data; + uint32_t sign_data_len; + unsigned long dirty_contacts_expiry; - uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; - uint8_t out_frame[MAX_FRAME_SIZE + 1]; - CayenneLPP telemetry; + uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; + uint8_t out_frame[MAX_FRAME_SIZE + 1]; + CayenneLPP telemetry; - struct Frame { - uint8_t len; - uint8_t buf[MAX_FRAME_SIZE]; - }; - int offline_queue_len; - Frame offline_queue[OFFLINE_QUEUE_SIZE]; + struct Frame { + uint8_t len; + uint8_t buf[MAX_FRAME_SIZE]; + }; + int offline_queue_len; + Frame offline_queue[OFFLINE_QUEUE_SIZE]; - struct AckTableEntry { - unsigned long msg_sent; - uint32_t ack; - }; - #define EXPECTED_ACK_TABLE_SIZE 8 - AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table - int next_ack_idx; + struct AckTableEntry { + unsigned long msg_sent; + uint32_t ack; + }; +#define EXPECTED_ACK_TABLE_SIZE 8 + AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table + int next_ack_idx; }; extern StdRNG fast_rng; extern SimpleMeshTables tables; extern MyMesh the_mesh; #ifdef DISPLAY_CLASS - extern UITask ui_task; -#endif -#endif // MYMESH_H \ No newline at end of file +extern UITask ui_task; +#endif \ No newline at end of file diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 09d04266..44d7ecbf 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -1,13 +1,11 @@ -#ifndef NODE_PREFS_H -#define NODE_PREFS_H - +#pragma once #include // For uint8_t, uint32_t -#define TELEM_MODE_DENY 0 -#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags -#define TELEM_MODE_ALLOW_ALL 2 +#define TELEM_MODE_DENY 0 +#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags +#define TELEM_MODE_ALLOW_ALL 2 -struct NodePrefs { // persisted to file +struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; float freq; @@ -22,6 +20,4 @@ struct NodePrefs { // persisted to file uint8_t telemetry_mode_env; float rx_delay_base; uint32_t ble_pin; -}; - -#endif // NODE_PREFS_H \ No newline at end of file +}; \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 6d7e1eb3..f52c0961 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,53 +1,43 @@ -#ifndef UI_TASK_H -#define UI_TASK_H - +#pragma once #include #include #include #ifdef PIN_BUZZER - #include +#include #endif -#include "NodePrefs.h" #include "Button.h" +#include "NodePrefs.h" - enum class UIEventType -{ - none, - contactMessage, - channelMessage, - roomMessage, - newContactMessage, - ack -}; +enum class UIEventType { none, contactMessage, channelMessage, roomMessage, newContactMessage, ack }; class UITask { - DisplayDriver* _display; - mesh::MainBoard* _board; + DisplayDriver *_display; + mesh::MainBoard *_board; #ifdef PIN_BUZZER genericBuzzer buzzer; #endif unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; - NodePrefs* _node_prefs; + NodePrefs *_node_prefs; char _version_info[32]; char _origin[62]; char _msg[80]; int _msgcount; bool _need_refresh = true; - bool _displayWasOn = false; // Track display state before button press + bool _displayWasOn = false; // Track display state before button press // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) - Button* _userButton = nullptr; + Button *_userButton = nullptr; #endif void renderCurrScreen(); void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); - + // Button action handlers void handleButtonAnyPress(); void handleButtonShortPress(); @@ -55,22 +45,21 @@ class UITask { void handleButtonTriplePress(); void handleButtonLongPress(); - public: - - UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { - _next_refresh = 0; - _connected = false; + UITask(mesh::MainBoard *board) : _board(board), _display(NULL) + { + _next_refresh = 0; + _connected = false; } - void begin(DisplayDriver* display, NodePrefs* node_prefs, const char* build_date, const char* firmware_version, uint32_t pin_code); + void begin(DisplayDriver *display, NodePrefs *node_prefs, const char *build_date, + const char *firmware_version, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } bool hasDisplay() const { return _display != NULL; } void clearMsgPreview(); void msgRead(int msgcount); - void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); + void newMsg(uint8_t path_len, const char *from_name, const char *text, int msgcount); void soundBuzzer(UIEventType bet = UIEventType::none); void shutdown(bool restart = false); void loop(); -}; -#endif //UI_TASK_H \ No newline at end of file +}; \ No newline at end of file From 73a7a96ae461301ba0419d4211ec82bb3249f75f Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 31 May 2025 20:29:03 +0200 Subject: [PATCH 061/103] wio_e5 : bme280 support --- variants/wio-e5-mini/platformio.ini | 2 ++ variants/wio-e5-mini/target.cpp | 34 ++++++++++++++++++++++------- variants/wio-e5-mini/target.h | 15 ++++++++++++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 55ab0bc8..887304ea 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -10,6 +10,8 @@ build_flags = ${stm32_base.build_flags} -I variants/wio-e5-mini build_src_filter = ${stm32_base.build_src_filter} +<../variants/wio-e5-mini> +lib_deps = ${stm32_base.lib_deps} + finitespace/BME280 @ ^3.0.0 [env:wio-e5-mini-repeater] extends = lora_e5_mini diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index 8ccbe384..7a2b0d39 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,20 +18,15 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -SensorManager sensors; +BME280I2C bme; +WIOE5SensorManager sensors(bme); #ifndef LORA_CR #define LORA_CR 5 #endif bool radio_init() { -// rtc_clock.begin(Wire); - -// #ifdef SX126X_DIO3_TCXO_VOLTAGE -// float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -// #else -// float tcxo = 1.6f; -// #endif + Wire.begin(); radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); @@ -71,3 +66,26 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (!has_bme) return false; + + float temp(NAN), hum(NAN), pres(NAN); + + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + BME280::PresUnit presUnit(BME280::PresUnit_bar); + + _bme->read(pres, temp, hum, tempUnit, presUnit); + + telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, pres); + + return true; +} + +bool WIOE5SensorManager::begin() { + has_bme = _bme->begin(); + + return has_bme; +} \ No newline at end of file diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index f7a882ee..daf7e174 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -8,6 +8,9 @@ #include #include +#include +#include + class WIOE5Board : public STM32Board { public: const char* getManufacturerName() const override { @@ -21,10 +24,20 @@ public: } }; +class WIOE5SensorManager : public SensorManager { + BME280I2C* _bme; + bool has_bme = false; + +public: + WIOE5SensorManager(BME280I2C& bme) : _bme(&bme) {} + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; +}; + extern WIOE5Board board; extern WRAPPER_CLASS radio_driver; extern VolatileRTCClock rtc_clock; -extern SensorManager sensors; +extern WIOE5SensorManager sensors; bool radio_init(); uint32_t radio_get_rng_seed(); From 92c296308a96b79c0e4199d1e9d126e37949c72f Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 08:30:53 +0200 Subject: [PATCH 062/103] wioe5: integrate sensor in sensor mgr --- src/helpers/stm32/InternalFileSystem.cpp | 7 +++++-- variants/wio-e5-mini/target.cpp | 9 ++++----- variants/wio-e5-mini/target.h | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/helpers/stm32/InternalFileSystem.cpp b/src/helpers/stm32/InternalFileSystem.cpp index 2714ec6b..dc032eb9 100644 --- a/src/helpers/stm32/InternalFileSystem.cpp +++ b/src/helpers/stm32/InternalFileSystem.cpp @@ -126,11 +126,14 @@ InternalFileSystem::InternalFileSystem(void) bool InternalFileSystem::begin(void) { + volatile bool format_fs; #ifdef FORMAT_FS - this->format(); + format_fs = true; + #else + format_fs = false; // you can always use debugger to force formatting ;) #endif // failed to mount, erase all sector then format and mount again - if ( !Adafruit_LittleFS::begin() ) + if ( format_fs || !Adafruit_LittleFS::begin() ) { // lfs format this->format(); diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index 7a2b0d39..e3dd2ec7 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,8 +18,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -BME280I2C bme; -WIOE5SensorManager sensors(bme); +WIOE5SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 @@ -73,9 +72,9 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); - BME280::PresUnit presUnit(BME280::PresUnit_bar); + BME280::PresUnit presUnit(BME280::PresUnit_hPa); - _bme->read(pres, temp, hum, tempUnit, presUnit); + bme.read(pres, temp, hum, tempUnit, presUnit); telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); @@ -85,7 +84,7 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } bool WIOE5SensorManager::begin() { - has_bme = _bme->begin(); + has_bme = bme.begin(); return has_bme; } \ No newline at end of file diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index daf7e174..4b510d56 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -25,11 +25,11 @@ public: }; class WIOE5SensorManager : public SensorManager { - BME280I2C* _bme; + BME280I2C bme; bool has_bme = false; public: - WIOE5SensorManager(BME280I2C& bme) : _bme(&bme) {} + WIOE5SensorManager() {} bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; }; From 31cbf9ed0e5af4ac70e5a59ea93f79319a685c12 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 31 May 2025 10:57:22 +0200 Subject: [PATCH 063/103] gps : sync time on fix --- examples/companion_radio/main.cpp | 2 +- src/helpers/sensors/LocationProvider.h | 6 ++++ .../sensors/MicroNMEALocationProvider.h | 29 +++++++++++++++++-- variants/t1000-e/target.cpp | 21 ++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 6677d49b..8c822a5c 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -74,7 +74,7 @@ static uint32_t _atoi(const char* sp) { /* GLOBAL OBJECTS */ 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); #ifdef DISPLAY_CLASS #include "UITask.h" diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 056e61e0..f51eea28 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -4,13 +4,19 @@ class LocationProvider { +protected: + bool _time_sync_needed = true; public: + virtual void syncTime() { _time_sync_needed = true; } + virtual bool waitingTimeSync() { return _time_sync_needed; } virtual long getLatitude() = 0; virtual long getLongitude() = 0; virtual long getAltitude() = 0; + virtual long satellitesCount() = 0; virtual bool isValid() = 0; virtual long getTimestamp() = 0; + virtual void sendSentence(const char * sentence); virtual void reset(); virtual void begin(); virtual void stop(); diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 9f439e25..ee6e43eb 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -19,13 +19,16 @@ class MicroNMEALocationProvider : public LocationProvider { char _nmeaBuffer[100]; MicroNMEA nmea; + mesh::RTCClock* _clock; Stream* _gps_serial; int _pin_reset; int _pin_en; + long next_check = 0; + long time_valid = 0; public : - MicroNMEALocationProvider(Stream& ser, 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) { + MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = &rtc_clock, 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) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); digitalWrite(_pin_reset, GPS_RESET_FORCE); @@ -59,6 +62,7 @@ public : } } + void syncTime() override { nmea.clear(); LocationProvider::syncTime(); } long getLatitude() override { return nmea.getLatitude(); } long getLongitude() override { return nmea.getLongitude(); } long getAltitude() override { @@ -66,6 +70,7 @@ public : nmea.getAltitude(alt); return alt; } + long satellitesCount() override { return nmea.getNumSatellites(); } bool isValid() override { return nmea.isValid(); } long getTimestamp() override { @@ -73,7 +78,12 @@ public : return dt.unixtime(); } + void sendSentence(const char *sentence) override { + nmea.sendSentence(*_gps_serial, sentence); + } + void loop() override { + while (_gps_serial->available()) { char c = _gps_serial->read(); #ifdef GPS_NMEA_DEBUG @@ -81,5 +91,20 @@ public : #endif nmea.process(c); } + + if (!isValid()) time_valid = 0; + + if (millis() > next_check) { + next_check = millis() + 1000; + if (_time_sync_needed && time_valid > 2) { + if (_clock != NULL) { + rtc_clock.setCurrentTime(getTimestamp()); + _time_sync_needed = false; + } + } + if (isValid()) { + time_valid ++; + } + } } }; \ No newline at end of file diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index be82ca76..296551e1 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -9,7 +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; -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); T1000SensorManager sensors = T1000SensorManager(nmea); #ifdef DISPLAY_CLASS @@ -179,14 +179,26 @@ void T1000SensorManager::loop() { } } -int T1000SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) +int T1000SensorManager::getNumSettings() const { return 2; } // just one supported: "gps" (power switch) const char* T1000SensorManager::getSettingName(int i) const { - return i == 0 ? "gps" : NULL; + switch (i) { + case 0: + return "gps"; + break; + case 1: + return "sync"; + break; + default: + return NULL; + break; + } } const char* T1000SensorManager::getSettingValue(int i) const { if (i == 0) { return gps_active ? "1" : "0"; + } else if (i == 1) { + return _nmea->waitingTimeSync() ? "1" : "0"; } return NULL; } @@ -198,6 +210,9 @@ bool T1000SensorManager::setSettingValue(const char* name, const char* value) { start_gps(); } return true; + } else if (strcmp(name, "sync") == 0) { + _nmea->syncTime(); // whatever the value ... + return true; } return false; // not supported } From 49da6957b5ea18f449f2e389f8c3476c2fdfd426 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 14:12:22 +0200 Subject: [PATCH 064/103] micronmea: was using global rtc_clock to sync instead of _clock --- src/helpers/sensors/MicroNMEALocationProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index ee6e43eb..a4a2f5d6 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -98,7 +98,7 @@ public : next_check = millis() + 1000; if (_time_sync_needed && time_valid > 2) { if (_clock != NULL) { - rtc_clock.setCurrentTime(getTimestamp()); + _clock.setCurrentTime(getTimestamp()); _time_sync_needed = false; } } From 14cd4ea0104b4be5f0b1a48ba822193bd603f2e8 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 1 Jun 2025 15:32:02 +0200 Subject: [PATCH 065/103] t1000: remove sync custom var --- .../sensors/MicroNMEALocationProvider.h | 2 +- variants/t1000-e/target.cpp | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index a4a2f5d6..e8b09d9a 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -98,7 +98,7 @@ public : next_check = millis() + 1000; if (_time_sync_needed && time_valid > 2) { if (_clock != NULL) { - _clock.setCurrentTime(getTimestamp()); + _clock->setCurrentTime(getTimestamp()); _time_sync_needed = false; } } diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 296551e1..29ca1acd 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -179,26 +179,14 @@ void T1000SensorManager::loop() { } } -int T1000SensorManager::getNumSettings() const { return 2; } // just one supported: "gps" (power switch) +int T1000SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) const char* T1000SensorManager::getSettingName(int i) const { - switch (i) { - case 0: - return "gps"; - break; - case 1: - return "sync"; - break; - default: - return NULL; - break; - } + return i == 0 ? "gps" : NULL; } const char* T1000SensorManager::getSettingValue(int i) const { if (i == 0) { return gps_active ? "1" : "0"; - } else if (i == 1) { - return _nmea->waitingTimeSync() ? "1" : "0"; } return NULL; } @@ -210,9 +198,6 @@ bool T1000SensorManager::setSettingValue(const char* name, const char* value) { start_gps(); } return true; - } else if (strcmp(name, "sync") == 0) { - _nmea->syncTime(); // whatever the value ... - return true; } return false; // not supported } From 3749264e073a8440a2cf28376f5132915baa3c49 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 1 Jun 2025 23:55:57 +1000 Subject: [PATCH 066/103] * MicroNMEALocationProvider: clock param now NULL by default --- src/helpers/sensors/MicroNMEALocationProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index e8b09d9a..5a2c59d3 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -27,7 +27,7 @@ class MicroNMEALocationProvider : public LocationProvider { long time_valid = 0; public : - MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = &rtc_clock, int pin_reset = GPS_RESET, int pin_en = GPS_EN) : + 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) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); From 42efbda40a8c7ef334b7745b661066e3f1baae69 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 19:57:35 -0700 Subject: [PATCH 067/103] Re-applying ecd2b0b --- examples/companion_radio/MyMesh.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index db40169f..f4ee900b 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -545,7 +545,8 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } - out_frame[i++] = findChannelIdx(channel); + uint8_t channel_idx = findChannelIdx(channel); + out_frame[i++] = channel_idx; uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; out_frame[i++] = TXT_TYPE_PLAIN; @@ -570,7 +571,13 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe #endif } #ifdef DISPLAY_CLASS - ui_task.newMsg(path_len, "Public", text, offline_queue_len); + // Get the channel name from the channel index + const char *channel_name = "Unknown"; + ChannelDetails channel_details; + if (getChannel(channel_idx, channel_details)) { + channel_name = channel_details.name; + } + ui_task.newMsg(path_len, channel_name, text, offline_queue_len); #endif } From 40bf7bbb9f4536c947d3589914013b81f0979565 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:15:16 -0700 Subject: [PATCH 068/103] Reverting changes to Button code --- examples/companion_radio/Button.cpp | 231 +++++++++++++--------------- examples/companion_radio/Button.h | 112 ++++++++------ 2 files changed, 169 insertions(+), 174 deletions(-) diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/Button.cpp index 5de4b702..ec1f0f69 100644 --- a/examples/companion_radio/Button.cpp +++ b/examples/companion_radio/Button.cpp @@ -1,142 +1,125 @@ #include "Button.h" -Button::Button(uint8_t pin, bool activeState) - : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) -{ - _currentState = false; // Initialize as not pressed - _lastState = _currentState; +Button::Button(uint8_t pin, bool activeState) + : _pin(pin), _activeState(activeState), _isAnalog(false), _analogThreshold(20) { + _currentState = false; // Initialize as not pressed + _lastState = _currentState; } Button::Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold) - : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) -{ - _currentState = false; // Initialize as not pressed - _lastState = _currentState; + : _pin(pin), _activeState(activeState), _isAnalog(isAnalog), _analogThreshold(analogThreshold) { + _currentState = false; // Initialize as not pressed + _lastState = _currentState; } -void Button::begin() -{ - _currentState = readButton(); - _lastState = _currentState; +void Button::begin() { + _currentState = readButton(); + _lastState = _currentState; } -void Button::update() -{ - uint32_t now = millis(); - - // Read button at specified interval - if (now - _lastReadTime < BUTTON_READ_INTERVAL_MS) { - return; - } - _lastReadTime = now; - - bool newState = readButton(); - - // Check if state has changed - if (newState != _lastState) { - _stateChangeTime = now; - } - - // Debounce check - if ((now - _stateChangeTime) > BUTTON_DEBOUNCE_TIME_MS) { - if (newState != _currentState) { - _currentState = newState; - handleStateChange(); +void Button::update() { + uint32_t now = millis(); + + // Read button at specified interval + if (now - _lastReadTime < BUTTON_READ_INTERVAL_MS) { + return; } - } - - _lastState = newState; - - // Handle multi-click timeout - if (_state == WAITING_FOR_MULTI_CLICK && (now - _releaseTime) > BUTTON_CLICK_TIMEOUT_MS) { - // Timeout reached, process the clicks - if (_clickCount == 1) { - triggerEvent(SHORT_PRESS); + _lastReadTime = now; + + bool newState = readButton(); + + // Check if state has changed + if (newState != _lastState) { + _stateChangeTime = now; } - else if (_clickCount == 2) { - triggerEvent(DOUBLE_PRESS); + + // Debounce check + if ((now - _stateChangeTime) > BUTTON_DEBOUNCE_TIME_MS) { + if (newState != _currentState) { + _currentState = newState; + handleStateChange(); + } } - else if (_clickCount >= 3) { - triggerEvent(TRIPLE_PRESS); - } - _clickCount = 0; - _state = IDLE; - } - - // Handle long press while button is held - if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { - triggerEvent(LONG_PRESS); - _state = IDLE; // Prevent multiple press events - _clickCount = 0; - } -} - -bool Button::readButton() -{ - if (_isAnalog) { - return (analogRead(_pin) < _analogThreshold); - } - else { - return (digitalRead(_pin) == _activeState); - } -} - -void Button::handleStateChange() -{ - uint32_t now = millis(); - - if (_currentState) { - // Button pressed - _pressTime = now; - _state = PRESSED; - triggerEvent(ANY_PRESS); - } - else { - // Button released - if (_state == PRESSED) { - uint32_t pressDuration = now - _pressTime; - - if (pressDuration < BUTTON_LONG_PRESS_TIME_MS) { - // Short press detected - _clickCount++; - _releaseTime = now; - _state = WAITING_FOR_MULTI_CLICK; - } - else { - // Long press already handled in update() - _state = IDLE; + + _lastState = newState; + + // Handle multi-click timeout + if (_state == WAITING_FOR_MULTI_CLICK && (now - _releaseTime) > BUTTON_CLICK_TIMEOUT_MS) { + // Timeout reached, process the clicks + if (_clickCount == 1) { + triggerEvent(SHORT_PRESS); + } else if (_clickCount == 2) { + triggerEvent(DOUBLE_PRESS); + } else if (_clickCount >= 3) { + triggerEvent(TRIPLE_PRESS); + } + _clickCount = 0; + _state = IDLE; + } + + // Handle long press while button is held + if (_state == PRESSED && (now - _pressTime) > BUTTON_LONG_PRESS_TIME_MS) { + triggerEvent(LONG_PRESS); + _state = IDLE; // Prevent multiple press events _clickCount = 0; - } } - } } -void Button::triggerEvent(EventType event) -{ - _lastEvent = event; +bool Button::readButton() { + if (_isAnalog) { + return (analogRead(_pin) < _analogThreshold); + } else { + return (digitalRead(_pin) == _activeState); + } +} - switch (event) { - case ANY_PRESS: - if (_onAnyPress) - _onAnyPress(); - break; - case SHORT_PRESS: - if (_onShortPress) - _onShortPress(); - break; - case DOUBLE_PRESS: - if (_onDoublePress) - _onDoublePress(); - break; - case TRIPLE_PRESS: - if (_onTriplePress) - _onTriplePress(); - break; - case LONG_PRESS: - if (_onLongPress) - _onLongPress(); - break; - default: - break; - } +void Button::handleStateChange() { + uint32_t now = millis(); + + if (_currentState) { + // Button pressed + _pressTime = now; + _state = PRESSED; + triggerEvent(ANY_PRESS); + } else { + // Button released + if (_state == PRESSED) { + uint32_t pressDuration = now - _pressTime; + + if (pressDuration < BUTTON_LONG_PRESS_TIME_MS) { + // Short press detected + _clickCount++; + _releaseTime = now; + _state = WAITING_FOR_MULTI_CLICK; + } else { + // Long press already handled in update() + _state = IDLE; + _clickCount = 0; + } + } + } +} + +void Button::triggerEvent(EventType event) { + _lastEvent = event; + + switch (event) { + case ANY_PRESS: + if (_onAnyPress) _onAnyPress(); + break; + case SHORT_PRESS: + if (_onShortPress) _onShortPress(); + break; + case DOUBLE_PRESS: + if (_onDoublePress) _onDoublePress(); + break; + case TRIPLE_PRESS: + if (_onTriplePress) _onTriplePress(); + break; + case LONG_PRESS: + if (_onLongPress) _onLongPress(); + break; + default: + break; + } } \ No newline at end of file diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/Button.h index 85564593..47c792bd 100644 --- a/examples/companion_radio/Button.h +++ b/examples/companion_radio/Button.h @@ -4,62 +4,74 @@ #include // Button timing configuration -#define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms -#define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click -#define BUTTON_LONG_PRESS_TIME_MS 3000 // Time to trigger long press (3 seconds) -#define BUTTON_READ_INTERVAL_MS 10 // How often to read the button +#define BUTTON_DEBOUNCE_TIME_MS 50 // Debounce time in ms +#define BUTTON_CLICK_TIMEOUT_MS 500 // Max time between clicks for multi-click +#define BUTTON_LONG_PRESS_TIME_MS 3000 // Time to trigger long press (3 seconds) +#define BUTTON_READ_INTERVAL_MS 10 // How often to read the button class Button { public: - enum EventType { NONE, SHORT_PRESS, DOUBLE_PRESS, TRIPLE_PRESS, LONG_PRESS, ANY_PRESS }; + enum EventType { + NONE, + SHORT_PRESS, + DOUBLE_PRESS, + TRIPLE_PRESS, + LONG_PRESS, + ANY_PRESS + }; - using EventCallback = std::function; + using EventCallback = std::function; - Button(uint8_t pin, bool activeState = LOW); - Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold = 20); - - void begin(); - void update(); - - // Set callbacks for different events - void onShortPress(EventCallback callback) { _onShortPress = callback; } - void onDoublePress(EventCallback callback) { _onDoublePress = callback; } - void onTriplePress(EventCallback callback) { _onTriplePress = callback; } - void onLongPress(EventCallback callback) { _onLongPress = callback; } - void onAnyPress(EventCallback callback) { _onAnyPress = callback; } - - // State getters - bool isPressed() const { return _currentState; } - EventType getLastEvent() const { return _lastEvent; } + Button(uint8_t pin, bool activeState = LOW); + Button(uint8_t pin, bool activeState, bool isAnalog, uint16_t analogThreshold = 20); + + void begin(); + void update(); + + // Set callbacks for different events + void onShortPress(EventCallback callback) { _onShortPress = callback; } + void onDoublePress(EventCallback callback) { _onDoublePress = callback; } + void onTriplePress(EventCallback callback) { _onTriplePress = callback; } + void onLongPress(EventCallback callback) { _onLongPress = callback; } + void onAnyPress(EventCallback callback) { _onAnyPress = callback; } + + // State getters + bool isPressed() const { return _currentState; } + EventType getLastEvent() const { return _lastEvent; } private: - enum State { IDLE, PRESSED, RELEASED, WAITING_FOR_MULTI_CLICK }; + enum State { + IDLE, + PRESSED, + RELEASED, + WAITING_FOR_MULTI_CLICK + }; - uint8_t _pin; - bool _activeState; - bool _isAnalog; - uint16_t _analogThreshold; - - State _state = IDLE; - bool _currentState; - bool _lastState; - - uint32_t _stateChangeTime = 0; - uint32_t _pressTime = 0; - uint32_t _releaseTime = 0; - uint32_t _lastReadTime = 0; - - uint8_t _clickCount = 0; - EventType _lastEvent = NONE; - - // Callbacks - EventCallback _onShortPress = nullptr; - EventCallback _onDoublePress = nullptr; - EventCallback _onTriplePress = nullptr; - EventCallback _onLongPress = nullptr; - EventCallback _onAnyPress = nullptr; - - bool readButton(); - void handleStateChange(); - void triggerEvent(EventType event); + uint8_t _pin; + bool _activeState; + bool _isAnalog; + uint16_t _analogThreshold; + + State _state = IDLE; + bool _currentState; + bool _lastState; + + uint32_t _stateChangeTime = 0; + uint32_t _pressTime = 0; + uint32_t _releaseTime = 0; + uint32_t _lastReadTime = 0; + + uint8_t _clickCount = 0; + EventType _lastEvent = NONE; + + // Callbacks + EventCallback _onShortPress = nullptr; + EventCallback _onDoublePress = nullptr; + EventCallback _onTriplePress = nullptr; + EventCallback _onLongPress = nullptr; + EventCallback _onAnyPress = nullptr; + + bool readButton(); + void handleStateChange(); + void triggerEvent(EventType event); }; \ No newline at end of file From 9247ce460a52008943b39f25d3565fdec05384ca Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:16:29 -0700 Subject: [PATCH 069/103] Reverting changes to simple_secure_chat main --- examples/simple_secure_chat/main.cpp | 426 +++++++++++---------------- 1 file changed, 174 insertions(+), 252 deletions(-) diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 2440b697..63ff20da 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -1,57 +1,56 @@ -#include // needed for PlatformIO +#include // needed for PlatformIO #include #if defined(NRF52_PLATFORM) -#include + #include #elif defined(RP2040_PLATFORM) -#include + #include #elif defined(ESP32) -#include + #include #endif -#include #include -#include -#include #include +#include +#include +#include #include /* ---------------------------------- CONFIGURATION ------------------------------------- */ -#define FIRMWARE_VER_TEXT "v2 (build: 4 Feb 2025)" +#define FIRMWARE_VER_TEXT "v2 (build: 4 Feb 2025)" #ifndef LORA_FREQ -#define LORA_FREQ 915.0 + #define LORA_FREQ 915.0 #endif #ifndef LORA_BW -#define LORA_BW 250 + #define LORA_BW 250 #endif #ifndef LORA_SF -#define LORA_SF 10 + #define LORA_SF 10 #endif #ifndef LORA_CR -#define LORA_CR 5 + #define LORA_CR 5 #endif #ifndef LORA_TX_POWER -#define LORA_TX_POWER 20 + #define LORA_TX_POWER 20 #endif #ifndef MAX_CONTACTS -#define MAX_CONTACTS 100 + #define MAX_CONTACTS 100 #endif #include -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" // Believe it or not, this std C function is busted on some platforms! -static uint32_t _atoi(const char *sp) -{ +static uint32_t _atoi(const char* sp) { uint32_t n = 0; while (*sp && *sp >= '0' && *sp <= '9') { n *= 10; @@ -62,7 +61,7 @@ static uint32_t _atoi(const char *sp) /* -------------------------------------------------------------------------------------- */ -struct NodePrefs { // persisted to file +struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; double node_lat, node_lon; @@ -72,35 +71,30 @@ struct NodePrefs { // persisted to file }; class MyMesh : public BaseChatMesh, ContactVisitor { - FILESYSTEM *_fs; + FILESYSTEM* _fs; NodePrefs _prefs; uint32_t expected_ack_crc; - ChannelDetails *_public; + ChannelDetails* _public; unsigned long last_msg_sent; - ContactInfo *curr_recipient; - char command[512 + 10]; + ContactInfo* curr_recipient; + char command[512+10]; uint8_t tmp_buf[256]; char hex_buf[512]; - const char *getTypeName(uint8_t type) const - { - if (type == ADV_TYPE_CHAT) - return "Chat"; - if (type == ADV_TYPE_REPEATER) - return "Repeater"; - if (type == ADV_TYPE_ROOM) - return "Room"; - return "??"; // unknown + const char* getTypeName(uint8_t type) const { + if (type == ADV_TYPE_CHAT) return "Chat"; + if (type == ADV_TYPE_REPEATER) return "Repeater"; + if (type == ADV_TYPE_ROOM) return "Room"; + return "??"; // unknown } - void loadContacts() - { + void loadContacts() { if (_fs->exists("/contacts")) { -#if defined(RP2040_PLATFORM) + #if defined(RP2040_PLATFORM) File file = _fs->open("/contacts", "r"); -#else + #else File file = _fs->open("/contacts"); -#endif + #endif if (file) { bool full = false; while (!full) { @@ -110,31 +104,28 @@ class MyMesh : public BaseChatMesh, ContactVisitor { uint32_t reserved; bool success = (file.read(pub_key, 32) == 32); - success = success && (file.read((uint8_t *)&c.name, 32) == 32); + success = success && (file.read((uint8_t *) &c.name, 32) == 32); success = success && (file.read(&c.type, 1) == 1); success = success && (file.read(&c.flags, 1) == 1); success = success && (file.read(&unused, 1) == 1); - success = success && (file.read((uint8_t *)&reserved, 4) == 4); - success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); - success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.read((uint8_t *) &reserved, 4) == 4); + success = success && (file.read((uint8_t *) &c.out_path_len, 1) == 1); + success = success && (file.read((uint8_t *) &c.last_advert_timestamp, 4) == 4); success = success && (file.read(c.out_path, 64) == 64); - c.gps_lat = c.gps_lon = 0; // not yet supported + c.gps_lat = c.gps_lon = 0; // not yet supported - if (!success) - break; // EOF + if (!success) break; // EOF c.id = mesh::Identity(pub_key); c.lastmod = 0; - if (!addContact(c)) - full = true; + if (!addContact(c)) full = true; } file.close(); } } } - void saveContacts() - { + void saveContacts() { #if defined(NRF52_PLATFORM) _fs->remove("/contacts"); File file = _fs->open("/contacts", FILE_O_WRITE); @@ -151,50 +142,44 @@ class MyMesh : public BaseChatMesh, ContactVisitor { while (iter.hasNext(this, c)) { bool success = (file.write(c.id.pub_key, 32) == 32); - success = success && (file.write((uint8_t *)&c.name, 32) == 32); + success = success && (file.write((uint8_t *) &c.name, 32) == 32); success = success && (file.write(&c.type, 1) == 1); success = success && (file.write(&c.flags, 1) == 1); success = success && (file.write(&unused, 1) == 1); - success = success && (file.write((uint8_t *)&reserved, 4) == 4); - success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); - success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.write((uint8_t *) &reserved, 4) == 4); + success = success && (file.write((uint8_t *) &c.out_path_len, 1) == 1); + success = success && (file.write((uint8_t *) &c.last_advert_timestamp, 4) == 4); success = success && (file.write(c.out_path, 64) == 64); - if (!success) - break; // write failed + if (!success) break; // write failed } file.close(); } } - void setClock(uint32_t timestamp) - { + void setClock(uint32_t timestamp) { uint32_t curr = getRTCClock()->getCurrentTime(); if (timestamp > curr) { getRTCClock()->setCurrentTime(timestamp); Serial.println(" (OK - clock set!)"); - } - else { + } else { Serial.println(" (ERR: clock cannot go backwards)"); } } - void importCard(const char *command) - { - while (*command == ' ') - command++; // skip leading spaces + void importCard(const char* command) { + while (*command == ' ') command++; // skip leading spaces if (memcmp(command, "meshcore://", 11) == 0) { - command += 11; // skip the prefix - char *ep = strchr(command, 0); // find end of string + command += 11; // skip the prefix + char *ep = strchr(command, 0); // find end of string while (ep > command) { ep--; - if (mesh::Utils::isHexChar(*ep)) - break; // found tail end of card - *ep = 0; // remove trailing spaces and other junk + if (mesh::Utils::isHexChar(*ep)) break; // found tail end of card + *ep = 0; // remove trailing spaces and other junk } int len = strlen(command); if (len % 2 == 0) { - len >>= 1; // halve, for num bytes + len >>= 1; // halve, for num bytes if (mesh::Utils::fromHex(tmp_buf, len, command)) { importContact(tmp_buf, len); return; @@ -205,112 +190,97 @@ class MyMesh : public BaseChatMesh, ContactVisitor { } protected: - float getAirtimeBudgetFactor() const override { return _prefs.airtime_factor; } - - int calcRxDelay(float score, uint32_t air_time) const override - { - return 0; // disable rxdelay + float getAirtimeBudgetFactor() const override { + return _prefs.airtime_factor; } - bool allowPacketForward(const mesh::Packet *packet) override { return true; } + int calcRxDelay(float score, uint32_t air_time) const override { + return 0; // disable rxdelay + } - void onDiscoveredContact(ContactInfo &contact, bool is_new) override - { + bool allowPacketForward(const mesh::Packet* packet) override { + return true; + } + + void onDiscoveredContact(ContactInfo& contact, bool is_new) override { // TODO: if not in favs, prompt to add as fav(?) Serial.printf("ADVERT from -> %s\n", contact.name); Serial.printf(" type: %s\n", getTypeName(contact.type)); - Serial.print(" public key: "); - mesh::Utils::printHex(Serial, contact.id.pub_key, PUB_KEY_SIZE); - Serial.println(); + Serial.print(" public key: "); mesh::Utils::printHex(Serial, contact.id.pub_key, PUB_KEY_SIZE); Serial.println(); saveContacts(); } - void onContactPathUpdated(const ContactInfo &contact) override - { - Serial.printf("PATH to: %s, path_len=%d\n", contact.name, (int32_t)contact.out_path_len); + void onContactPathUpdated(const ContactInfo& contact) override { + Serial.printf("PATH to: %s, path_len=%d\n", contact.name, (int32_t) contact.out_path_len); saveContacts(); } - bool processAck(const uint8_t *data) override - { - if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient + bool processAck(const uint8_t *data) override { + if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient Serial.printf(" Got ACK! (round trip: %d millis)\n", _ms->getMillis() - last_msg_sent); // NOTE: the same ACK can be received multiple times! - expected_ack_crc = 0; // reset our expected hash, now that we have received ACK + expected_ack_crc = 0; // reset our expected hash, now that we have received ACK return true; } - // uint32_t crc; - // memcpy(&crc, data, 4); - // MESH_DEBUG_PRINTLN("unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc); + //uint32_t crc; + //memcpy(&crc, data, 4); + //MESH_DEBUG_PRINTLN("unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc); return false; } - void onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) override - { + void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { Serial.printf("(%s) MSG -> from %s\n", pkt->isRouteDirect() ? "DIRECT" : "FLOOD", from.name); Serial.printf(" %s\n", text); - if (strcmp(text, "clock sync") == 0) { // special text command + if (strcmp(text, "clock sync") == 0) { // special text command setClock(sender_timestamp + 1); } } - void onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) override - { + void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { } - void onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const uint8_t *sender_prefix, const char *text) override - { + void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { } - void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, - const char *text) override - { + void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override { if (pkt->isRouteDirect()) { Serial.printf("PUBLIC CHANNEL MSG -> (Direct!)\n"); - } - else { + } else { Serial.printf("PUBLIC CHANNEL MSG -> (Flood) hops %d\n", pkt->path_len); } Serial.printf(" %s\n", text); } - uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, - uint8_t len, uint8_t *reply) override - { - return 0; // unknown + uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) override { + return 0; // unknown } - void onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) override - { + void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { // not supported } - uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override - { + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } - uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override - { - return SEND_TIMEOUT_BASE_MILLIS + - ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * - (path_len + 1)); + uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override { + return SEND_TIMEOUT_BASE_MILLIS + + ( (pkt_airtime_millis*DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); } - void onSendTimeout() override { Serial.println(" ERROR: timed out, no ACK."); } + void onSendTimeout() override { + Serial.println(" ERROR: timed out, no ACK."); + } public: - MyMesh(mesh::Radio &radio, StdRNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) - : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) + MyMesh(mesh::Radio& radio, StdRNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables) + : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) { // defaults memset(&_prefs, 0, sizeof(_prefs)); - _prefs.airtime_factor = 2.0; // one third + _prefs.airtime_factor = 2.0; // one third strcpy(_prefs.node_name, "NONAME"); _prefs.freq = LORA_FREQ; _prefs.tx_power_dbm = LORA_TX_POWER; @@ -322,49 +292,45 @@ public: float getFreqPref() const { return _prefs.freq; } uint8_t getTxPowerPref() const { return _prefs.tx_power_dbm; } - void begin(FILESYSTEM &fs) - { + void begin(FILESYSTEM& fs) { _fs = &fs; BaseChatMesh::begin(); -#if defined(NRF52_PLATFORM) + #if defined(NRF52_PLATFORM) IdentityStore store(fs, ""); -#elif defined(RP2040_PLATFORM) + #elif defined(RP2040_PLATFORM) IdentityStore store(fs, "/identity"); store.begin(); -#else + #else IdentityStore store(fs, "/identity"); -#endif - if (!store.load("_main", self_id, _prefs.node_name, - sizeof(_prefs.node_name))) { // legacy: node_name was from identity file + #endif + if (!store.load("_main", self_id, _prefs.node_name, sizeof(_prefs.node_name))) { // legacy: node_name was from identity file // Need way to get some entropy to seed RNG Serial.println("Press ENTER to generate key:"); char c = 0; - while (c != '\n') { // wait for ENTER to be pressed - if (Serial.available()) - c = Serial.read(); + while (c != '\n') { // wait for ENTER to be pressed + if (Serial.available()) c = Serial.read(); } ((StdRNG *)getRNG())->begin(millis()); - self_id = mesh::LocalIdentity(getRNG()); // create new random identity + self_id = mesh::LocalIdentity(getRNG()); // create new random identity int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes - self_id = mesh::LocalIdentity(getRNG()); - count++; + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes + self_id = mesh::LocalIdentity(getRNG()); count++; } store.save("_main", self_id); } // load persisted prefs if (_fs->exists("/node_prefs")) { -#if defined(RP2040_PLATFORM) + #if defined(RP2040_PLATFORM) File file = _fs->open("/node_prefs", "r"); -#else + #else File file = _fs->open("/node_prefs"); -#endif + #endif if (file) { - file.read((uint8_t *)&_prefs, sizeof(_prefs)); + file.read((uint8_t *) &_prefs, sizeof(_prefs)); file.close(); } } @@ -373,8 +339,7 @@ public: _public = addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel } - void savePrefs() - { + void savePrefs() { #if defined(NRF52_PLATFORM) _fs->remove("/node_prefs"); File file = _fs->open("/node_prefs", FILE_O_WRITE); @@ -389,8 +354,7 @@ public: } } - void showWelcome() - { + void showWelcome() { Serial.println("===== MeshCore Chat Terminal ====="); Serial.println(); Serial.printf("WELCOME %s\n", _prefs.node_name); @@ -400,8 +364,7 @@ public: Serial.println(); } - void sendSelfAdvert(int delay_millis) - { + void sendSelfAdvert(int delay_millis) { auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { sendFlood(pkt, delay_millis); @@ -409,8 +372,7 @@ public: } // ContactVisitor - void onContactVisit(const ContactInfo &contact) override - { + void onContactVisit(const ContactInfo& contact) override { Serial.printf(" %s - ", contact.name); char tmp[40]; int32_t secs = contact.last_advert_timestamp - getRTCClock()->getCurrentTime(); @@ -418,159 +380,129 @@ public: Serial.println(tmp); } - void handleCommand(const char *command) - { - while (*command == ' ') - command++; // skip leading spaces + void handleCommand(const char* command) { + while (*command == ' ') command++; // skip leading spaces if (memcmp(command, "send ", 5) == 0) { if (curr_recipient) { const char *text = &command[5]; uint32_t est_timeout; - int result = sendMessage(*curr_recipient, getRTCClock()->getCurrentTime(), 0, text, expected_ack_crc, - est_timeout); + int result = sendMessage(*curr_recipient, getRTCClock()->getCurrentTime(), 0, text, expected_ack_crc, est_timeout); if (result == MSG_SEND_FAILED) { Serial.println(" ERROR: unable to send."); - } - else { + } else { last_msg_sent = _ms->getMillis(); Serial.printf(" (message sent - %s)\n", result == MSG_SEND_SENT_FLOOD ? "FLOOD" : "DIRECT"); } - } - else { + } else { Serial.println(" ERROR: no recipient selected (use 'to' cmd)."); } - } - else if (memcmp(command, "public ", 7) == 0) { // send GroupChannel msg - uint8_t temp[5 + MAX_TEXT_LEN + 32]; + } else if (memcmp(command, "public ", 7) == 0) { // send GroupChannel msg + uint8_t temp[5+MAX_TEXT_LEN+32]; uint32_t timestamp = getRTCClock()->getCurrentTime(); - memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique - temp[4] = 0; // attempt and flags + memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique + temp[4] = 0; // attempt and flags - sprintf((char *)&temp[5], "%s: %s", _prefs.node_name, &command[7]); // : - temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long + sprintf((char *) &temp[5], "%s: %s", _prefs.node_name, &command[7]); // : + temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long - int len = strlen((char *)&temp[5]); + int len = strlen((char *) &temp[5]); auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, _public->channel, temp, 5 + len); if (pkt) { sendFlood(pkt); Serial.println(" Sent."); - } - else { + } else { Serial.println(" ERROR: unable to send"); } - } - else if (memcmp(command, "list", 4) == 0) { // show Contact list, by most recent + } else if (memcmp(command, "list", 4) == 0) { // show Contact list, by most recent int n = 0; - if (command[4] == ' ') { // optional param, last 'N' + if (command[4] == ' ') { // optional param, last 'N' n = atoi(&command[5]); } scanRecentContacts(n, this); - } - else if (strcmp(command, "clock") == 0) { // show current time + } else if (strcmp(command, "clock") == 0) { // show current time uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); - Serial.printf("%02d:%02d - %d/%d/%d UTC\n", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); - } - else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) + Serial.printf( "%02d:%02d - %d/%d/%d UTC\n", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); + } else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) uint32_t secs = _atoi(&command[5]); setClock(secs); - } - else if (memcmp(command, "to ", 3) == 0) { // set current recipient + } else if (memcmp(command, "to ", 3) == 0) { // set current recipient curr_recipient = searchContactsByPrefix(&command[3]); if (curr_recipient) { Serial.printf(" Recipient %s now selected.\n", curr_recipient->name); - } - else { + } else { Serial.println(" Error: Name prefix not found."); } - } - else if (strcmp(command, "to") == 0) { // show current recipient + } else if (strcmp(command, "to") == 0) { // show current recipient if (curr_recipient) { - Serial.printf(" Current: %s\n", curr_recipient->name); + Serial.printf(" Current: %s\n", curr_recipient->name); + } else { + Serial.println(" Err: no recipient selected"); } - else { - Serial.println(" Err: no recipient selected"); - } - } - else if (strcmp(command, "advert") == 0) { + } else if (strcmp(command, "advert") == 0) { auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { sendZeroHop(pkt); Serial.println(" (advert sent, zero hop)."); - } - else { + } else { Serial.println(" ERR: unable to send"); } - } - else if (strcmp(command, "reset path") == 0) { + } else if (strcmp(command, "reset path") == 0) { if (curr_recipient) { resetPathTo(*curr_recipient); saveContacts(); Serial.println(" Done."); } - } - else if (memcmp(command, "card", 4) == 0) { + } else if (memcmp(command, "card", 4) == 0) { Serial.printf("Hello %s\n", _prefs.node_name); auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon); if (pkt) { - uint8_t len = pkt->writeTo(tmp_buf); - releasePacket(pkt); // undo the obtainNewPacket() + uint8_t len = pkt->writeTo(tmp_buf); + releasePacket(pkt); // undo the obtainNewPacket() mesh::Utils::toHex(hex_buf, tmp_buf, len); Serial.println("Your MeshCore biz card:"); - Serial.print("meshcore://"); - Serial.println(hex_buf); + Serial.print("meshcore://"); Serial.println(hex_buf); Serial.println(); - } - else { + } else { Serial.println(" Error"); } - } - else if (memcmp(command, "import ", 7) == 0) { + } else if (memcmp(command, "import ", 7) == 0) { importCard(&command[7]); - } - else if (memcmp(command, "set ", 4) == 0) { - const char *config = &command[4]; + } else if (memcmp(command, "set ", 4) == 0) { + const char* config = &command[4]; if (memcmp(config, "af ", 3) == 0) { _prefs.airtime_factor = atof(&config[3]); savePrefs(); Serial.println(" OK"); - } - else if (memcmp(config, "name ", 5) == 0) { + } else if (memcmp(config, "name ", 5) == 0) { StrHelper::strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)); savePrefs(); Serial.println(" OK"); - } - else if (memcmp(config, "lat ", 4) == 0) { + } else if (memcmp(config, "lat ", 4) == 0) { _prefs.node_lat = atof(&config[4]); savePrefs(); Serial.println(" OK"); - } - else if (memcmp(config, "lon ", 4) == 0) { + } else if (memcmp(config, "lon ", 4) == 0) { _prefs.node_lon = atof(&config[4]); savePrefs(); Serial.println(" OK"); - } - else if (memcmp(config, "tx ", 3) == 0) { + } else if (memcmp(config, "tx ", 3) == 0) { _prefs.tx_power_dbm = atoi(&config[3]); savePrefs(); Serial.println(" OK - reboot to apply"); - } - else if (memcmp(config, "freq ", 5) == 0) { + } else if (memcmp(config, "freq ", 5) == 0) { _prefs.freq = atof(&config[5]); savePrefs(); Serial.println(" OK - reboot to apply"); - } - else { + } else { Serial.printf(" ERROR: unknown config: %s\n", config); } - } - else if (memcmp(command, "ver", 3) == 0) { + } else if (memcmp(command, "ver", 3) == 0) { Serial.println(FIRMWARE_VER_TEXT); - } - else if (memcmp(command, "help", 4) == 0) { + } else if (memcmp(command, "help", 4) == 0) { Serial.println("Commands:"); Serial.println(" set {name|lat|lon|freq|tx|af} {value}"); Serial.println(" card"); @@ -584,59 +516,50 @@ public: Serial.println(" advert"); Serial.println(" reset path"); Serial.println(" public "); - } - else { - Serial.print(" ERROR: unknown command: "); - Serial.println(command); + } else { + Serial.print(" ERROR: unknown command: "); Serial.println(command); } } - void loop() - { + void loop() { BaseChatMesh::loop(); int len = strlen(command); - while (Serial.available() && len < sizeof(command) - 1) { + while (Serial.available() && len < sizeof(command)-1) { char c = Serial.read(); - if (c != '\n') { + if (c != '\n') { command[len++] = c; command[len] = 0; } Serial.print(c); } - if (len == sizeof(command) - 1) { // command buffer full - command[sizeof(command) - 1] = '\r'; + if (len == sizeof(command)-1) { // command buffer full + command[sizeof(command)-1] = '\r'; } - if (len > 0 && command[len - 1] == '\r') { // received complete line - command[len - 1] = 0; // replace newline with C string null terminator + if (len > 0 && command[len - 1] == '\r') { // received complete line + command[len - 1] = 0; // replace newline with C string null terminator handleCommand(command); - command[0] = 0; // reset command buffer + command[0] = 0; // reset command buffer } } }; 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, *new VolatileRTCClock(), tables); // TODO: test with 'rtc_clock' in target.cpp -void halt() -{ - while (1) - ; +void halt() { + while (1) ; } -void setup() -{ +void setup() { Serial.begin(115200); board.begin(); - if (!radio_init()) { - halt(); - } + if (!radio_init()) { halt(); } fast_rng.begin(radio_get_rng_seed()); @@ -650,7 +573,7 @@ void setup() SPIFFS.begin(true); the_mesh.begin(SPIFFS); #else -#error "need to define filesystem" + #error "need to define filesystem" #endif radio_set_params(the_mesh.getFreqPref(), LORA_BW, LORA_SF, LORA_CR); @@ -659,10 +582,9 @@ void setup() the_mesh.showWelcome(); // send out initial Advertisement to the mesh - the_mesh.sendSelfAdvert(1200); // add slight delay + the_mesh.sendSelfAdvert(1200); // add slight delay } -void loop() -{ +void loop() { the_mesh.loop(); } From 69b431a517b99cc425521ad2840f938407c5658a Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:28:29 -0700 Subject: [PATCH 070/103] Re-applying 73a7a96, formatting, MyMesh reformat --- .clang-format | 4 +- examples/companion_radio/MyMesh.cpp | 504 ++++++++++------------------ variants/wio-e5-mini/target.cpp | 9 +- 3 files changed, 177 insertions(+), 340 deletions(-) diff --git a/.clang-format b/.clang-format index 80c7a10a..66ecd43a 100644 --- a/.clang-format +++ b/.clang-format @@ -14,7 +14,7 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None @@ -35,7 +35,7 @@ BraceWrapping: BeforeElse: true IndentBraces: false BreakBeforeBinaryOperators: None -BreakBeforeBraces: Stroustrup +BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 110 diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1988c367..e53022f6 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -72,8 +72,7 @@ #include "UITask.h" #endif -void MyMesh::loadMainIdentity() -{ +void MyMesh::loadMainIdentity() { if (!_identity_store->load("_main", self_id)) { self_id = radio_new_identity(); // create new random identity int count = 0; @@ -85,13 +84,11 @@ void MyMesh::loadMainIdentity() } } -bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) -{ +bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) { return _identity_store->save("_main", identity); } -void MyMesh::loadContacts() -{ +void MyMesh::loadContacts() { if (_fs->exists("/contacts3")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/contacts3", "r"); @@ -118,20 +115,17 @@ void MyMesh::loadContacts() success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); - if (!success) - break; // EOF + if (!success) break; // EOF c.id = mesh::Identity(pub_key); - if (!addContact(c)) - full = true; + if (!addContact(c)) full = true; } file.close(); } } } -void MyMesh::saveContacts() -{ +void MyMesh::saveContacts() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/contacts3"); File file = _fs->open("/contacts3", FILE_O_WRITE); @@ -159,15 +153,13 @@ void MyMesh::saveContacts() success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); - if (!success) - break; // write failed + if (!success) break; // write failed } file.close(); } } -void MyMesh::loadChannels() -{ +void MyMesh::loadChannels() { if (_fs->exists("/channels2")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "r"); @@ -185,13 +177,11 @@ void MyMesh::loadChannels() success = success && (file.read((uint8_t *)ch.name, 32) == 32); success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); - if (!success) - break; // EOF + if (!success) break; // EOF if (setChannel(channel_idx, ch)) { channel_idx++; - } - else { + } else { full = true; } } @@ -200,8 +190,7 @@ void MyMesh::loadChannels() } } -void MyMesh::saveChannels() -{ +void MyMesh::saveChannels() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/channels2"); File file = _fs->open("/channels2", FILE_O_WRITE); @@ -221,21 +210,18 @@ void MyMesh::saveChannels() success = success && (file.write((uint8_t *)ch.name, 32) == 32); success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); - if (!success) - break; // write failed + if (!success) break; // write failed channel_idx++; } file.close(); } } -int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) -{ +int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { char path[64]; char fname[18]; - if (key_len > 8) - key_len = 8; // just use first 8 bytes (prefix) + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); @@ -254,13 +240,11 @@ int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) return 0; // not found } -bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) -{ +bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { char path[64]; char fname[18]; - if (key_len > 8) - key_len = 8; // just use first 8 bytes (prefix) + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); @@ -275,37 +259,32 @@ bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_bu if (f) { int n = f.write(src_buf, len); f.close(); - if (n == len) - return true; // success! + if (n == len) return true; // success! _fs->remove(path); // blob was only partially written! } return false; // error } -void MyMesh::writeOKFrame() -{ +void MyMesh::writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; _serial->writeFrame(buf, 1); } -void MyMesh::writeErrFrame(uint8_t err_code) -{ +void MyMesh::writeErrFrame(uint8_t err_code) { uint8_t buf[2]; buf[0] = RESP_CODE_ERR; buf[1] = err_code; _serial->writeFrame(buf, 2); } -void MyMesh::writeDisabledFrame() -{ +void MyMesh::writeDisabledFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_DISABLED; _serial->writeFrame(buf, 1); } -void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) -{ +void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) { int i = 0; out_frame[i++] = code; memcpy(&out_frame[i], contact.id.pub_key, PUB_KEY_SIZE); @@ -328,8 +307,7 @@ void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) _serial->writeFrame(out_frame, i); } -void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) -{ +void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) { int i = 0; uint8_t code = frame[i++]; // eg. CMD_ADD_UPDATE_CONTACT memcpy(contact.id.pub_key, &frame[i], PUB_KEY_SIZE); @@ -351,19 +329,16 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, } } -void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) -{ +void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { if (offline_queue_len >= OFFLINE_QUEUE_SIZE) { MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); - } - else { + } else { offline_queue[offline_queue_len].len = len; memcpy(offline_queue[offline_queue_len].buf, frame, len); offline_queue_len++; } } -int MyMesh::getFromOfflineQueue(uint8_t frame[]) -{ +int MyMesh::getFromOfflineQueue(uint8_t frame[]) { if (offline_queue_len > 0) { // check offline queue size_t len = offline_queue[0].len; // take from top of queue memcpy(frame, offline_queue[0].buf, len); @@ -377,25 +352,20 @@ int MyMesh::getFromOfflineQueue(uint8_t frame[]) return 0; // queue is empty } -float MyMesh::getAirtimeBudgetFactor() const -{ +float MyMesh::getAirtimeBudgetFactor() const { return _prefs.airtime_factor; } -int MyMesh::getInterferenceThreshold() const -{ +int MyMesh::getInterferenceThreshold() const { return 0; // disabled for now, until currentRSSI() problem is resolved } -int MyMesh::calcRxDelay(float score, uint32_t air_time) const -{ - if (_prefs.rx_delay_base <= 0.0f) - return 0; +int MyMesh::calcRxDelay(float score, uint32_t air_time) const { + if (_prefs.rx_delay_base <= 0.0f) return 0; return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } -void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) -{ +void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; out_frame[i++] = PUSH_CODE_LOG_RX_DATA; @@ -408,24 +378,20 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) } } -bool MyMesh::isAutoAddEnabled() const -{ +bool MyMesh::isAutoAddEnabled() const { return (_prefs.manual_add_contacts & 1) == 0; } -void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) -{ +void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) { if (_serial->isConnected()) { if (!isAutoAddEnabled() && is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); - } - else { + } else { out_frame[0] = PUSH_CODE_ADVERT; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::newContactMessage); #endif @@ -434,8 +400,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } -void MyMesh::onContactPathUpdated(const ContactInfo &contact) -{ +void MyMesh::onContactPathUpdated(const ContactInfo &contact) { out_frame[0] = PUSH_CODE_PATH_UPDATED; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected @@ -443,8 +408,7 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } -bool MyMesh::processAck(const uint8_t *data) -{ +bool MyMesh::processAck(const uint8_t *data) { // see if matches any in a table for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) { if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient @@ -463,16 +427,14 @@ bool MyMesh::processAck(const uint8_t *data) } void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, - uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) -{ + uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) { int i = 0; if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 - } - else { + } else { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; } memcpy(&out_frame[i], from.id.pub_key, 6); @@ -497,8 +459,7 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::contactMessage); #endif @@ -509,22 +470,19 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe } void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) -{ + const char *text) { markConnectionActive(from); // in case this is from a server, and we have a connection queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); } void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) -{ + const char *text) { markConnectionActive(from); // in case this is from a server, and we have a connection queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); } void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const uint8_t *sender_prefix, const char *text) -{ + const uint8_t *sender_prefix, const char *text) { markConnectionActive(from); // from.sync_since change needs to be persisted dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); @@ -532,16 +490,14 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin } void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, - const char *text) -{ + const char *text) { int i = 0; if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 - } - else { + } else { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } @@ -564,8 +520,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::channelMessage); #endif @@ -582,30 +537,26 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe } uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, - uint8_t len, uint8_t *reply) -{ + uint8_t len, uint8_t *reply) { if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { uint8_t permissions = 0; uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) { permissions = TELEM_PERM_BASE; - } - else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { permissions = cp & TELEM_PERM_BASE; } if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_LOCATION; - } - else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_LOCATION; } if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_ENVIRONMENT; - } - else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_ENVIRONMENT; } @@ -626,8 +577,7 @@ uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_tim return 0; // unknown } -void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) -{ +void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) { uint32_t tag; memcpy(&tag, data, 4); @@ -640,9 +590,8 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false memcpy(&out_frame[i], contact.id.pub_key, 6); - i += 6; // pub_key_prefix - } - else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response + i += 6; // pub_key_prefix + } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; if (keep_alive_secs > 0) { startConnection(contact, keep_alive_secs); @@ -653,19 +602,17 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, i += 6; // pub_key_prefix memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp - } - else { + } else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } _serial->writeFrame(out_frame, i); - } - else if (len > 4 && // check for status response - pending_status && - memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme - // FUTURE: tag == pending_status + } else if (len > 4 && // check for status response + pending_status && + memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme + // FUTURE: tag == pending_status ) { pending_status = 0; @@ -677,8 +624,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, memcpy(&out_frame[i], &data[4], len - 4); i += (len - 4); _serial->writeFrame(out_frame, i); - } - else if (len > 4 && tag == pending_telemetry) { // check for telemetry response + } else if (len > 4 && tag == pending_telemetry) { // check for telemetry response pending_telemetry = 0; int i = 0; @@ -692,8 +638,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, } } -void MyMesh::onRawDataRecv(mesh::Packet *packet) -{ +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); return; @@ -708,15 +653,13 @@ void MyMesh::onRawDataRecv(mesh::Packet *packet) if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); - } - else { + } else { MESH_DEBUG_PRINTLN("onRawDataRecv(), data received while app offline"); } } void MyMesh::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) -{ + const uint8_t *path_snrs, const uint8_t *path_hashes, uint8_t path_len) { int i = 0; out_frame[i++] = PUSH_CODE_TRACE_DATA; out_frame[i++] = 0; // reserved @@ -734,18 +677,15 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); - } - else { + } else { MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); } } -uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const -{ +uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } -uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const -{ +uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const { return SEND_TIMEOUT_BASE_MILLIS + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); @@ -755,8 +695,7 @@ void MyMesh::onSendTimeout() {} MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), - _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) -{ + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; offline_queue_len = 0; app_target_ver = 0; @@ -778,8 +717,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe //_prefs.rx_delay_base = 10.0f; enable once new algo fixed } -void MyMesh::loadPrefsInt(const char *filename) -{ +void MyMesh::loadPrefsInt(const char *filename) { #if defined(RP2040_PLATFORM) File file = _fs->open(filename, "r"); #else @@ -820,8 +758,7 @@ void MyMesh::loadPrefsInt(const char *filename) } } -void MyMesh::begin(FILESYSTEM &fs, bool has_display) -{ +void MyMesh::begin(FILESYSTEM &fs, bool has_display) { _fs = &fs; BaseChatMesh::begin(); @@ -850,8 +787,7 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) // load persisted prefs if (_fs->exists("/new_prefs")) { loadPrefsInt("/new_prefs"); // new filename - } - else if (_fs->exists("/node_prefs")) { + } else if (_fs->exists("/node_prefs")) { loadPrefsInt("/node_prefs"); savePrefs(); // save to new filename _fs->remove("/node_prefs"); // remove old @@ -863,15 +799,13 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) if (has_display) { StdRNG rng; _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session - } - else { + } else { _active_ble_pin = BLE_PIN_CODE; // otherwise static pin } #else _active_ble_pin = BLE_PIN_CODE; // otherwise static pin #endif - } - else { + } else { _active_ble_pin = _prefs.ble_pin; } #else @@ -889,27 +823,22 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) radio_set_tx_power(_prefs.tx_power_dbm); } -const char *MyMesh::getNodeName() -{ +const char *MyMesh::getNodeName() { return _prefs.node_name; } -NodePrefs *MyMesh::getNodePrefs() -{ +NodePrefs *MyMesh::getNodePrefs() { return &_prefs; } -uint32_t MyMesh::getBLEPin() -{ +uint32_t MyMesh::getBLEPin() { return _active_ble_pin; } -void MyMesh::startInterface(BaseSerialInterface &serial) -{ +void MyMesh::startInterface(BaseSerialInterface &serial) { _serial = &serial; serial.enable(); } -void MyMesh::savePrefs() -{ +void MyMesh::savePrefs() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/new_prefs"); File file = _fs->open("/new_prefs", FILE_O_WRITE); @@ -945,8 +874,7 @@ void MyMesh::savePrefs() } } -void MyMesh::handleCmdFrame(size_t len) -{ +void MyMesh::handleCmdFrame(size_t len) { if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection app_target_ver = cmd_frame[1]; // which version of protocol does app understand @@ -965,9 +893,8 @@ void MyMesh::handleCmdFrame(size_t len) StrHelper::strzcpy((char *)&out_frame[i], FIRMWARE_VERSION, 20); i += 20; _serial->writeFrame(out_frame, i); - } - else if (cmd_frame[0] == CMD_APP_START && - len >= 8) { // sent when app establishes connection, respond with node ID + } else if (cmd_frame[0] == CMD_APP_START && + len >= 8) { // sent when app establishes connection, respond with node ID // cmd_frame[1..7] reserved future char *app_name = (char *)&cmd_frame[8]; cmd_frame[len] = 0; // make app_name null terminated @@ -1008,8 +935,7 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[i], _prefs.node_name, tlen); i += tlen; _serial->writeFrame(out_frame, i); - } - else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { + } else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { int i = 1; uint8_t txt_type = cmd_frame[i++]; uint8_t attempt = cmd_frame[i++]; @@ -1029,15 +955,13 @@ void MyMesh::handleCmdFrame(size_t len) if (txt_type == TXT_TYPE_CLI_DATA) { result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); expected_ack = 0; // no Ack expected - } - else { + } else { result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); } // TODO: add expected ACK to table if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { if (expected_ack) { expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table expected_ack_table[next_ack_idx].ack = expected_ack; @@ -1050,14 +974,12 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* } - } - else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg + } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1068,27 +990,22 @@ void MyMesh::handleCmdFrame(size_t len) if (txt_type != TXT_TYPE_PLAIN) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } - else { + } else { ChannelDetails channel; bool success = getChannel(channel_idx, channel); if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } - } - else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list + } else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list if (_iter_started) { writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy - } - else { + } else { if (len >= 5) { // has optional 'since' param memcpy(&_iter_filter_since, &cmd_frame[1], 4); - } - else { + } else { _iter_filter_since = 0; } @@ -1103,17 +1020,14 @@ void MyMesh::handleCmdFrame(size_t len) _iter_started = true; _most_recent_lastmod = 0; } - } - else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { + } else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { int nlen = len - 1; - if (nlen > sizeof(_prefs.node_name) - 1) - nlen = sizeof(_prefs.node_name) - 1; // max len + if (nlen > sizeof(_prefs.node_name) - 1) nlen = sizeof(_prefs.node_name) - 1; // max len memcpy(_prefs.node_name, &cmd_frame[1], nlen); _prefs.node_name[nlen] = 0; // null terminator savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { + } else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { int32_t lat, lon, alt = 0; memcpy(&lat, &cmd_frame[1], 4); memcpy(&lon, &cmd_frame[5], 4); @@ -1125,46 +1039,38 @@ void MyMesh::handleCmdFrame(size_t len) sensors.node_lon = ((double)lon) / 1000000.0; savePrefs(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate } - } - else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { + } else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { uint8_t reply[5]; reply[0] = RESP_CODE_CURR_TIME; uint32_t now = getRTCClock()->getCurrentTime(); memcpy(&reply[1], &now, 4); _serial->writeFrame(reply, 5); - } - else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { + } else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { uint32_t secs; memcpy(&secs, &cmd_frame[1], 4); uint32_t curr = getRTCClock()->getCurrentTime(); if (secs >= curr) { getRTCClock()->setCurrentTime(secs); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { + } else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); if (pkt) { if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop) sendFlood(pkt); - } - else { + } else { sendZeroHop(pkt); } writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { + } else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1172,12 +1078,10 @@ void MyMesh::handleCmdFrame(size_t len) // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact } - } - else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { + } else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1185,8 +1089,7 @@ void MyMesh::handleCmdFrame(size_t len) // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { ContactInfo contact; updateContactFromFrame(contact, cmd_frame, len); contact.lastmod = getRTCClock()->getCurrentTime(); @@ -1194,49 +1097,40 @@ void MyMesh::handleCmdFrame(size_t len) if (addContact(contact)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - } - else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { + } else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient && removeContact(*recipient)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove } - } - else if (cmd_frame[0] == CMD_SHARE_CONTACT) { + } else if (cmd_frame[0] == CMD_SHARE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { if (shareContactZeroHop(*recipient)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { + } else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (contact) { writeContactRespFrame(RESP_CODE_CONTACT, *contact); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } - } - else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { + } else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { if (len < 1 + PUB_KEY_SIZE) { // export SELF auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); @@ -1247,46 +1141,38 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t out_len = pkt->writeTo(&out_frame[1]); releasePacket(pkt); // undo the obtainNewPacket() _serial->writeFrame(out_frame, out_len + 1); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); // Error } - } - else { + } else { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); uint8_t out_len; if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) { out_frame[0] = RESP_CODE_EXPORT_CONTACT; _serial->writeFrame(out_frame, out_len + 1); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } - } - else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { + } else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { if (importContact(&cmd_frame[1], len - 1)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { + } else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); #ifdef DISPLAY_CLASS ui_task.msgRead(offline_queue_len); #endif - } - else { + } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); } - } - else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { int i = 1; uint32_t freq; memcpy(&freq, &cmd_frame[i], 4); @@ -1310,25 +1196,21 @@ void MyMesh::handleCmdFrame(size_t len) (uint32_t)cr); writeOKFrame(); - } - else { + } else { MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { + } else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { if (cmd_frame[1] > MAX_LORA_TX_POWER) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - else { + } else { _prefs.tx_power_dbm = cmd_frame[1]; savePrefs(); radio_set_tx_power(_prefs.tx_power_dbm); writeOKFrame(); } - } - else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { int i = 1; uint32_t rx, af; memcpy(&rx, &cmd_frame[i], 4); @@ -1339,8 +1221,7 @@ void MyMesh::handleCmdFrame(size_t len) _prefs.airtime_factor = ((float)af) / 1000.0f; savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { _prefs.manual_add_contacts = cmd_frame[1]; if (len >= 3) { _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ @@ -1349,21 +1230,18 @@ void MyMesh::handleCmdFrame(size_t len) } savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { + } else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? saveContacts(); } board.reboot(); - } - else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { + } else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { uint8_t reply[3]; reply[0] = RESP_CODE_BATTERY_VOLTAGE; uint16_t battery_millivolts = board.getBattMilliVolts(); memcpy(&reply[1], &battery_millivolts, 2); _serial->writeFrame(reply, 3); - } - else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { + } else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { #if ENABLE_PRIVATE_KEY_EXPORT uint8_t reply[65]; reply[0] = RESP_CODE_PRIVATE_KEY; @@ -1372,23 +1250,20 @@ void MyMesh::handleCmdFrame(size_t len) #else writeDisabledFrame(); #endif - } - else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { + } else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { #if ENABLE_PRIVATE_KEY_IMPORT mesh::LocalIdentity identity; identity.readFrom(&cmd_frame[1], 64); if (saveMainIdentity(identity)) { self_id = identity; writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_FILE_IO_ERROR); } #else writeDisabledFrame(); #endif - } - else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { + } else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { int i = 1; int8_t path_len = cmd_frame[i++]; if (path_len >= 0 && i + path_len + 4 <= len) { // minimum 4 byte payload @@ -1398,16 +1273,13 @@ void MyMesh::handleCmdFrame(size_t len) if (pkt) { sendDirect(pkt, path, path_len); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else { + } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) } - } - else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); char *password = (char *)&cmd_frame[1 + PUB_KEY_SIZE]; @@ -1417,8 +1289,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendLogin(*recipient, password, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_telemetry = pending_status = 0; memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1427,12 +1298,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1440,8 +1309,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_telemetry = pending_login = 0; // FUTURE: pending_status = tag; // match this in onContactResponse() memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme @@ -1451,12 +1319,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[4]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1464,8 +1330,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_status = pending_login = 0; pending_telemetry = tag; // match this in onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1474,26 +1339,21 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; if (hasConnectionTo(pub_key)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; stopConnection(pub_key); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { + } else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; if (getChannel(channel_idx, channel)) { @@ -1505,15 +1365,12 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported _serial->writeFrame(out_frame, i); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) - } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32); @@ -1522,12 +1379,10 @@ void MyMesh::handleCmdFrame(size_t len) if (setChannel(channel_idx, channel)) { saveChannels(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } - } - else if (cmd_frame[0] == CMD_SIGN_START) { + } else if (cmd_frame[0] == CMD_SIGN_START) { out_frame[0] = RESP_CODE_SIGN_START; out_frame[1] = 0; // reserved uint32_t len = MAX_SIGN_DATA_LEN; @@ -1539,18 +1394,15 @@ void MyMesh::handleCmdFrame(size_t len) } sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); sign_data_len = 0; - } - else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { + } else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long - } - else { + } else { memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); sign_data_len += (len - 1); writeOKFrame(); } - } - else if (cmd_frame[0] == CMD_SIGN_FINISH) { + } else if (cmd_frame[0] == CMD_SIGN_FINISH) { if (sign_data) { self_id.sign(&out_frame[1], sign_data, sign_data_len); @@ -1559,12 +1411,10 @@ void MyMesh::handleCmdFrame(size_t len) out_frame[0] = RESP_CODE_SIGNATURE; _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); - } - else { + } else { writeErrFrame(ERR_CODE_BAD_STATE); } - } - else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { uint32_t tag, auth; memcpy(&tag, &cmd_frame[1], 4); memcpy(&auth, &cmd_frame[5], 4); @@ -1581,12 +1431,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[2], &tag, 4); memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { + } else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { // get pin from command frame uint32_t pin; @@ -1597,12 +1445,10 @@ void MyMesh::handleCmdFrame(size_t len) _prefs.ble_pin = pin; savePrefs(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { + } else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { out_frame[0] = RESP_CODE_CUSTOM_VARS; char *dp = (char *)&out_frame[1]; for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) { @@ -1616,8 +1462,7 @@ void MyMesh::handleCmdFrame(size_t len) dp = strchr(dp, 0); } _serial->writeFrame(out_frame, dp - (char *)out_frame); - } - else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { + } else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { cmd_frame[len] = 0; char *sp = (char *)&cmd_frame[1]; char *np = strchr(sp, ':'); // look for separator char @@ -1626,31 +1471,26 @@ void MyMesh::handleCmdFrame(size_t len) bool success = sensors.setSettingValue(sp, np); if (success) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else { + } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); } } -void MyMesh::loop() -{ +void MyMesh::loop() { BaseChatMesh::loop(); size_t len = _serial->checkRecvFrame(cmd_frame); if (len > 0) { handleCmdFrame(len); - } - else if (_iter_started // check if our ContactsIterator is 'running' - && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! + } else if (_iter_started // check if our ContactsIterator is 'running' + && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! ) { ContactInfo contact; if (_iter.hasNext(this, contact)) { @@ -1660,16 +1500,14 @@ void MyMesh::loop() _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame } } - } - else { // EOF + } else { // EOF out_frame[0] = RESP_CODE_END_OF_CONTACTS; memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' _serial->writeFrame(out_frame, 5); _iter_started = false; } - } - else if (!_serial->isWriteBusy()) { + } else if (!_serial->isWriteBusy()) { checkConnections(); } @@ -1685,15 +1523,13 @@ void MyMesh::loop() #endif } -bool MyMesh::advert() -{ +bool MyMesh::advert() { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); if (pkt) { sendZeroHop(pkt); writeOKFrame(); return true; - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); return false; } diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index e3dd2ec7..7a2b0d39 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,7 +18,8 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -WIOE5SensorManager sensors; +BME280I2C bme; +WIOE5SensorManager sensors(bme); #ifndef LORA_CR #define LORA_CR 5 @@ -72,9 +73,9 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); - BME280::PresUnit presUnit(BME280::PresUnit_hPa); + BME280::PresUnit presUnit(BME280::PresUnit_bar); - bme.read(pres, temp, hum, tempUnit, presUnit); + _bme->read(pres, temp, hum, tempUnit, presUnit); telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); @@ -84,7 +85,7 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } bool WIOE5SensorManager::begin() { - has_bme = bme.begin(); + has_bme = _bme->begin(); return has_bme; } \ No newline at end of file From 4e2786c516a28c2feb3b25b0750a78dc2c26afc0 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:28:29 -0700 Subject: [PATCH 071/103] Re-applying 73a7a96, formatting, MyMesh reformat --- .clang-format | 4 +- examples/companion_radio/MyMesh.cpp | 504 ++++++++++------------------ 2 files changed, 172 insertions(+), 336 deletions(-) diff --git a/.clang-format b/.clang-format index 80c7a10a..66ecd43a 100644 --- a/.clang-format +++ b/.clang-format @@ -14,7 +14,7 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None @@ -35,7 +35,7 @@ BraceWrapping: BeforeElse: true IndentBraces: false BreakBeforeBinaryOperators: None -BreakBeforeBraces: Stroustrup +BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 110 diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1988c367..e53022f6 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -72,8 +72,7 @@ #include "UITask.h" #endif -void MyMesh::loadMainIdentity() -{ +void MyMesh::loadMainIdentity() { if (!_identity_store->load("_main", self_id)) { self_id = radio_new_identity(); // create new random identity int count = 0; @@ -85,13 +84,11 @@ void MyMesh::loadMainIdentity() } } -bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) -{ +bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) { return _identity_store->save("_main", identity); } -void MyMesh::loadContacts() -{ +void MyMesh::loadContacts() { if (_fs->exists("/contacts3")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/contacts3", "r"); @@ -118,20 +115,17 @@ void MyMesh::loadContacts() success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); - if (!success) - break; // EOF + if (!success) break; // EOF c.id = mesh::Identity(pub_key); - if (!addContact(c)) - full = true; + if (!addContact(c)) full = true; } file.close(); } } } -void MyMesh::saveContacts() -{ +void MyMesh::saveContacts() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/contacts3"); File file = _fs->open("/contacts3", FILE_O_WRITE); @@ -159,15 +153,13 @@ void MyMesh::saveContacts() success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); - if (!success) - break; // write failed + if (!success) break; // write failed } file.close(); } } -void MyMesh::loadChannels() -{ +void MyMesh::loadChannels() { if (_fs->exists("/channels2")) { #if defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "r"); @@ -185,13 +177,11 @@ void MyMesh::loadChannels() success = success && (file.read((uint8_t *)ch.name, 32) == 32); success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); - if (!success) - break; // EOF + if (!success) break; // EOF if (setChannel(channel_idx, ch)) { channel_idx++; - } - else { + } else { full = true; } } @@ -200,8 +190,7 @@ void MyMesh::loadChannels() } } -void MyMesh::saveChannels() -{ +void MyMesh::saveChannels() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/channels2"); File file = _fs->open("/channels2", FILE_O_WRITE); @@ -221,21 +210,18 @@ void MyMesh::saveChannels() success = success && (file.write((uint8_t *)ch.name, 32) == 32); success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); - if (!success) - break; // write failed + if (!success) break; // write failed channel_idx++; } file.close(); } } -int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) -{ +int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { char path[64]; char fname[18]; - if (key_len > 8) - key_len = 8; // just use first 8 bytes (prefix) + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); @@ -254,13 +240,11 @@ int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) return 0; // not found } -bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) -{ +bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { char path[64]; char fname[18]; - if (key_len > 8) - key_len = 8; // just use first 8 bytes (prefix) + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); @@ -275,37 +259,32 @@ bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_bu if (f) { int n = f.write(src_buf, len); f.close(); - if (n == len) - return true; // success! + if (n == len) return true; // success! _fs->remove(path); // blob was only partially written! } return false; // error } -void MyMesh::writeOKFrame() -{ +void MyMesh::writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; _serial->writeFrame(buf, 1); } -void MyMesh::writeErrFrame(uint8_t err_code) -{ +void MyMesh::writeErrFrame(uint8_t err_code) { uint8_t buf[2]; buf[0] = RESP_CODE_ERR; buf[1] = err_code; _serial->writeFrame(buf, 2); } -void MyMesh::writeDisabledFrame() -{ +void MyMesh::writeDisabledFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_DISABLED; _serial->writeFrame(buf, 1); } -void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) -{ +void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) { int i = 0; out_frame[i++] = code; memcpy(&out_frame[i], contact.id.pub_key, PUB_KEY_SIZE); @@ -328,8 +307,7 @@ void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) _serial->writeFrame(out_frame, i); } -void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) -{ +void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) { int i = 0; uint8_t code = frame[i++]; // eg. CMD_ADD_UPDATE_CONTACT memcpy(contact.id.pub_key, &frame[i], PUB_KEY_SIZE); @@ -351,19 +329,16 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, } } -void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) -{ +void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { if (offline_queue_len >= OFFLINE_QUEUE_SIZE) { MESH_DEBUG_PRINTLN("ERROR: offline_queue is full!"); - } - else { + } else { offline_queue[offline_queue_len].len = len; memcpy(offline_queue[offline_queue_len].buf, frame, len); offline_queue_len++; } } -int MyMesh::getFromOfflineQueue(uint8_t frame[]) -{ +int MyMesh::getFromOfflineQueue(uint8_t frame[]) { if (offline_queue_len > 0) { // check offline queue size_t len = offline_queue[0].len; // take from top of queue memcpy(frame, offline_queue[0].buf, len); @@ -377,25 +352,20 @@ int MyMesh::getFromOfflineQueue(uint8_t frame[]) return 0; // queue is empty } -float MyMesh::getAirtimeBudgetFactor() const -{ +float MyMesh::getAirtimeBudgetFactor() const { return _prefs.airtime_factor; } -int MyMesh::getInterferenceThreshold() const -{ +int MyMesh::getInterferenceThreshold() const { return 0; // disabled for now, until currentRSSI() problem is resolved } -int MyMesh::calcRxDelay(float score, uint32_t air_time) const -{ - if (_prefs.rx_delay_base <= 0.0f) - return 0; +int MyMesh::calcRxDelay(float score, uint32_t air_time) const { + if (_prefs.rx_delay_base <= 0.0f) return 0; return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } -void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) -{ +void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; out_frame[i++] = PUSH_CODE_LOG_RX_DATA; @@ -408,24 +378,20 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) } } -bool MyMesh::isAutoAddEnabled() const -{ +bool MyMesh::isAutoAddEnabled() const { return (_prefs.manual_add_contacts & 1) == 0; } -void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) -{ +void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) { if (_serial->isConnected()) { if (!isAutoAddEnabled() && is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); - } - else { + } else { out_frame[0] = PUSH_CODE_ADVERT; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::newContactMessage); #endif @@ -434,8 +400,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } -void MyMesh::onContactPathUpdated(const ContactInfo &contact) -{ +void MyMesh::onContactPathUpdated(const ContactInfo &contact) { out_frame[0] = PUSH_CODE_PATH_UPDATED; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected @@ -443,8 +408,7 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } -bool MyMesh::processAck(const uint8_t *data) -{ +bool MyMesh::processAck(const uint8_t *data) { // see if matches any in a table for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) { if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient @@ -463,16 +427,14 @@ bool MyMesh::processAck(const uint8_t *data) } void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, - uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) -{ + uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) { int i = 0; if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 - } - else { + } else { out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; } memcpy(&out_frame[i], from.id.pub_key, 6); @@ -497,8 +459,7 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::contactMessage); #endif @@ -509,22 +470,19 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe } void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) -{ + const char *text) { markConnectionActive(from); // in case this is from a server, and we have a connection queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text); } void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const char *text) -{ + const char *text) { markConnectionActive(from); // in case this is from a server, and we have a connection queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text); } void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, - const uint8_t *sender_prefix, const char *text) -{ + const uint8_t *sender_prefix, const char *text) { markConnectionActive(from); // from.sync_since change needs to be persisted dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); @@ -532,16 +490,14 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin } void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, - const char *text) -{ + const char *text) { int i = 0; if (app_target_ver >= 3) { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3; out_frame[i++] = (int8_t)(pkt->getSNR() * 4); out_frame[i++] = 0; // reserved1 out_frame[i++] = 0; // reserved2 - } - else { + } else { out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } @@ -564,8 +520,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); - } - else { + } else { #ifdef DISPLAY_CLASS ui_task.soundBuzzer(UIEventType::channelMessage); #endif @@ -582,30 +537,26 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe } uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, - uint8_t len, uint8_t *reply) -{ + uint8_t len, uint8_t *reply) { if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { uint8_t permissions = 0; uint8_t cp = contact.flags >> 1; // LSB used as 'favourite' bit (so only use upper bits) if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_ALL) { permissions = TELEM_PERM_BASE; - } - else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_base == TELEM_MODE_ALLOW_FLAGS) { permissions = cp & TELEM_PERM_BASE; } if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_LOCATION; - } - else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_loc == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_LOCATION; } if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { permissions |= TELEM_PERM_ENVIRONMENT; - } - else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { + } else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { permissions |= cp & TELEM_PERM_ENVIRONMENT; } @@ -626,8 +577,7 @@ uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_tim return 0; // unknown } -void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) -{ +void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) { uint32_t tag; memcpy(&tag, data, 4); @@ -640,9 +590,8 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false memcpy(&out_frame[i], contact.id.pub_key, 6); - i += 6; // pub_key_prefix - } - else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response + i += 6; // pub_key_prefix + } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; if (keep_alive_secs > 0) { startConnection(contact, keep_alive_secs); @@ -653,19 +602,17 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, i += 6; // pub_key_prefix memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp - } - else { + } else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } _serial->writeFrame(out_frame, i); - } - else if (len > 4 && // check for status response - pending_status && - memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme - // FUTURE: tag == pending_status + } else if (len > 4 && // check for status response + pending_status && + memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme + // FUTURE: tag == pending_status ) { pending_status = 0; @@ -677,8 +624,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, memcpy(&out_frame[i], &data[4], len - 4); i += (len - 4); _serial->writeFrame(out_frame, i); - } - else if (len > 4 && tag == pending_telemetry) { // check for telemetry response + } else if (len > 4 && tag == pending_telemetry) { // check for telemetry response pending_telemetry = 0; int i = 0; @@ -692,8 +638,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, } } -void MyMesh::onRawDataRecv(mesh::Packet *packet) -{ +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); return; @@ -708,15 +653,13 @@ void MyMesh::onRawDataRecv(mesh::Packet *packet) if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); - } - else { + } else { MESH_DEBUG_PRINTLN("onRawDataRecv(), data received while app offline"); } } void MyMesh::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) -{ + const uint8_t *path_snrs, const uint8_t *path_hashes, uint8_t path_len) { int i = 0; out_frame[i++] = PUSH_CODE_TRACE_DATA; out_frame[i++] = 0; // reserved @@ -734,18 +677,15 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, if (_serial->isConnected()) { _serial->writeFrame(out_frame, i); - } - else { + } else { MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline"); } } -uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const -{ +uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } -uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const -{ +uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const { return SEND_TIMEOUT_BASE_MILLIS + ((pkt_airtime_millis * DIRECT_SEND_PERHOP_FACTOR + DIRECT_SEND_PERHOP_EXTRA_MILLIS) * (path_len + 1)); @@ -755,8 +695,7 @@ void MyMesh::onSendTimeout() {} MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), - _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) -{ + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; offline_queue_len = 0; app_target_ver = 0; @@ -778,8 +717,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe //_prefs.rx_delay_base = 10.0f; enable once new algo fixed } -void MyMesh::loadPrefsInt(const char *filename) -{ +void MyMesh::loadPrefsInt(const char *filename) { #if defined(RP2040_PLATFORM) File file = _fs->open(filename, "r"); #else @@ -820,8 +758,7 @@ void MyMesh::loadPrefsInt(const char *filename) } } -void MyMesh::begin(FILESYSTEM &fs, bool has_display) -{ +void MyMesh::begin(FILESYSTEM &fs, bool has_display) { _fs = &fs; BaseChatMesh::begin(); @@ -850,8 +787,7 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) // load persisted prefs if (_fs->exists("/new_prefs")) { loadPrefsInt("/new_prefs"); // new filename - } - else if (_fs->exists("/node_prefs")) { + } else if (_fs->exists("/node_prefs")) { loadPrefsInt("/node_prefs"); savePrefs(); // save to new filename _fs->remove("/node_prefs"); // remove old @@ -863,15 +799,13 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) if (has_display) { StdRNG rng; _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session - } - else { + } else { _active_ble_pin = BLE_PIN_CODE; // otherwise static pin } #else _active_ble_pin = BLE_PIN_CODE; // otherwise static pin #endif - } - else { + } else { _active_ble_pin = _prefs.ble_pin; } #else @@ -889,27 +823,22 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) radio_set_tx_power(_prefs.tx_power_dbm); } -const char *MyMesh::getNodeName() -{ +const char *MyMesh::getNodeName() { return _prefs.node_name; } -NodePrefs *MyMesh::getNodePrefs() -{ +NodePrefs *MyMesh::getNodePrefs() { return &_prefs; } -uint32_t MyMesh::getBLEPin() -{ +uint32_t MyMesh::getBLEPin() { return _active_ble_pin; } -void MyMesh::startInterface(BaseSerialInterface &serial) -{ +void MyMesh::startInterface(BaseSerialInterface &serial) { _serial = &serial; serial.enable(); } -void MyMesh::savePrefs() -{ +void MyMesh::savePrefs() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove("/new_prefs"); File file = _fs->open("/new_prefs", FILE_O_WRITE); @@ -945,8 +874,7 @@ void MyMesh::savePrefs() } } -void MyMesh::handleCmdFrame(size_t len) -{ +void MyMesh::handleCmdFrame(size_t len) { if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection app_target_ver = cmd_frame[1]; // which version of protocol does app understand @@ -965,9 +893,8 @@ void MyMesh::handleCmdFrame(size_t len) StrHelper::strzcpy((char *)&out_frame[i], FIRMWARE_VERSION, 20); i += 20; _serial->writeFrame(out_frame, i); - } - else if (cmd_frame[0] == CMD_APP_START && - len >= 8) { // sent when app establishes connection, respond with node ID + } else if (cmd_frame[0] == CMD_APP_START && + len >= 8) { // sent when app establishes connection, respond with node ID // cmd_frame[1..7] reserved future char *app_name = (char *)&cmd_frame[8]; cmd_frame[len] = 0; // make app_name null terminated @@ -1008,8 +935,7 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[i], _prefs.node_name, tlen); i += tlen; _serial->writeFrame(out_frame, i); - } - else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { + } else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { int i = 1; uint8_t txt_type = cmd_frame[i++]; uint8_t attempt = cmd_frame[i++]; @@ -1029,15 +955,13 @@ void MyMesh::handleCmdFrame(size_t len) if (txt_type == TXT_TYPE_CLI_DATA) { result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); expected_ack = 0; // no Ack expected - } - else { + } else { result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); } // TODO: add expected ACK to table if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { if (expected_ack) { expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table expected_ack_table[next_ack_idx].ack = expected_ack; @@ -1050,14 +974,12 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* } - } - else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg + } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1068,27 +990,22 @@ void MyMesh::handleCmdFrame(size_t len) if (txt_type != TXT_TYPE_PLAIN) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } - else { + } else { ChannelDetails channel; bool success = getChannel(channel_idx, channel); if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } - } - else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list + } else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list if (_iter_started) { writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy - } - else { + } else { if (len >= 5) { // has optional 'since' param memcpy(&_iter_filter_since, &cmd_frame[1], 4); - } - else { + } else { _iter_filter_since = 0; } @@ -1103,17 +1020,14 @@ void MyMesh::handleCmdFrame(size_t len) _iter_started = true; _most_recent_lastmod = 0; } - } - else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { + } else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { int nlen = len - 1; - if (nlen > sizeof(_prefs.node_name) - 1) - nlen = sizeof(_prefs.node_name) - 1; // max len + if (nlen > sizeof(_prefs.node_name) - 1) nlen = sizeof(_prefs.node_name) - 1; // max len memcpy(_prefs.node_name, &cmd_frame[1], nlen); _prefs.node_name[nlen] = 0; // null terminator savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { + } else if (cmd_frame[0] == CMD_SET_ADVERT_LATLON && len >= 9) { int32_t lat, lon, alt = 0; memcpy(&lat, &cmd_frame[1], 4); memcpy(&lon, &cmd_frame[5], 4); @@ -1125,46 +1039,38 @@ void MyMesh::handleCmdFrame(size_t len) sensors.node_lon = ((double)lon) / 1000000.0; savePrefs(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate } - } - else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { + } else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { uint8_t reply[5]; reply[0] = RESP_CODE_CURR_TIME; uint32_t now = getRTCClock()->getCurrentTime(); memcpy(&reply[1], &now, 4); _serial->writeFrame(reply, 5); - } - else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { + } else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) { uint32_t secs; memcpy(&secs, &cmd_frame[1], 4); uint32_t curr = getRTCClock()->getCurrentTime(); if (secs >= curr) { getRTCClock()->setCurrentTime(secs); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { + } else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); if (pkt) { if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop) sendFlood(pkt); - } - else { + } else { sendZeroHop(pkt); } writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { + } else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1 + 32) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1172,12 +1078,10 @@ void MyMesh::handleCmdFrame(size_t len) // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact } - } - else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { + } else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1185,8 +1089,7 @@ void MyMesh::handleCmdFrame(size_t len) // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { ContactInfo contact; updateContactFromFrame(contact, cmd_frame, len); contact.lastmod = getRTCClock()->getCurrentTime(); @@ -1194,49 +1097,40 @@ void MyMesh::handleCmdFrame(size_t len) if (addContact(contact)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } } - } - else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { + } else if (cmd_frame[0] == CMD_REMOVE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient && removeContact(*recipient)) { dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove } - } - else if (cmd_frame[0] == CMD_SHARE_CONTACT) { + } else if (cmd_frame[0] == CMD_SHARE_CONTACT) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { if (shareContactZeroHop(*recipient)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { + } else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *contact = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (contact) { writeContactRespFrame(RESP_CODE_CONTACT, *contact); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } - } - else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { + } else if (cmd_frame[0] == CMD_EXPORT_CONTACT) { if (len < 1 + PUB_KEY_SIZE) { // export SELF auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); @@ -1247,46 +1141,38 @@ void MyMesh::handleCmdFrame(size_t len) uint8_t out_len = pkt->writeTo(&out_frame[1]); releasePacket(pkt); // undo the obtainNewPacket() _serial->writeFrame(out_frame, out_len + 1); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); // Error } - } - else { + } else { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); uint8_t out_len; if (recipient && (out_len = exportContact(*recipient, &out_frame[1])) > 0) { out_frame[0] = RESP_CODE_EXPORT_CONTACT; _serial->writeFrame(out_frame, out_len + 1); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // not found } } - } - else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { + } else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2 + 32 + 64) { if (importContact(&cmd_frame[1], len - 1)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { + } else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); #ifdef DISPLAY_CLASS ui_task.msgRead(offline_queue_len); #endif - } - else { + } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); } - } - else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { int i = 1; uint32_t freq; memcpy(&freq, &cmd_frame[i], 4); @@ -1310,25 +1196,21 @@ void MyMesh::handleCmdFrame(size_t len) (uint32_t)cr); writeOKFrame(); - } - else { + } else { MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { + } else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) { if (cmd_frame[1] > MAX_LORA_TX_POWER) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } - else { + } else { _prefs.tx_power_dbm = cmd_frame[1]; savePrefs(); radio_set_tx_power(_prefs.tx_power_dbm); writeOKFrame(); } - } - else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) { int i = 1; uint32_t rx, af; memcpy(&rx, &cmd_frame[i], 4); @@ -1339,8 +1221,7 @@ void MyMesh::handleCmdFrame(size_t len) _prefs.airtime_factor = ((float)af) / 1000.0f; savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { + } else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) { _prefs.manual_add_contacts = cmd_frame[1]; if (len >= 3) { _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ @@ -1349,21 +1230,18 @@ void MyMesh::handleCmdFrame(size_t len) } savePrefs(); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { + } else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) { if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed? saveContacts(); } board.reboot(); - } - else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { + } else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) { uint8_t reply[3]; reply[0] = RESP_CODE_BATTERY_VOLTAGE; uint16_t battery_millivolts = board.getBattMilliVolts(); memcpy(&reply[1], &battery_millivolts, 2); _serial->writeFrame(reply, 3); - } - else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { + } else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { #if ENABLE_PRIVATE_KEY_EXPORT uint8_t reply[65]; reply[0] = RESP_CODE_PRIVATE_KEY; @@ -1372,23 +1250,20 @@ void MyMesh::handleCmdFrame(size_t len) #else writeDisabledFrame(); #endif - } - else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { + } else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) { #if ENABLE_PRIVATE_KEY_IMPORT mesh::LocalIdentity identity; identity.readFrom(&cmd_frame[1], 64); if (saveMainIdentity(identity)) { self_id = identity; writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_FILE_IO_ERROR); } #else writeDisabledFrame(); #endif - } - else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { + } else if (cmd_frame[0] == CMD_SEND_RAW_DATA && len >= 6) { int i = 1; int8_t path_len = cmd_frame[i++]; if (path_len >= 0 && i + path_len + 4 <= len) { // minimum 4 byte payload @@ -1398,16 +1273,13 @@ void MyMesh::handleCmdFrame(size_t len) if (pkt) { sendDirect(pkt, path, path_len); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else { + } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet) } - } - else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); char *password = (char *)&cmd_frame[1 + PUB_KEY_SIZE]; @@ -1417,8 +1289,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendLogin(*recipient, password, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_telemetry = pending_status = 0; memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1427,12 +1298,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1440,8 +1309,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendRequest(*recipient, REQ_TYPE_GET_STATUS, tag, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_telemetry = pending_login = 0; // FUTURE: pending_status = tag; // match this in onContactResponse() memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme @@ -1451,12 +1319,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[4]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient) { @@ -1464,8 +1330,7 @@ void MyMesh::handleCmdFrame(size_t len) int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout); if (result == MSG_SEND_FAILED) { writeErrFrame(ERR_CODE_TABLE_FULL); - } - else { + } else { pending_status = pending_login = 0; pending_telemetry = tag; // match this in onContactResponse() out_frame[0] = RESP_CODE_SENT; @@ -1474,26 +1339,21 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); } - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } - } - else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; if (hasConnectionTo(pub_key)) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { + } else if (cmd_frame[0] == CMD_LOGOUT && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; stopConnection(pub_key); writeOKFrame(); - } - else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { + } else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; if (getChannel(channel_idx, channel)) { @@ -1505,15 +1365,12 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported _serial->writeFrame(out_frame, i); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); } - } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 32) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet) - } - else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) { uint8_t channel_idx = cmd_frame[1]; ChannelDetails channel; StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32); @@ -1522,12 +1379,10 @@ void MyMesh::handleCmdFrame(size_t len) if (setChannel(channel_idx, channel)) { saveChannels(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } - } - else if (cmd_frame[0] == CMD_SIGN_START) { + } else if (cmd_frame[0] == CMD_SIGN_START) { out_frame[0] = RESP_CODE_SIGN_START; out_frame[1] = 0; // reserved uint32_t len = MAX_SIGN_DATA_LEN; @@ -1539,18 +1394,15 @@ void MyMesh::handleCmdFrame(size_t len) } sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); sign_data_len = 0; - } - else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { + } else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long - } - else { + } else { memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); sign_data_len += (len - 1); writeOKFrame(); } - } - else if (cmd_frame[0] == CMD_SIGN_FINISH) { + } else if (cmd_frame[0] == CMD_SIGN_FINISH) { if (sign_data) { self_id.sign(&out_frame[1], sign_data, sign_data_len); @@ -1559,12 +1411,10 @@ void MyMesh::handleCmdFrame(size_t len) out_frame[0] = RESP_CODE_SIGNATURE; _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); - } - else { + } else { writeErrFrame(ERR_CODE_BAD_STATE); } - } - else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { + } else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) { uint32_t tag, auth; memcpy(&tag, &cmd_frame[1], 4); memcpy(&auth, &cmd_frame[5], 4); @@ -1581,12 +1431,10 @@ void MyMesh::handleCmdFrame(size_t len) memcpy(&out_frame[2], &tag, 4); memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); } - } - else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { + } else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) { // get pin from command frame uint32_t pin; @@ -1597,12 +1445,10 @@ void MyMesh::handleCmdFrame(size_t len) _prefs.ble_pin = pin; savePrefs(); writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { + } else if (cmd_frame[0] == CMD_GET_CUSTOM_VARS) { out_frame[0] = RESP_CODE_CUSTOM_VARS; char *dp = (char *)&out_frame[1]; for (int i = 0; i < sensors.getNumSettings() && dp - (char *)&out_frame[1] < 140; i++) { @@ -1616,8 +1462,7 @@ void MyMesh::handleCmdFrame(size_t len) dp = strchr(dp, 0); } _serial->writeFrame(out_frame, dp - (char *)out_frame); - } - else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { + } else if (cmd_frame[0] == CMD_SET_CUSTOM_VAR && len >= 4) { cmd_frame[len] = 0; char *sp = (char *)&cmd_frame[1]; char *np = strchr(sp, ':'); // look for separator char @@ -1626,31 +1471,26 @@ void MyMesh::handleCmdFrame(size_t len) bool success = sensors.setSettingValue(sp, np); if (success) { writeOKFrame(); - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else { + } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } - else { + } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); } } -void MyMesh::loop() -{ +void MyMesh::loop() { BaseChatMesh::loop(); size_t len = _serial->checkRecvFrame(cmd_frame); if (len > 0) { handleCmdFrame(len); - } - else if (_iter_started // check if our ContactsIterator is 'running' - && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! + } else if (_iter_started // check if our ContactsIterator is 'running' + && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! ) { ContactInfo contact; if (_iter.hasNext(this, contact)) { @@ -1660,16 +1500,14 @@ void MyMesh::loop() _most_recent_lastmod = contact.lastmod; // save for the RESP_CODE_END_OF_CONTACTS frame } } - } - else { // EOF + } else { // EOF out_frame[0] = RESP_CODE_END_OF_CONTACTS; memcpy(&out_frame[1], &_most_recent_lastmod, 4); // include the most recent lastmod, so app can update their 'since' _serial->writeFrame(out_frame, 5); _iter_started = false; } - } - else if (!_serial->isWriteBusy()) { + } else if (!_serial->isWriteBusy()) { checkConnections(); } @@ -1685,15 +1523,13 @@ void MyMesh::loop() #endif } -bool MyMesh::advert() -{ +bool MyMesh::advert() { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); if (pkt) { sendZeroHop(pkt); writeOKFrame(); return true; - } - else { + } else { writeErrFrame(ERR_CODE_TABLE_FULL); return false; } From 9fe218e0d8e67322c1377b380cfee95fa7ec0a3c Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:41:04 -0700 Subject: [PATCH 072/103] Reverting format changes to NodePrefs But changing to pragma once. --- examples/companion_radio/NodePrefs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 44d7ecbf..4fd0fd3b 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -1,11 +1,11 @@ #pragma once #include // For uint8_t, uint32_t -#define TELEM_MODE_DENY 0 -#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags -#define TELEM_MODE_ALLOW_ALL 2 +#define TELEM_MODE_DENY 0 +#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags +#define TELEM_MODE_ALLOW_ALL 2 -struct NodePrefs { // persisted to file +struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; float freq; From 33d5f85556b1bba24f48661e20301251bd406201 Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:42:40 -0700 Subject: [PATCH 073/103] Re-merging 92c2963 --- variants/wio-e5-mini/target.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/variants/wio-e5-mini/target.cpp b/variants/wio-e5-mini/target.cpp index 7a2b0d39..e3dd2ec7 100644 --- a/variants/wio-e5-mini/target.cpp +++ b/variants/wio-e5-mini/target.cpp @@ -18,8 +18,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { }; VolatileRTCClock rtc_clock; -BME280I2C bme; -WIOE5SensorManager sensors(bme); +WIOE5SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 @@ -73,9 +72,9 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); - BME280::PresUnit presUnit(BME280::PresUnit_bar); + BME280::PresUnit presUnit(BME280::PresUnit_hPa); - _bme->read(pres, temp, hum, tempUnit, presUnit); + bme.read(pres, temp, hum, tempUnit, presUnit); telemetry.addTemperature(TELEM_CHANNEL_SELF, temp); telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, hum); @@ -85,7 +84,7 @@ bool WIOE5SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } bool WIOE5SensorManager::begin() { - has_bme = _bme->begin(); + has_bme = bme.begin(); return has_bme; } \ No newline at end of file From 884d8f1a987af3fcd0bb8368cd271e5004b3682b Mon Sep 17 00:00:00 2001 From: hank Date: Sun, 1 Jun 2025 20:45:18 -0700 Subject: [PATCH 074/103] Reverting UITask.h changes --- examples/companion_radio/UITask.h | 43 +++++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index f52c0961..acf5237e 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,43 +1,52 @@ #pragma once + #include #include #include #ifdef PIN_BUZZER -#include + #include #endif -#include "Button.h" #include "NodePrefs.h" +#include "Button.h" -enum class UIEventType { none, contactMessage, channelMessage, roomMessage, newContactMessage, ack }; + enum class UIEventType +{ + none, + contactMessage, + channelMessage, + roomMessage, + newContactMessage, + ack +}; class UITask { - DisplayDriver *_display; - mesh::MainBoard *_board; + DisplayDriver* _display; + mesh::MainBoard* _board; #ifdef PIN_BUZZER genericBuzzer buzzer; #endif unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; - NodePrefs *_node_prefs; + NodePrefs* _node_prefs; char _version_info[32]; char _origin[62]; char _msg[80]; int _msgcount; bool _need_refresh = true; - bool _displayWasOn = false; // Track display state before button press + bool _displayWasOn = false; // Track display state before button press // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) - Button *_userButton = nullptr; + Button* _userButton = nullptr; #endif void renderCurrScreen(); void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); - + // Button action handlers void handleButtonAnyPress(); void handleButtonShortPress(); @@ -45,21 +54,21 @@ class UITask { void handleButtonTriplePress(); void handleButtonLongPress(); + public: - UITask(mesh::MainBoard *board) : _board(board), _display(NULL) - { - _next_refresh = 0; - _connected = false; + + UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { + _next_refresh = 0; + _connected = false; } - void begin(DisplayDriver *display, NodePrefs *node_prefs, const char *build_date, - const char *firmware_version, uint32_t pin_code); + void begin(DisplayDriver* display, NodePrefs* node_prefs, const char* build_date, const char* firmware_version, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } bool hasDisplay() const { return _display != NULL; } void clearMsgPreview(); void msgRead(int msgcount); - void newMsg(uint8_t path_len, const char *from_name, const char *text, int msgcount); + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void soundBuzzer(UIEventType bet = UIEventType::none); void shutdown(bool restart = false); void loop(); -}; \ No newline at end of file +}; From 5729d66a9ead2ff7a7620882a82607180fee4824 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 15:25:55 +1000 Subject: [PATCH 075/103] * companion: some further refactors after the MyMesh refactor --- examples/companion_radio/MyMesh.cpp | 33 +++++++++++++++++++++++++++-- examples/companion_radio/MyMesh.h | 33 ----------------------------- examples/companion_radio/UITask.cpp | 18 +++++++--------- examples/companion_radio/UITask.h | 3 +-- examples/companion_radio/main.cpp | 2 +- 5 files changed, 41 insertions(+), 48 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index e53022f6..b0905bb6 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -68,6 +68,37 @@ #define RESP_CODE_SIGNATURE 20 #define RESP_CODE_CUSTOM_VARS 21 +#define SEND_TIMEOUT_BASE_MILLIS 500 +#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f +#define DIRECT_SEND_PERHOP_FACTOR 6.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 +#define LAZY_CONTACTS_WRITE_DELAY 5000 + +#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" + +// these are _pushed_ to client app at any time +#define PUSH_CODE_ADVERT 0x80 +#define PUSH_CODE_PATH_UPDATED 0x81 +#define PUSH_CODE_SEND_CONFIRMED 0x82 +#define PUSH_CODE_MSG_WAITING 0x83 +#define PUSH_CODE_RAW_DATA 0x84 +#define PUSH_CODE_LOGIN_SUCCESS 0x85 +#define PUSH_CODE_LOGIN_FAIL 0x86 +#define PUSH_CODE_STATUS_RESPONSE 0x87 +#define PUSH_CODE_LOG_RX_DATA 0x88 +#define PUSH_CODE_TRACE_DATA 0x89 +#define PUSH_CODE_NEW_ADVERT 0x8A +#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B + +#define ERR_CODE_UNSUPPORTED_CMD 1 +#define ERR_CODE_NOT_FOUND 2 +#define ERR_CODE_TABLE_FULL 3 +#define ERR_CODE_BAD_STATE 4 +#define ERR_CODE_FILE_IO_ERROR 5 +#define ERR_CODE_ILLEGAL_ARG 6 + +#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K + #ifdef DISPLAY_CLASS #include "UITask.h" #endif @@ -1527,10 +1558,8 @@ bool MyMesh::advert() { auto pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); if (pkt) { sendZeroHop(pkt); - writeOKFrame(); return true; } else { - writeErrFrame(ERR_CODE_TABLE_FULL); return false; } } \ No newline at end of file diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 574b9878..9d7da0df 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -70,43 +70,12 @@ #include -#define SEND_TIMEOUT_BASE_MILLIS 500 -#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 6.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 -#define LAZY_CONTACTS_WRITE_DELAY 5000 - -#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" - -// these are _pushed_ to client app at any time -#define PUSH_CODE_ADVERT 0x80 -#define PUSH_CODE_PATH_UPDATED 0x81 -#define PUSH_CODE_SEND_CONFIRMED 0x82 -#define PUSH_CODE_MSG_WAITING 0x83 -#define PUSH_CODE_RAW_DATA 0x84 -#define PUSH_CODE_LOGIN_SUCCESS 0x85 -#define PUSH_CODE_LOGIN_FAIL 0x86 -#define PUSH_CODE_STATUS_RESPONSE 0x87 -#define PUSH_CODE_LOG_RX_DATA 0x88 -#define PUSH_CODE_TRACE_DATA 0x89 -#define PUSH_CODE_NEW_ADVERT 0x8A -#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B - -#define ERR_CODE_UNSUPPORTED_CMD 1 -#define ERR_CODE_NOT_FOUND 2 -#define ERR_CODE_TABLE_FULL 3 -#define ERR_CODE_BAD_STATE 4 -#define ERR_CODE_FILE_IO_ERROR 5 -#define ERR_CODE_ILLEGAL_ARG 6 - /* -------------------------------------------------------------------------------------- */ #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS #define REQ_TYPE_KEEP_ALIVE 0x02 #define REQ_TYPE_GET_TELEMETRY_DATA 0x03 -#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K - class MyMesh : public BaseChatMesh { public: MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables); @@ -213,8 +182,6 @@ private: int next_ack_idx; }; -extern StdRNG fast_rng; -extern SimpleMeshTables tables; extern MyMesh the_mesh; #ifdef DISPLAY_CLASS extern UITask ui_task; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index eabfd8f7..00263e5e 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -34,26 +34,25 @@ static const uint8_t meshcore_logo [] PROGMEM = { 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8, }; -void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs, const char* build_date, const char* firmware_version, uint32_t pin_code) { +void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs) { _display = display; _auto_off = millis() + AUTO_OFF_MILLIS; clearMsgPreview(); _node_prefs = node_prefs; - _pin_code = pin_code; if (_display != NULL) { _display->turnOn(); } // strip off dash and commit hash by changing dash to null terminator // e.g: v1.2.3-abcdef -> v1.2.3 - char *version = strdup(firmware_version); + char *version = strdup(FIRMWARE_VERSION); char *dash = strchr(version, '-'); - if(dash){ + if (dash) { *dash = 0; } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + sprintf(_version_info, "%s (%s)", version, FIRMWARE_BUILD_DATE); #ifdef PIN_BUZZER buzzer.begin(); @@ -216,11 +215,11 @@ void UITask::renderCurrScreen() { _display->print(tmp); // BT pin - if (!_connected && _pin_code != 0) { + if (!_connected && the_mesh.getBLEPin() != 0) { _display->setColor(DisplayDriver::RED); _display->setTextSize(2); _display->setCursor(0, 43); - sprintf(tmp, "Pin:%d", _pin_code); + sprintf(tmp, "Pin:%d", the_mesh.getBLEPin()); _display->print(tmp); _display->setColor(DisplayDriver::GREEN); } else { @@ -343,10 +342,9 @@ void UITask::handleButtonShortPress() { void UITask::handleButtonDoublePress() { MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); // ADVERT - if(the_mesh.advert()) { + if (the_mesh.advert()) { MESH_DEBUG_PRINTLN("Advert sent!"); - } - else { + } else { MESH_DEBUG_PRINTLN("Advert failed!"); } } diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index acf5237e..6914e9ec 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -29,7 +29,6 @@ class UITask { #endif unsigned long _next_refresh, _auto_off; bool _connected; - uint32_t _pin_code; NodePrefs* _node_prefs; char _version_info[32]; char _origin[62]; @@ -61,7 +60,7 @@ public: _next_refresh = 0; _connected = false; } - void begin(DisplayDriver* display, NodePrefs* node_prefs, const char* build_date, const char* firmware_version, uint32_t pin_code); + void begin(DisplayDriver* display, NodePrefs* node_prefs); void setHasConnection(bool connected) { _connected = connected; } bool hasDisplay() const { return _display != NULL; } diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 8c822a5c..a276a2b5 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -181,7 +181,7 @@ void setup() { sensors.begin(); #ifdef DISPLAY_CLASS - ui_task.begin(disp, the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION, the_mesh.getBLEPin()); + ui_task.begin(disp, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved #endif } From 006cd425e5b5777682822b73ea82ec843a26241f Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 15:27:33 +1000 Subject: [PATCH 076/103] * removing ESP32C6 stuff. (causing cache corruptions) --- platformio.ini | 5 -- variants/lilygo_tlora_c6/platformio.ini | 68 -------------------- variants/lilygo_tlora_c6/target.cpp | 82 ------------------------- variants/lilygo_tlora_c6/target.h | 20 ------ 4 files changed, 175 deletions(-) delete mode 100644 variants/lilygo_tlora_c6/platformio.ini delete mode 100644 variants/lilygo_tlora_c6/target.cpp delete mode 100644 variants/lilygo_tlora_c6/target.h diff --git a/platformio.ini b/platformio.ini index 94c961cf..80f850ea 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,11 +47,6 @@ lib_deps = me-no-dev/ESPAsyncWebServer @ ^3.6.0 file://arch/esp32/AsyncElegantOTA -; esp32c6 uses arduino framework 3.x -[esp32c6_base] -extends = esp32_base -platform = https://github.com/pioarduino/platform-espressif32.git - ; ----------------- NRF52 --------------------- [nrf52_base] diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini deleted file mode 100644 index 48e310c0..00000000 --- a/variants/lilygo_tlora_c6/platformio.ini +++ /dev/null @@ -1,68 +0,0 @@ -[tlora_c6] -extends = esp32c6_base -board = esp32-c6-devkitm-1 -board_build.partitions = min_spiffs.csv ; get around 4mb flash limit -build_flags = - ${esp32c6_base.build_flags} - -I variants/lilygo_tlora_c6 - -D ARDUINO_USB_CDC_ON_BOOT=1 - -D ARDUINO_USB_MODE=1 - -D P_LORA_TX_LED=7 - -D P_LORA_SCLK=6 - -D P_LORA_MISO=1 - -D P_LORA_MOSI=0 - -D P_LORA_NSS=18 - -D P_LORA_DIO_1=23 - -D P_LORA_BUSY=22 - -D P_LORA_RESET=21 - -D PIN_BOARD_SDA=8 - -D PIN_BOARD_SCL=9 - -D SX126X_RXEN=15 - -D SX126X_TXEN=14 - -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 RADIO_CLASS=CustomSX1262 - -D WRAPPER_CLASS=CustomSX1262Wrapper - -D LORA_TX_POWER=22 - -D DISABLE_WIFI_OTA=1 -build_src_filter = ${esp32c6_base.build_src_filter} - +<../variants/lilygo_tlora_c6> - -[env:LilyGo_Tlora_c6_Repeater] -extends = tlora_c6 -build_src_filter = ${tlora_c6.build_src_filter} - +<../examples/simple_repeater/main.cpp> -build_flags = - ${tlora_c6.build_flags} - -D ADVERT_NAME='"Tlora C6 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 = - ${tlora_c6.lib_deps} -; ${esp32_ota.lib_deps} - -[env:LilyGo_Tlora_c6_companion_radio_ble] -extends = tlora_c6 -build_flags = ${tlora_c6.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 - -D BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 - -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${tlora_c6.build_src_filter} - + - - - +<../examples/companion_radio> -lib_deps = - ${tlora_c6.lib_deps} - densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tlora_c6/target.cpp b/variants/lilygo_tlora_c6/target.cpp deleted file mode 100644 index 1e60dc32..00000000 --- a/variants/lilygo_tlora_c6/target.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include "target.h" - -ESP32Board board; - -#if defined(P_LORA_SCLK) - static SPIClass spi(0); - 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; - -#ifndef LORA_CR - #define LORA_CR 5 -#endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#ifdef SX126X_DIO3_TCXO_VOLTAGE - float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -#else - float tcxo = 1.6f; -#endif - -#if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); -#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); - -#if defined(SX126X_RXEN) && defined(SX126X_TXEN) - radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); -#endif - -#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() { - 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/lilygo_tlora_c6/target.h b/variants/lilygo_tlora_c6/target.h deleted file mode 100644 index eef923ab..00000000 --- a/variants/lilygo_tlora_c6/target.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#define RADIOLIB_STATIC_ONLY 1 -#include -#include -#include -#include -#include -#include - -extern ESP32Board board; -extern WRAPPER_CLASS radio_driver; -extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager 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(); From 870b5d2b702a0604f2735b8ce3ace80c928014e5 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 20:28:00 +1000 Subject: [PATCH 077/103] * companion: 'self telemetry' request with CMD_SEND_TELEMETRY_REQ (with no pubkey param) --- examples/companion_radio/MyMesh.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index b0905bb6..f7811415 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1373,6 +1373,28 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } + } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len == 4) { // 'self' telemetry request + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = 0; + uint32_t tag = 0, est_timeout = 50; + memcpy(&out_frame[2], &tag, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + + telemetry.reset(); + telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); + // query other sensors -- target specific + sensors.querySensors(0xFF, telemetry); + + int i = 0; + out_frame[i++] = PUSH_CODE_TELEMETRY_RESPONSE; + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], self_id.pub_key, 6); + i += 6; // pub_key_prefix + uint8_t tlen = telemetry.getSize(); + memcpy(&out_frame[i], telemetry.getBuffer(), tlen); + i += tlen; + _serial->writeFrame(out_frame, i); } else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) { uint8_t *pub_key = &cmd_frame[1]; if (hasConnectionTo(pub_key)) { From 1ba69f3b8dcc36247245b359da10f5aa3f34eee4 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 20:44:05 +1000 Subject: [PATCH 078/103] * self telemetry response simpler now --- examples/companion_radio/MyMesh.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index f7811415..121584a6 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1374,13 +1374,6 @@ void MyMesh::handleCmdFrame(size_t len) { writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found } } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len == 4) { // 'self' telemetry request - out_frame[0] = RESP_CODE_SENT; - out_frame[1] = 0; - uint32_t tag = 0, est_timeout = 50; - memcpy(&out_frame[2], &tag, 4); - memcpy(&out_frame[6], &est_timeout, 4); - _serial->writeFrame(out_frame, 10); - telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific From 8cf20c7c240d87b87394debfc417651fac26e899 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 2 Jun 2025 22:19:46 +1000 Subject: [PATCH 079/103] * Room server fix: re-tries for pushPostToClient() used to have same packet hash --- examples/simple_room_server/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 5ba6cbca..219c73ab 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -204,7 +204,10 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { void pushPostToClient(ClientInfo* client, PostInfo& post) { int len = 0; memcpy(&reply_data[len], &post.post_timestamp, 4); len += 4; // this is a PAST timestamp... but should be accepted by client - reply_data[len++] = (TXT_TYPE_SIGNED_PLAIN << 2); // 'signed' plain text + + uint8_t attempt; + getRNG()->random(&attempt, 1); // need this for re-tries, so packet hash (and ACK) will be different + reply_data[len++] = (TXT_TYPE_SIGNED_PLAIN << 2) | (attempt & 3); // 'signed' plain text // encode prefix of post.author.pub_key memcpy(&reply_data[len], post.author.pub_key, 4); len += 4; // just first 4 bytes From a3f8c21ff49c7a4cb60b84c0ed17484507bda93b Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 2 Jun 2025 19:33:48 +0200 Subject: [PATCH 080/103] t1000e: light and temp sensor support --- variants/t1000-e/t1000e_sensors.cpp | 119 ++++++++++++++++++++++++++++ variants/t1000-e/t1000e_sensors.h | 8 ++ variants/t1000-e/target.cpp | 5 ++ 3 files changed, 132 insertions(+) create mode 100644 variants/t1000-e/t1000e_sensors.cpp create mode 100644 variants/t1000-e/t1000e_sensors.h diff --git a/variants/t1000-e/t1000e_sensors.cpp b/variants/t1000-e/t1000e_sensors.cpp new file mode 100644 index 00000000..17693022 --- /dev/null +++ b/variants/t1000-e/t1000e_sensors.cpp @@ -0,0 +1,119 @@ +#include +#include "t1000e_sensors.h" + +#define HEATER_NTC_BX 4250 // thermistor coefficient B +#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor +#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin +#define NTC_REF_VCC 3000 // mV, output voltage of LDO +#define LIGHT_REF_VCC 2400 // + +static unsigned int ntc_res2[136]={ + 113347,107565,102116,96978,92132,87559,83242,79166,75316,71677, + 68237,64991,61919,59011,56258,53650,51178,48835,46613,44506, + 42506,40600,38791,37073,35442,33892,32420,31020,29689,28423, + 27219,26076,24988,23951,22963,22021,21123,20267,19450,18670, + 17926,17214,16534,15886,15266,14674,14108,13566,13049,12554, + 12081,11628,11195,10780,10382,10000,9634,9284,8947,8624, + 8315,8018,7734,7461,7199,6948,6707,6475,6253,6039, + 5834,5636,5445,5262,5086,4917,4754,4597,4446,4301, + 4161,4026,3896,3771,3651,3535,3423,3315,3211,3111, + 3014,2922,2834,2748,2666,2586,2509,2435,2364,2294, + 2228,2163,2100,2040,1981,1925,1870,1817,1766,1716, + 1669,1622,1578,1535,1493,1452,1413,1375,1338,1303, + 1268,1234,1202,1170,1139,1110,1081,1053,1026,999, + 974,949,925,902,880,858, +}; + +static char ntc_temp2[136]= +{ + -30,-29,-28,-27,-26,-25,-24,-23,-22,-21, + -20,-19,-18,-17,-16,-15,-14,-13,-12,-11, + -10,-9,-8,-7,-6,-5,-4,-3,-2,-1, + 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,32,33,34,35,36,37,38,39, + 40,41,42,43,44,45,46,47,48,49, + 50,51,52,53,54,55,56,57,58,59, + 60,61,62,63,64,65,66,67,68,69, + 70,71,72,73,74,75,76,77,78,79, + 80,81,82,83,84,85,86,87,88,89, + 90,91,92,93,94,95,96,97,98,99, + 100,101,102,103,104,105, +}; + +static float get_heater_temperature( unsigned int vcc_volt, unsigned int ntc_volt ) +{ + int i = 0; + float Vout = 0, Rt = 0, temp = 0; + Vout = ntc_volt; + + Rt = ( HEATER_NTC_RP * vcc_volt ) / Vout - HEATER_NTC_RP; + + for( i = 0; i < 136; i++ ) + { + if( Rt >= ntc_res2[i] ) + { + break; + } + } + + temp = ntc_temp2[i - 1] + 1 * ( ntc_res2[i - 1] - Rt ) / ( float )( ntc_res2[i - 1] - ntc_res2[i] ); + + temp = ( temp * 100 + 5 ) / 100; + return temp; +} + +static int get_light_lv( unsigned int light_volt ) +{ + float Vout = 0, Vin = 0, Rt = 0, temp = 0; + unsigned int light_level = 0; + + if( light_volt <= 80 ) + { + light_level = 0; + return light_level; + } + else if( light_volt >= 2480 ) + { + light_level = 100; + return light_level; + } + Vout = light_volt; + light_level = 100 * ( Vout - 80 ) / LIGHT_REF_VCC; + + return light_level; +} + +float t1000e_get_temperature( void ) +{ + unsigned int ntc_v, vcc_v; + + digitalWrite(PIN_3V3_EN, HIGH); + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + vcc_v = (1000.0*(analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096; + ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096; + digitalWrite(PIN_3V3_EN, LOW); + digitalWrite(SENSOR_EN, LOW); + + return get_heater_temperature (vcc_v, ntc_v); +} + +uint32_t t1000e_get_light( void ) +{ + int lux = 0; + unsigned int lux_v = 0; + + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096; + lux = get_light_lv( lux_v ); + digitalWrite(SENSOR_EN, LOW); + + return lux; +} \ No newline at end of file diff --git a/variants/t1000-e/t1000e_sensors.h b/variants/t1000-e/t1000e_sensors.h new file mode 100644 index 00000000..8f3967ca --- /dev/null +++ b/variants/t1000-e/t1000e_sensors.h @@ -0,0 +1,8 @@ +#pragma once + +// Light and temperature sensors are on ADC ports +// functions adapted from Seeed examples to get values +// see : https://github.com/Seeed-Studio/Seeed-Tracker-T1000-E-for-LoRaWAN-dev-board + +extern uint32_t t1000e_get_light(); +extern float t1000e_get_temperature(); \ No newline at end of file diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 29ca1acd..caa53930 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -1,4 +1,5 @@ #include +#include "t1000e_sensors.h" #include "target.h" #include @@ -160,6 +161,10 @@ bool T1000SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { + telemetry.addLuminosity(TELEM_CHANNEL_SELF, t1000e_get_light()); + telemetry.addTemperature(TELEM_CHANNEL_SELF, t1000e_get_temperature()); + } return true; } From 203a7f2bd363e74281bf6cb3b9b51057ef91f266 Mon Sep 17 00:00:00 2001 From: JQ Date: Mon, 2 Jun 2025 14:10:10 -0700 Subject: [PATCH 081/103] add display to station G2 firmwares --- variants/station_g2/platformio.ini | 11 ++++++++--- variants/station_g2/target.cpp | 4 ++++ variants/station_g2/target.h | 8 ++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index d3f45eb0..03914dce 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -9,18 +9,23 @@ build_flags = -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=7 ; -D P_LORA_TX_LED=35 -; -D PIN_BOARD_SDA=5 -; -D PIN_BOARD_SCL=6 - -D PIN_USER_BTN=0 + -D PIN_BOARD_SDA=5 + -D PIN_BOARD_SCL=6 + -D PIN_USER_BTN=38 -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! + -I src/helpers/ui + -D DISPLAY_CLASS=SH1106Display ; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance build_src_filter = ${esp32_base.build_src_filter} +<../variants/station_g2> + + lib_deps = ${esp32_base.lib_deps} + adafruit/Adafruit SH110X @ ~2.1.13 + adafruit/Adafruit GFX Library @ ^1.12.1 [env:Station_G2_repeater] extends = Station_G2 diff --git a/variants/station_g2/target.cpp b/variants/station_g2/target.cpp index e279b139..dca94f21 100644 --- a/variants/station_g2/target.cpp +++ b/variants/station_g2/target.cpp @@ -16,6 +16,10 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + #ifndef LORA_CR #define LORA_CR 5 #endif diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index e44c2ebe..695e700a 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -8,11 +8,19 @@ #include #include +#ifdef DISPLAY_CLASS + #include +#endif + extern StationG2Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; +#endif + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); From 8f5e521717c5956aeb910805a60eec50009225c1 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 2 Jun 2025 22:09:37 +0200 Subject: [PATCH 082/103] sx1262_wio: hook rxen, specify txen as not connected --- variants/xiao_nrf52/platformio.ini | 2 ++ variants/xiao_nrf52/target.cpp | 4 ++++ variants/xiao_s3_wio/platformio.ini | 2 ++ variants/xiao_s3_wio/target.cpp | 6 +++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 45d28740..50baccad 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -36,6 +36,8 @@ build_flags = ${nrf52840_xiao.build_flags} -D P_LORA_RESET=D2 -D P_LORA_BUSY=D3 -D P_LORA_NSS=D4 + -D SX126X_RXEN=D5 + -D SX126X_TXEN=RADIOLIB_NC -D SX126X_DIO2_AS_RF_SWITCH=1 -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/xiao_nrf52/target.cpp b/variants/xiao_nrf52/target.cpp index c78ea159..724e7c63 100644 --- a/variants/xiao_nrf52/target.cpp +++ b/variants/xiao_nrf52/target.cpp @@ -35,6 +35,10 @@ bool radio_init() { radio.setCRC(1); +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + #ifdef SX126X_CURRENT_LIMIT radio.setCurrentLimit(SX126X_CURRENT_LIMIT); #endif diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index cfc8e774..bb5ae698 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -15,6 +15,8 @@ build_flags = ${esp32_base.build_flags} -D P_LORA_MOSI=9 -D PIN_USER_BTN=21 -D PIN_STATUS_LED=48 + -D SX126X_RXEN=38 + -D SX126X_TXEN=RADIOLIB_NC -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/xiao_s3_wio/target.cpp b/variants/xiao_s3_wio/target.cpp index 09ee5daa..2f443205 100644 --- a/variants/xiao_s3_wio/target.cpp +++ b/variants/xiao_s3_wio/target.cpp @@ -47,7 +47,11 @@ bool radio_init() { } radio.setCRC(1); - + +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + #ifdef SX126X_CURRENT_LIMIT radio.setCurrentLimit(SX126X_CURRENT_LIMIT); #endif From bdc369be67e62ad04480138e9109385520da0bf8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 19:01:03 +1000 Subject: [PATCH 083/103] * repeater & room server: new SERVER_RESPONSE_DELAY and TXT_ACK_DELAY defines. --- examples/simple_repeater/main.cpp | 21 ++++++++++++++------- examples/simple_room_server/main.cpp | 26 +++++++++++++++++--------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 5db62ff1..d8ae9556 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -59,6 +59,14 @@ #define ADMIN_PASSWORD "password" #endif +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + #ifdef DISPLAY_CLASS #include "UITask.h" static UITask ui_task(display); @@ -112,8 +120,7 @@ struct NeighbourInfo { int8_t snr; // multiplied by 4, user should divide to get float value }; -// NOTE: need to space the ACK and the reply text apart (in CLI) -#define CLI_REPLY_DELAY_MILLIS 1500 +#define CLI_REPLY_DELAY_MILLIS 1000 class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; @@ -446,14 +453,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } @@ -482,9 +489,9 @@ protected: mesh::Packet* ack = createAck(ack_hash); if (ack) { if (client->out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, client->out_path, client->out_path_len); + sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); } } } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 219c73ab..9f2149fe 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -67,6 +67,14 @@ #define MAX_UNSYNCED_POSTS 32 #endif +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + #ifdef DISPLAY_CLASS #include "UITask.h" static UITask ui_task(display); @@ -568,12 +576,12 @@ protected: mesh::Packet* ack = createAck(ack_hash); if (ack) { if (client->out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, client->out_path, client->out_path_len); + sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); } } - delay_millis = REPLY_DELAY_MILLIS; + delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { delay_millis = 0; } @@ -592,9 +600,9 @@ protected: auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); if (reply) { if (client->out_path_len < 0) { - sendFlood(reply, delay_millis); + sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY); } else { - sendDirect(reply, client->out_path, client->out_path_len, delay_millis); + sendDirect(reply, client->out_path, client->out_path_len, delay_millis + SERVER_RESPONSE_DELAY); } } } @@ -637,7 +645,7 @@ protected: auto reply = createAck(ack_hash); if (reply) { reply->payload[reply->payload_len++] = getUnsyncedCount(client); // NEW: add unsynced counter to end of ACK packet - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } } } else { @@ -647,14 +655,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } From 6e2a0f3a9cf4a59201e9220dc824bc83dd5b5540 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 19:01:45 +1000 Subject: [PATCH 084/103] * ESP32-C3 targets, now 80Mhz cpu --- 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 6024905f..55baf2b8 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -4,6 +4,7 @@ board = seeed_xiao_esp32c3 build_flags = ${esp32_base.build_flags} -I variants/xiao_c3 + -D ESP32_CPU_FREQ=80 -D LORA_TX_BOOST_PIN=D3 -D P_LORA_TX_LED=D5 -D PIN_VBAT_READ=D0 From 75503ed52abccb79ee2c377888ec9fc6e3806189 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 20:27:09 +1000 Subject: [PATCH 085/103] * Companion now can be configured with TXT_ACK_DELAY (default is 200ms) --- src/helpers/BaseChatMesh.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 36ddcbb4..bcf83f8b 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -1,6 +1,10 @@ #include #include +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, double lon) { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; @@ -131,14 +135,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 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); + if (path) sendFlood(path, TXT_ACK_DELAY); } else { mesh::Packet* ack = createAck(ack_hash); if (ack) { if (from.out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, from.out_path, from.out_path_len); + sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); } } } @@ -164,14 +168,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 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); + if (path) sendFlood(path, TXT_ACK_DELAY); } else { mesh::Packet* ack = createAck(ack_hash); if (ack) { if (from.out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, from.out_path, from.out_path_len); + sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); } } } From 0535919d63e4524dad66c4cbf04971a2c492f44d Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 20:28:05 +1000 Subject: [PATCH 086/103] * Mesh: reciprocal path send now with slightly less priority and 500ms delay --- src/Mesh.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 6029c192..a6b06c07 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -142,7 +142,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteFlood()) { // send a reciprocal return path to sender, but send DIRECTLY! mesh::Packet* rpath = createPathReturn(&src_hash, secret, pkt->path, pkt->path_len, 0, NULL, 0); - if (rpath) sendDirect(rpath, path, path_len); + if (rpath) sendDirect(rpath, path, path_len, 500); } } } else { @@ -518,7 +518,11 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin pri = 5; // maybe make this configurable } else { memcpy(packet->path, path, packet->path_len = path_len); - pri = 0; + if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { + pri = 1; // slightly less priority + } else { + pri = 0; + } } _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, pri, delay_millis); From 5d15a68d0d8f62dc21aeac4ce2a4cd993f8dd0d6 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 4 Jun 2025 18:10:47 +1000 Subject: [PATCH 087/103] * SERVER_RESPONSE_DELAY now applied to: login responses, companion telemetry responses --- examples/simple_repeater/main.cpp | 6 +++--- examples/simple_room_server/main.cpp | 6 +++--- src/helpers/BaseChatMesh.cpp | 10 +++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index d8ae9556..c2a8ae92 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -383,14 +383,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, 12); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 12); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 9f2149fe..32fe4a04 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -479,14 +479,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, 8 + 2); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 8 + 2); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index bcf83f8b..ba8c3e28 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -1,6 +1,10 @@ #include #include +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + #ifndef TXT_ACK_DELAY #define TXT_ACK_DELAY 200 #endif @@ -191,14 +195,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); + if (path) sendFlood(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); + sendDirect(reply, from.out_path, from.out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } From 647d712ae8a553d3f3aab3036076db7a2667e1ee Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 4 Jun 2025 21:33:48 +1000 Subject: [PATCH 088/103] * Companion: long-press in first 8 seconds now enters CLI Rescue mode --- examples/companion_radio/MyMesh.cpp | 84 +++++++++++++++++++++++++- examples/companion_radio/MyMesh.h | 7 +++ examples/companion_radio/UITask.cpp | 7 ++- examples/companion_radio/UITask.h | 4 +- variants/heltec_tracker/platformio.ini | 2 +- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 121584a6..730747e1 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -728,6 +728,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; + _cli_rescue = false; offline_queue_len = 0; app_target_ver = 0; _identity_store = NULL; @@ -1529,9 +1530,78 @@ void MyMesh::handleCmdFrame(size_t len) { } } -void MyMesh::loop() { - BaseChatMesh::loop(); +void MyMesh::enterCLIRescue() { + _cli_rescue = true; + cli_command[0] = 0; + Serial.println("========= CLI Rescue ========="); +} +bool MyMesh::formatFileSystem() { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return InternalFS.format(); +#elif defined(RP2040_PLATFORM) + return LittleFS.format(); +#elif defined(ESP32) + return SPIFFS.format(); +#else + #error "need to implement file system erase" + return false; +#endif +} + +void MyMesh::checkCLIRescueCmd() { + int len = strlen(cli_command); + while (Serial.available() && len < sizeof(cli_command)-1) { + char c = Serial.read(); + if (c != '\n') { + cli_command[len++] = c; + cli_command[len] = 0; + } + Serial.print(c); // echo + } + if (len == sizeof(cli_command)-1) { // command buffer full + cli_command[sizeof(cli_command)-1] = '\r'; + } + + if (len > 0 && cli_command[len - 1] == '\r') { // received complete line + cli_command[len - 1] = 0; // replace newline with C string null terminator + + if (memcmp(cli_command, "set ", 4) == 0) { + const char* config = &cli_command[4]; + if (memcmp(config, "pin ", 4) == 0) { + _prefs.ble_pin = atoi(&config[4]); + savePrefs(); + Serial.printf(" > pin is now %06d\n", _prefs.ble_pin); + } else { + Serial.printf(" Error: unknown config: %s\n", config); + } + } else if (strcmp(cli_command, "rebuild") == 0) { + bool success = formatFileSystem(); + if (success) { + saveMainIdentity(self_id); + saveContacts(); + Serial.println(" > erase and rebuild done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "erase") == 0) { + bool success = formatFileSystem(); + if (success) { + Serial.println(" > erase done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "reboot") == 0) { + board.reboot(); // doesn't return + } else { + Serial.println(" Error: unknown command"); + } + + cli_command[0] = 0; // reset command buffer + } +} + +void MyMesh::checkSerialInterface() { size_t len = _serial->checkRecvFrame(cmd_frame); if (len > 0) { handleCmdFrame(len); @@ -1556,6 +1626,16 @@ void MyMesh::loop() { } else if (!_serial->isWriteBusy()) { checkConnections(); } +} + +void MyMesh::loop() { + BaseChatMesh::loop(); + + if (_cli_rescue) { + checkCLIRescueCmd(); + } else { + checkSerialInterface(); + } // is there are pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 9d7da0df..92da8c9d 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -92,6 +92,7 @@ public: void loop(); void handleCmdFrame(size_t len); bool advert(); + void enterCLIRescue(); protected: float getAirtimeBudgetFactor() const override; @@ -143,6 +144,10 @@ private: int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + void checkCLIRescueCmd(); + void checkSerialInterface(); + bool formatFileSystem(); + private: FILESYSTEM *_fs; IdentityStore *_identity_store; @@ -157,6 +162,8 @@ private: uint32_t _most_recent_lastmod; uint32_t _active_ble_pin; bool _iter_started; + bool _cli_rescue; + char cli_command[80]; uint8_t app_target_ver; uint8_t *sign_data; uint32_t sign_data_len; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 00263e5e..f0f780d6 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -75,6 +75,7 @@ void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs) { _userButton->onLongPress([this]() { handleButtonLongPress(); }); _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); #endif + ui_started_at = millis(); } void UITask::soundBuzzer(UIEventType bet) { @@ -365,5 +366,9 @@ void UITask::handleButtonTriplePress() { void UITask::handleButtonLongPress() { MESH_DEBUG_PRINTLN("UITask: long press triggered"); - shutdown(); + if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue + the_mesh.enterCLIRescue(); + } else { + shutdown(); + } } \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 6914e9ec..fff0bbe1 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -36,6 +36,7 @@ class UITask { int _msgcount; bool _need_refresh = true; bool _displayWasOn = false; // Track display state before button press + unsigned long ui_started_at; // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) @@ -57,7 +58,8 @@ class UITask { public: UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { - _next_refresh = 0; + _next_refresh = 0; + ui_started_at = 0; _connected = false; } void begin(DisplayDriver* display, NodePrefs* node_prefs); diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index d62771f4..ed4cbe28 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -39,7 +39,7 @@ extends = Heltec_tracker_base build_flags = ${Heltec_tracker_base.build_flags} -I src/helpers/ui - ; -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for debugging + -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display -D MAX_CONTACTS=100 From 9bcab0949e5c80b7c5934abfd5e2121eed06f4ee Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 5 Jun 2025 14:04:33 +1000 Subject: [PATCH 089/103] * noise floor lower bound now clamped to -120 --- src/helpers/RadioLibWrappers.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index d37bc498..1415a864 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -63,6 +63,9 @@ void RadioLibWrapper::loop() { } } else if (_num_floor_samples >= NUM_NOISE_FLOOR_SAMPLES && _floor_sample_sum != 0) { _noise_floor = _floor_sample_sum / NUM_NOISE_FLOOR_SAMPLES; + if (_noise_floor < -120) { + _noise_floor = -120; // clamp to lower bound of -120dBi + } _floor_sample_sum = 0; MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor); From 6e0b505a2a83d628f11efc2052f52ab6ddc4af49 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 6 Jun 2025 15:30:35 +1000 Subject: [PATCH 090/103] * companion: refactor of all filesystem access to new DataStore module --- examples/companion_radio/DataStore.cpp | 306 ++++++++++++++++++++++ examples/companion_radio/DataStore.h | 36 +++ examples/companion_radio/MyMesh.cpp | 344 ++----------------------- examples/companion_radio/MyMesh.h | 32 +-- examples/companion_radio/main.cpp | 15 +- src/helpers/BaseChatMesh.cpp | 7 + src/helpers/BaseChatMesh.h | 20 +- src/helpers/ChannelDetails.h | 9 + src/helpers/ContactInfo.h | 18 ++ 9 files changed, 433 insertions(+), 354 deletions(-) create mode 100644 examples/companion_radio/DataStore.cpp create mode 100644 examples/companion_radio/DataStore.h create mode 100644 src/helpers/ChannelDetails.h create mode 100644 src/helpers/ContactInfo.h diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp new file mode 100644 index 00000000..fa848c05 --- /dev/null +++ b/examples/companion_radio/DataStore.cpp @@ -0,0 +1,306 @@ +#include +#include "DataStore.h" + +DataStore::DataStore(FILESYSTEM& fs) : _fs(&fs), +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + identity_store(fs, "") +#elif defined(RP2040_PLATFORM) + identity_store(fs, "/identity") +#else + identity_store(fs, "/identity") +#endif +{ +} + +void DataStore::begin() { +#if defined(RP2040_PLATFORM) + identity_store.begin(); +#endif + + // init 'blob store' support + _fs->mkdir("/bl"); +} + +#if defined(ESP32) + #include +#elif defined(RP2040_PLATFORM) + #include +#endif + +bool DataStore::formatFileSystem() { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return _fs->format(); +#elif defined(RP2040_PLATFORM) + return LittleFS.format(); +#elif defined(ESP32) + return ((fs::SPIFFSFS *)_fs)->format(); +#else + #error "need to implement format()" +#endif +} + +bool DataStore::loadMainIdentity(mesh::LocalIdentity &identity) { + return identity_store.load("_main", identity); +} + +bool DataStore::saveMainIdentity(const mesh::LocalIdentity &identity) { + return identity_store.save("_main", identity); +} + +void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) { + if (_fs->exists("/new_prefs")) { + loadPrefsInt("/new_prefs", prefs, node_lat, node_lon); // new filename + } else if (_fs->exists("/node_prefs")) { + loadPrefsInt("/node_prefs", prefs, node_lat, node_lon); + savePrefs(prefs, node_lat, node_lon); // save to new filename + _fs->remove("/node_prefs"); // remove old + } +} + +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 + if (file) { + uint8_t pad[8]; + + file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 + file.read((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 + file.read(pad, 4); // 36 + file.read((uint8_t *)&node_lat, sizeof(node_lat)); // 40 + file.read((uint8_t *)&node_lon, sizeof(node_lon)); // 48 + file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 + file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 + file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 + file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 + file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 + file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 + file.read((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 + file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 + file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 + file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 + file.read(pad, 4); // 76 + file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 + + file.close(); + } +} + +void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_lon) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/new_prefs"); + File file = _fs->open("/new_prefs", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/new_prefs", "w"); +#else + File file = _fs->open("/new_prefs", "w", true); +#endif + if (file) { + uint8_t pad[8]; + memset(pad, 0, sizeof(pad)); + + file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 + file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 + file.write(pad, 4); // 36 + file.write((uint8_t *)&node_lat, sizeof(node_lat)); // 40 + file.write((uint8_t *)&node_lon, sizeof(node_lon)); // 48 + file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 + file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 + file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 + file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 + file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 + file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 + file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 + file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 + file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 + file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 + file.write(pad, 4); // 76 + file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 + + file.close(); + } +} + +void DataStore::loadContacts(DataStoreHost* host) { + if (_fs->exists("/contacts3")) { +#if defined(RP2040_PLATFORM) + File file = _fs->open("/contacts3", "r"); +#else + File file = _fs->open("/contacts3"); +#endif + if (file) { + bool full = false; + while (!full) { + ContactInfo c; + uint8_t pub_key[32]; + uint8_t unused; + + bool success = (file.read(pub_key, 32) == 32); + success = success && (file.read((uint8_t *)&c.name, 32) == 32); + success = success && (file.read(&c.type, 1) == 1); + success = success && (file.read(&c.flags, 1) == 1); + success = success && (file.read(&unused, 1) == 1); + success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved' + success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.read(c.out_path, 64) == 64); + success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4); + success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); + success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); + + if (!success) break; // EOF + + c.id = mesh::Identity(pub_key); + if (!host->onContactLoaded(c)) full = true; + } + file.close(); + } + } +} + +void DataStore::saveContacts(DataStoreHost* host) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/contacts3"); + File file = _fs->open("/contacts3", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/contacts3", "w"); +#else + File file = _fs->open("/contacts3", "w", true); +#endif + if (file) { + uint32_t idx = 0; + ContactInfo c; + uint8_t unused = 0; + + while (host->getContactForSave(idx, c)) { + bool success = (file.write(c.id.pub_key, 32) == 32); + success = success && (file.write((uint8_t *)&c.name, 32) == 32); + success = success && (file.write(&c.type, 1) == 1); + success = success && (file.write(&c.flags, 1) == 1); + success = success && (file.write(&unused, 1) == 1); + success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4); + success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); + success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); + success = success && (file.write(c.out_path, 64) == 64); + success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4); + success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); + success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); + + if (!success) break; // write failed + + idx++; // advance to next contact + } + file.close(); + } +} + +void DataStore::loadChannels(DataStoreHost* host) { + if (_fs->exists("/channels2")) { +#if defined(RP2040_PLATFORM) + File file = _fs->open("/channels2", "r"); +#else + File file = _fs->open("/channels2"); +#endif + if (file) { + bool full = false; + uint8_t channel_idx = 0; + while (!full) { + ChannelDetails ch; + uint8_t unused[4]; + + bool success = (file.read(unused, 4) == 4); + success = success && (file.read((uint8_t *)ch.name, 32) == 32); + success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); + + if (!success) break; // EOF + + if (host->onChannelLoaded(channel_idx, ch)) { + channel_idx++; + } else { + full = true; + } + } + file.close(); + } + } +} + +void DataStore::saveChannels(DataStoreHost* host) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/channels2"); + File file = _fs->open("/channels2", FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File file = _fs->open("/channels2", "w"); +#else + File file = _fs->open("/channels2", "w", true); +#endif + if (file) { + uint8_t channel_idx = 0; + ChannelDetails ch; + uint8_t unused[4]; + memset(unused, 0, 4); + + while (host->getChannelForSave(channel_idx, ch)) { + bool success = (file.write(unused, 4) == 4); + success = success && (file.write((uint8_t *)ch.name, 32) == 32); + success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); + + if (!success) break; // write failed + channel_idx++; + } + file.close(); + } +} + +int DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { + char path[64]; + char fname[18]; + + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + 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 + if (f) { + int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! + f.close(); + return len; + } + } + return 0; // not found +} + +bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { + char path[64]; + char fname[18]; + + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + sprintf(path, "/bl/%s", fname); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(path); + File f = _fs->open(path, FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + File f = _fs->open(path, "w"); +#else + File f = _fs->open(path, "w", true); +#endif + if (f) { + int n = f.write(src_buf, len); + f.close(); + if (n == len) return true; // success! + + _fs->remove(path); // blob was only partially written! + } + return false; // error +} diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h new file mode 100644 index 00000000..d1ed19bf --- /dev/null +++ b/examples/companion_radio/DataStore.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include "NodePrefs.h" + +class DataStoreHost { +public: + virtual bool onContactLoaded(const ContactInfo& contact) =0; + virtual bool getContactForSave(uint32_t idx, ContactInfo& contact) =0; + virtual bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) =0; + virtual bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) =0; +}; + +class DataStore { + FILESYSTEM* _fs; + IdentityStore identity_store; + + void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); + + public: + DataStore(FILESYSTEM& fs); + void begin(); + bool formatFileSystem(); + bool loadMainIdentity(mesh::LocalIdentity &identity); + bool saveMainIdentity(const mesh::LocalIdentity &identity); + void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon); + void savePrefs(const NodePrefs& prefs, double node_lat, double node_lon); + void loadContacts(DataStoreHost* host); + void saveContacts(DataStoreHost* host); + void loadChannels(DataStoreHost* host); + void saveChannels(DataStoreHost* host); + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len); +}; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 730747e1..4c7b1618 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -103,200 +103,6 @@ #include "UITask.h" #endif -void MyMesh::loadMainIdentity() { - if (!_identity_store->load("_main", self_id)) { - self_id = radio_new_identity(); // create new random identity - int count = 0; - while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes - self_id = radio_new_identity(); - count++; - } - saveMainIdentity(self_id); - } -} - -bool MyMesh::saveMainIdentity(const mesh::LocalIdentity &identity) { - return _identity_store->save("_main", identity); -} - -void MyMesh::loadContacts() { - if (_fs->exists("/contacts3")) { -#if defined(RP2040_PLATFORM) - File file = _fs->open("/contacts3", "r"); -#else - File file = _fs->open("/contacts3"); -#endif - if (file) { - bool full = false; - while (!full) { - ContactInfo c; - uint8_t pub_key[32]; - uint8_t unused; - - bool success = (file.read(pub_key, 32) == 32); - success = success && (file.read((uint8_t *)&c.name, 32) == 32); - success = success && (file.read(&c.type, 1) == 1); - success = success && (file.read(&c.flags, 1) == 1); - success = success && (file.read(&unused, 1) == 1); - success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved' - success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); - success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); - success = success && (file.read(c.out_path, 64) == 64); - success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4); - success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); - success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); - - if (!success) break; // EOF - - c.id = mesh::Identity(pub_key); - if (!addContact(c)) full = true; - } - file.close(); - } - } -} - -void MyMesh::saveContacts() { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/contacts3"); - File file = _fs->open("/contacts3", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/contacts3", "w"); -#else - File file = _fs->open("/contacts3", "w", true); -#endif - if (file) { - ContactsIterator iter; - ContactInfo c; - uint8_t unused = 0; - - while (iter.hasNext(this, c)) { - bool success = (file.write(c.id.pub_key, 32) == 32); - success = success && (file.write((uint8_t *)&c.name, 32) == 32); - success = success && (file.write(&c.type, 1) == 1); - success = success && (file.write(&c.flags, 1) == 1); - success = success && (file.write(&unused, 1) == 1); - success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4); - success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); - success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); - success = success && (file.write(c.out_path, 64) == 64); - success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4); - success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); - success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); - - if (!success) break; // write failed - } - file.close(); - } -} - -void MyMesh::loadChannels() { - if (_fs->exists("/channels2")) { -#if defined(RP2040_PLATFORM) - File file = _fs->open("/channels2", "r"); -#else - File file = _fs->open("/channels2"); -#endif - if (file) { - bool full = false; - uint8_t channel_idx = 0; - while (!full) { - ChannelDetails ch; - uint8_t unused[4]; - - bool success = (file.read(unused, 4) == 4); - success = success && (file.read((uint8_t *)ch.name, 32) == 32); - success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); - - if (!success) break; // EOF - - if (setChannel(channel_idx, ch)) { - channel_idx++; - } else { - full = true; - } - } - file.close(); - } - } -} - -void MyMesh::saveChannels() { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/channels2"); - File file = _fs->open("/channels2", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/channels2", "w"); -#else - File file = _fs->open("/channels2", "w", true); -#endif - if (file) { - uint8_t channel_idx = 0; - ChannelDetails ch; - uint8_t unused[4]; - memset(unused, 0, 4); - - while (getChannel(channel_idx, ch)) { - bool success = (file.write(unused, 4) == 4); - success = success && (file.write((uint8_t *)ch.name, 32) == 32); - success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); - - if (!success) break; // write failed - channel_idx++; - } - file.close(); - } -} - -int MyMesh::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { - char path[64]; - char fname[18]; - - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) - mesh::Utils::toHex(fname, key, key_len); - 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 - if (f) { - int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! - f.close(); - return len; - } - } - return 0; // not found -} - -bool MyMesh::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { - char path[64]; - char fname[18]; - - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) - mesh::Utils::toHex(fname, key, key_len); - sprintf(path, "/bl/%s", fname); - -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(path); - File f = _fs->open(path, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File f = _fs->open(path, "w"); -#else - File f = _fs->open(path, "w", true); -#endif - if (f) { - int n = f.write(src_buf, len); - f.close(); - if (n == len) return true; // success! - - _fs->remove(path); // blob was only partially written! - } - return false; // error -} - void MyMesh::writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; @@ -724,14 +530,13 @@ uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t void MyMesh::onSendTimeout() {} -MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables) +MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store) : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), - _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store) { _iter_started = false; _cli_rescue = false; offline_queue_len = 0; app_target_ver = 0; - _identity_store = NULL; pending_login = pending_status = pending_telemetry = 0; next_ack_idx = 0; sign_data = NULL; @@ -749,62 +554,18 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe //_prefs.rx_delay_base = 10.0f; enable once new algo fixed } -void MyMesh::loadPrefsInt(const char *filename) { -#if defined(RP2040_PLATFORM) - File file = _fs->open(filename, "r"); -#else - File file = _fs->open(filename); -#endif - if (file) { - uint8_t pad[8]; - - file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 - file.read((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 - file.read(pad, 4); // 36 - file.read((uint8_t *)&sensors.node_lat, sizeof(sensors.node_lat)); // 40 - file.read((uint8_t *)&sensors.node_lon, sizeof(sensors.node_lon)); // 48 - file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 - file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 - file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 - file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 - file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 - file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 - file.read((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 - file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 - file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 - file.read(pad, 4); // 76 - file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 - - // sanitise bad pref values - _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.cr = constrain(_prefs.cr, 5, 8); - _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); - - file.close(); - } -} - -void MyMesh::begin(FILESYSTEM &fs, bool has_display) { - _fs = &fs; - +void MyMesh::begin(bool has_display) { BaseChatMesh::begin(); -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _identity_store = new IdentityStore(fs, ""); -#elif defined(RP2040_PLATFORM) - _identity_store = new IdentityStore(fs, "/identity"); - _identity_store->begin(); -#else - _identity_store = new IdentityStore(fs, "/identity"); -#endif - - loadMainIdentity(); + if (!_store->loadMainIdentity(self_id)) { + self_id = radio_new_identity(); // create new random identity + int count = 0; + while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes + self_id = radio_new_identity(); + count++; + } + _store->saveMainIdentity(self_id); + } // use hex of first 4 bytes of identity public key as default node name char pub_key_hex[10]; @@ -817,13 +578,16 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) { #endif // load persisted prefs - if (_fs->exists("/new_prefs")) { - loadPrefsInt("/new_prefs"); // new filename - } else if (_fs->exists("/node_prefs")) { - loadPrefsInt("/node_prefs"); - savePrefs(); // save to new filename - _fs->remove("/node_prefs"); // remove old - } + _store->loadPrefs(_prefs, sensors.node_lat, sensors.node_lon); + + // sanitise bad pref values + _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.cr = constrain(_prefs.cr, 5, 8); + _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); #ifdef BLE_PIN_CODE if (_prefs.ble_pin == 0) { @@ -844,12 +608,9 @@ void MyMesh::begin(FILESYSTEM &fs, bool has_display) { _active_ble_pin = 0; #endif - // init 'blob store' support - _fs->mkdir("/bl"); - - loadContacts(); + _store->loadContacts(this); addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel - loadChannels(); + _store->loadChannels(this); radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); @@ -870,42 +631,6 @@ void MyMesh::startInterface(BaseSerialInterface &serial) { serial.enable(); } -void MyMesh::savePrefs() { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/new_prefs"); - File file = _fs->open("/new_prefs", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/new_prefs", "w"); -#else - File file = _fs->open("/new_prefs", "w", true); -#endif - if (file) { - uint8_t pad[8]; - memset(pad, 0, sizeof(pad)); - - file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0 - file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4 - file.write(pad, 4); // 36 - file.write((uint8_t *)&sensors.node_lat, sizeof(sensors.node_lat)); // 40 - file.write((uint8_t *)&sensors.node_lon, sizeof(sensors.node_lon)); // 48 - file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 - file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 - file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 - file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 - file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 - file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 - file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 - file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 - file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 - file.write(pad, 4); // 76 - file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 - - file.close(); - } -} - void MyMesh::handleCmdFrame(size_t len) { if (cmd_frame[0] == CMD_DEVICE_QEURY && len >= 2) { // sent when app establishes connection app_target_ver = cmd_frame[1]; // which version of protocol does app understand @@ -1286,7 +1011,7 @@ void MyMesh::handleCmdFrame(size_t len) { #if ENABLE_PRIVATE_KEY_IMPORT mesh::LocalIdentity identity; identity.readFrom(&cmd_frame[1], 64); - if (saveMainIdentity(identity)) { + if (_store->saveMainIdentity(identity)) { self_id = identity; writeOKFrame(); } else { @@ -1536,19 +1261,6 @@ void MyMesh::enterCLIRescue() { Serial.println("========= CLI Rescue ========="); } -bool MyMesh::formatFileSystem() { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - return InternalFS.format(); -#elif defined(RP2040_PLATFORM) - return LittleFS.format(); -#elif defined(ESP32) - return SPIFFS.format(); -#else - #error "need to implement file system erase" - return false; -#endif -} - void MyMesh::checkCLIRescueCmd() { int len = strlen(cli_command); while (Serial.available() && len < sizeof(cli_command)-1) { @@ -1576,16 +1288,16 @@ void MyMesh::checkCLIRescueCmd() { Serial.printf(" Error: unknown config: %s\n", config); } } else if (strcmp(cli_command, "rebuild") == 0) { - bool success = formatFileSystem(); + bool success = _store->formatFileSystem(); if (success) { - saveMainIdentity(self_id); + _store->saveMainIdentity(self_id); saveContacts(); Serial.println(" > erase and rebuild done"); } else { Serial.println(" Error: erase failed"); } } else if (strcmp(cli_command, "erase") == 0) { - bool success = formatFileSystem(); + bool success = _store->formatFileSystem(); if (success) { Serial.println(" > erase done"); } else { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 92da8c9d..6ac030d9 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -25,6 +25,7 @@ #include #endif +#include "DataStore.h" #include "NodePrefs.h" #include @@ -76,14 +77,12 @@ #define REQ_TYPE_KEEP_ALIVE 0x02 #define REQ_TYPE_GET_TELEMETRY_DATA 0x03 -class MyMesh : public BaseChatMesh { +class MyMesh : public BaseChatMesh, public DataStoreHost { public: - MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables); + MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store); - void begin(FILESYSTEM &fs, bool has_display); + void begin(bool has_display); void startInterface(BaseSerialInterface &serial); - void loadPrefsInt(const char *filename); - void savePrefs(); const char *getNodeName(); NodePrefs *getNodePrefs(); @@ -127,6 +126,12 @@ protected: uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; void onSendTimeout() override; + // DataStoreHost methods + bool onContactLoaded(const ContactInfo& contact) override { return addContact(contact); } + bool getContactForSave(uint32_t idx, ContactInfo& contact) override { return getContactByIdx(idx, contact); } + bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) override { return setChannel(channel_idx, ch); } + bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) override { return getChannel(channel_idx, ch); } + private: void writeOKFrame(); void writeErrFrame(uint8_t err_code); @@ -135,22 +140,17 @@ private: void updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len); void addToOfflineQueue(const uint8_t frame[], int len); int getFromOfflineQueue(uint8_t frame[]); - void loadMainIdentity(); - bool saveMainIdentity(const mesh::LocalIdentity &identity); - void loadContacts(); - void saveContacts(); - void loadChannels(); - void saveChannels(); - int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; - bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; void checkCLIRescueCmd(); void checkSerialInterface(); - bool formatFileSystem(); + + // helpers, short-cuts + void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } + void saveChannels() { _store->saveChannels(this); } + void saveContacts() { _store->saveContacts(this); } private: - FILESYSTEM *_fs; - IdentityStore *_identity_store; + DataStore* _store; NodePrefs _prefs; uint32_t pending_login; uint32_t pending_status; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index a276a2b5..b463dcbe 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -14,10 +14,13 @@ static uint32_t _atoi(const char* sp) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include + DataStore store(InternalFS); #elif defined(RP2040_PLATFORM) #include + DataStore store(LittleFS); #elif defined(ESP32) #include + DataStore store(SPIFFS); #endif #ifdef ESP32 @@ -74,7 +77,7 @@ static uint32_t _atoi(const char* sp) { /* GLOBAL OBJECTS */ StdRNG fast_rng; SimpleMeshTables tables; -MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables); +MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store); #ifdef DISPLAY_CLASS #include "UITask.h" @@ -82,7 +85,6 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables); #endif /* END GLOBAL OBJECTS */ - void halt() { while (1) ; } @@ -108,7 +110,8 @@ void setup() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) InternalFS.begin(); - the_mesh.begin(InternalFS, + store.begin(); + the_mesh.begin( #ifdef DISPLAY_CLASS disp != NULL #else @@ -126,7 +129,8 @@ void setup() { the_mesh.startInterface(serial_interface); #elif defined(RP2040_PLATFORM) LittleFS.begin(); - the_mesh.begin(LittleFS, + store.begin(); + the_mesh.begin( #ifdef DISPLAY_CLASS disp != NULL #else @@ -151,7 +155,8 @@ void setup() { the_mesh.startInterface(serial_interface); #elif defined(ESP32) SPIFFS.begin(true); - the_mesh.begin(SPIFFS, + store.begin(); + the_mesh.begin( #ifdef DISPLAY_CLASS disp != NULL #else diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index ba8c3e28..7efd4735 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -699,6 +699,13 @@ int BaseChatMesh::findChannelIdx(const mesh::GroupChannel& ch) { } #endif +bool BaseChatMesh::getContactByIdx(uint32_t idx, ContactInfo& contact) { + if (idx >= num_contacts) return false; + + contact = contacts[idx]; + return true; +} + ContactsIterator BaseChatMesh::startContactsIterator() { return ContactsIterator(); } diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 2222bf4e..83ad2a89 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -7,19 +7,7 @@ #define MAX_TEXT_LEN (10*CIPHER_BLOCK_SIZE) // must be LESS than (MAX_PACKET_PAYLOAD - 4 - CIPHER_MAC_SIZE - 1) -struct ContactInfo { - mesh::Identity id; - char name[32]; - uint8_t type; // on of ADV_TYPE_* - uint8_t flags; - int8_t out_path_len; - uint8_t out_path[MAX_PATH_SIZE]; - uint32_t last_advert_timestamp; // by THEIR clock - uint8_t shared_secret[PUB_KEY_SIZE]; - uint32_t lastmod; // by OUR clock - int32_t gps_lat, gps_lon; // 6 dec places - uint32_t sync_since; -}; +#include "ContactInfo.h" #define MAX_SEARCH_RESULTS 8 @@ -61,10 +49,7 @@ struct ConnectionInfo { uint32_t expected_ack; }; -struct ChannelDetails { - mesh::GroupChannel channel; - char name[32]; -}; +#include "ChannelDetails.h" /** * \brief abstract Mesh class for common 'chat' client @@ -158,6 +143,7 @@ public: bool removeContact(ContactInfo& contact); bool addContact(const ContactInfo& contact); int getNumContacts() const { return num_contacts; } + bool getContactByIdx(uint32_t idx, ContactInfo& contact); ContactsIterator startContactsIterator(); ChannelDetails* addChannel(const char* name, const char* psk_base64); bool getChannel(int idx, ChannelDetails& dest); diff --git a/src/helpers/ChannelDetails.h b/src/helpers/ChannelDetails.h new file mode 100644 index 00000000..b9d38d4f --- /dev/null +++ b/src/helpers/ChannelDetails.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +struct ChannelDetails { + mesh::GroupChannel channel; + char name[32]; +}; diff --git a/src/helpers/ContactInfo.h b/src/helpers/ContactInfo.h new file mode 100644 index 00000000..4a8038d3 --- /dev/null +++ b/src/helpers/ContactInfo.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +struct ContactInfo { + mesh::Identity id; + char name[32]; + uint8_t type; // on of ADV_TYPE_* + uint8_t flags; + int8_t out_path_len; + uint8_t out_path[MAX_PATH_SIZE]; + uint32_t last_advert_timestamp; // by THEIR clock + uint8_t shared_secret[PUB_KEY_SIZE]; + uint32_t lastmod; // by OUR clock + int32_t gps_lat, gps_lon; // 6 dec places + uint32_t sync_since; +}; From dd808ee6c741367f57010e2e56fd1db8fb9a35a7 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 6 Jun 2025 19:50:51 +1000 Subject: [PATCH 091/103] * new nRF52 impl for advert blobs --- examples/companion_radio/DataStore.cpp | 128 ++++++++++++++++++------- examples/companion_radio/DataStore.h | 9 +- examples/companion_radio/MyMesh.cpp | 2 + 3 files changed, 102 insertions(+), 37 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index fa848c05..8e0d119a 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -12,13 +12,28 @@ DataStore::DataStore(FILESYSTEM& fs) : _fs(&fs), { } +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 +} + void DataStore::begin() { #if defined(RP2040_PLATFORM) identity_store.begin(); #endif +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + checkAdvBlobFile(); +#else // init 'blob store' support _fs->mkdir("/bl"); +#endif } #if defined(ESP32) @@ -90,14 +105,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no } void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_lon) { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/new_prefs"); - File file = _fs->open("/new_prefs", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/new_prefs", "w"); -#else - File file = _fs->open("/new_prefs", "w", true); -#endif + File file = openWrite(_fs, "/new_prefs"); if (file) { uint8_t pad[8]; memset(pad, 0, sizeof(pad)); @@ -163,14 +171,7 @@ void DataStore::loadContacts(DataStoreHost* host) { } void DataStore::saveContacts(DataStoreHost* host) { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/contacts3"); - File file = _fs->open("/contacts3", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/contacts3", "w"); -#else - File file = _fs->open("/contacts3", "w", true); -#endif + File file = openWrite(_fs, "/contacts3"); if (file) { uint32_t idx = 0; ContactInfo c; @@ -230,14 +231,7 @@ void DataStore::loadChannels(DataStoreHost* host) { } void DataStore::saveChannels(DataStoreHost* host) { -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove("/channels2"); - File file = _fs->open("/channels2", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File file = _fs->open("/channels2", "w"); -#else - File file = _fs->open("/channels2", "w", true); -#endif + File file = openWrite(_fs, "/channels2"); if (file) { uint8_t channel_idx = 0; ChannelDetails ch; @@ -256,7 +250,79 @@ void DataStore::saveChannels(DataStoreHost* host) { } } -int DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + +#define MAX_ADVERT_PKT_LEN (PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE) + +void DataStore::checkAdvBlobFile() { + if (!_fs->exists("/adv_blobs")) { + File file = openWrite(_fs, "/adv_blobs"); + if (file) { + uint8_t zeroes[1 + MAX_ADVERT_PKT_LEN]; + memset(zeroes, 0, sizeof(zeroes)); + for (int i = 0; i < 24; i++) { // pre-allocate to fixed size + file.write(zeroes, sizeof(zeroes)); + } + file.close(); + } + } +} + +uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { + File file = _fs->open("/adv_blobs"); + uint8_t len = 0; // 0 = not found + if (file) { + uint8_t tmp[1 + MAX_ADVERT_PKT_LEN]; + while (file.read(tmp, sizeof(tmp)) == sizeof(tmp)) { + if (memcmp(key, &tmp[1], PUB_KEY_SIZE) == 0) { // public key is first 32 bytes of advert blob + len = tmp[0]; + memcpy(dest_buf, &tmp[1], len); + break; + } + } + file.close(); + } + return len; +} + +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 = _fs->open("/adv_blobs", FILE_O_WRITE); + if (file) { + uint32_t pos = 0, found_pos = 0; + uint32_t min_timestamp = 0xFFFFFFFF; + + // search for matching key OR evict by oldest timestmap + uint8_t tmp[1 + MAX_ADVERT_PKT_LEN]; + while (file.read(tmp, sizeof(tmp)) == sizeof(tmp)) { + if (memcmp(src_buf, &tmp[1], PUB_KEY_SIZE) == 0) { // public key is first 32 bytes of advert blob + found_pos = pos; + break; + } + uint32_t timestamp; + memcpy(×tamp, &tmp[1 + PUB_KEY_SIZE], 4); + if (timestamp < min_timestamp) { + min_timestamp = timestamp; + found_pos = pos; + } + + pos += sizeof(tmp); + } + + file.seek(found_pos); + file.write(&len, 1); + file.write(src_buf, len); + + file.close(); + return true; + } + return false; // error +} +#else +uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { char path[64]; char fname[18]; @@ -279,7 +345,7 @@ int DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[] return 0; // not found } -bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { +bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { char path[64]; char fname[18]; @@ -287,14 +353,7 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); -#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(path); - File f = _fs->open(path, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) - File f = _fs->open(path, "w"); -#else - File f = _fs->open(path, "w", true); -#endif + File f = openWrite(_fs, path); if (f) { int n = f.write(src_buf, len); f.close(); @@ -304,3 +363,4 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src } return false; // error } +#endif diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index d1ed19bf..540bc9cd 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -18,8 +18,11 @@ class DataStore { IdentityStore identity_store; void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + void checkAdvBlobFile(); +#endif - public: +public: DataStore(FILESYSTEM& fs); void begin(); bool formatFileSystem(); @@ -31,6 +34,6 @@ class DataStore { void saveContacts(DataStoreHost* host); void loadChannels(DataStoreHost* host); void saveChannels(DataStoreHost* host); - int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); - bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len); + uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 4c7b1618..054d0cba 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1291,7 +1291,9 @@ void MyMesh::checkCLIRescueCmd() { bool success = _store->formatFileSystem(); if (success) { _store->saveMainIdentity(self_id); + savePrefs(); saveContacts(); + saveChannels(); Serial.println(" > erase and rebuild done"); } else { Serial.println(" Error: erase failed"); From 4b9eac81c6d72fc18302a0999fe36f89639d9cfb Mon Sep 17 00:00:00 2001 From: liamcottle Date: Fri, 6 Jun 2025 21:55:03 +1200 Subject: [PATCH 092/103] fix 150mA power draw on ThinkNode M1 --- src/helpers/ui/buzzer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/ui/buzzer.cpp b/src/helpers/ui/buzzer.cpp index ccc18cd3..c8e5cfcc 100644 --- a/src/helpers/ui/buzzer.cpp +++ b/src/helpers/ui/buzzer.cpp @@ -11,6 +11,7 @@ void genericBuzzer::begin() { quiet(false); pinMode(PIN_BUZZER, OUTPUT); + digitalWrite(PIN_BUZZER, LOW); // need to pull low by default to avoid extreme power draw startup(); } From 9c833486bf3c25b730fd81fc2d7f190f58d1f74c Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 6 Jun 2025 21:35:54 +1000 Subject: [PATCH 093/103] * DataStore, advert blob record format change --- examples/companion_radio/DataStore.cpp | 51 ++++++++++++++++---------- examples/companion_radio/DataStore.h | 3 +- examples/companion_radio/MyMesh.h | 6 +++ examples/companion_radio/main.cpp | 6 +-- variants/t114/platformio.ini | 2 +- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 8e0d119a..a37cfa49 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -1,7 +1,7 @@ #include #include "DataStore.h" -DataStore::DataStore(FILESYSTEM& fs) : _fs(&fs), +DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock), #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) identity_store(fs, "") #elif defined(RP2040_PLATFORM) @@ -252,16 +252,23 @@ void DataStore::saveChannels(DataStoreHost* host) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#define MAX_ADVERT_PKT_LEN (PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE) +#define MAX_ADVERT_PKT_LEN (2 + 32 + PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE) + +struct BlobRec { + uint32_t timestamp; + uint8_t key[7]; + uint8_t len; + uint8_t data[MAX_ADVERT_PKT_LEN]; +}; void DataStore::checkAdvBlobFile() { if (!_fs->exists("/adv_blobs")) { File file = openWrite(_fs, "/adv_blobs"); if (file) { - uint8_t zeroes[1 + MAX_ADVERT_PKT_LEN]; - memset(zeroes, 0, sizeof(zeroes)); - for (int i = 0; i < 24; i++) { // pre-allocate to fixed size - file.write(zeroes, sizeof(zeroes)); + BlobRec zeroes; + memset(&zeroes, 0, sizeof(zeroes)); + for (int i = 0; i < 20; i++) { // pre-allocate to fixed size + file.write((uint8_t *) &zeroes, sizeof(zeroes)); } file.close(); } @@ -271,12 +278,13 @@ void DataStore::checkAdvBlobFile() { uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { File file = _fs->open("/adv_blobs"); uint8_t len = 0; // 0 = not found + if (file) { - uint8_t tmp[1 + MAX_ADVERT_PKT_LEN]; - while (file.read(tmp, sizeof(tmp)) == sizeof(tmp)) { - if (memcmp(key, &tmp[1], PUB_KEY_SIZE) == 0) { // public key is first 32 bytes of advert blob - len = tmp[0]; - memcpy(dest_buf, &tmp[1], len); + BlobRec tmp; + while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { + if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix + len = tmp.len; + memcpy(dest_buf, tmp.data, len); break; } } @@ -296,25 +304,28 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src uint32_t min_timestamp = 0xFFFFFFFF; // search for matching key OR evict by oldest timestmap - uint8_t tmp[1 + MAX_ADVERT_PKT_LEN]; - while (file.read(tmp, sizeof(tmp)) == sizeof(tmp)) { - if (memcmp(src_buf, &tmp[1], PUB_KEY_SIZE) == 0) { // public key is first 32 bytes of advert blob + BlobRec tmp; + file.seek(0); + while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { + if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix found_pos = pos; break; } - uint32_t timestamp; - memcpy(×tamp, &tmp[1 + PUB_KEY_SIZE], 4); - if (timestamp < min_timestamp) { - min_timestamp = timestamp; + if (tmp.timestamp < min_timestamp) { + min_timestamp = tmp.timestamp; found_pos = pos; } pos += sizeof(tmp); } + memcpy(tmp.key, key, sizeof(tmp.key)); // just record 7 byte prefix of key + memcpy(tmp.data, src_buf, len); + tmp.len = len; + tmp.timestamp = _clock->getCurrentTime(); + file.seek(found_pos); - file.write(&len, 1); - file.write(src_buf, len); + file.write((uint8_t *) &tmp, sizeof(tmp)); file.close(); return true; diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 540bc9cd..139131e1 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -15,6 +15,7 @@ public: class DataStore { FILESYSTEM* _fs; + mesh::RTCClock* _clock; IdentityStore identity_store; void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); @@ -23,7 +24,7 @@ class DataStore { #endif public: - DataStore(FILESYSTEM& fs); + DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); void begin(); bool formatFileSystem(); bool loadMainIdentity(mesh::LocalIdentity &identity); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 6ac030d9..43194f09 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -140,6 +140,12 @@ private: void updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len); void addToOfflineQueue(const uint8_t frame[], int len); int getFromOfflineQueue(uint8_t frame[]); + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { + return _store->getBlobByKey(key, key_len, dest_buf); + } + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override { + return _store->putBlobByKey(key, key_len, src_buf, len); + } void checkCLIRescueCmd(); void checkSerialInterface(); diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index b463dcbe..b46cdaab 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -14,13 +14,13 @@ static uint32_t _atoi(const char* sp) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include - DataStore store(InternalFS); + DataStore store(InternalFS, rtc_clock); #elif defined(RP2040_PLATFORM) #include - DataStore store(LittleFS); + DataStore store(LittleFS, rtc_clock); #elif defined(ESP32) #include - DataStore store(SPIFFS); + DataStore store(SPIFFS, rtc_clock); #endif #ifdef ESP32 diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index fd9d6f34..5343e5ad 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -70,7 +70,7 @@ build_flags = -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 +; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 From da5b0f8524aa04be94d4b9106682f344cd85ca5b Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 4 Jun 2025 09:51:16 -0700 Subject: [PATCH 094/103] add basic alert system to companion UI Adds `_alert[80]` which can be set along with `_needs_refresh` to trigger a simple on screen alert that lasts 1s at this time. Implements POC with double press to advert action --- examples/companion_radio/UITask.cpp | 17 ++++++++++++++++- examples/companion_radio/UITask.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index f0f780d6..deeacdad 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -162,7 +162,16 @@ void UITask::renderCurrScreen() { if (_display == NULL) return; // assert() ?? char tmp[80]; - if (_origin[0] && _msg[0]) { // message preview + if (_alert[0]) { + uint16_t textWidth = _display->getTextWidth(_alert); + _display->setCursor((_display->width() - textWidth) / 2, 22); + _display->setTextSize(1.4); + _display->setColor(DisplayDriver::GREEN); + _display->print(_alert); + _alert[0] = 0; + _need_refresh = true; + return; + } else if (_origin[0] && _msg[0]) { // message preview // render message preview _display->setCursor(0, 0); _display->setTextSize(1); @@ -343,11 +352,17 @@ void UITask::handleButtonShortPress() { void UITask::handleButtonDoublePress() { MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); // ADVERT + #ifdef PIN_BUZZER + soundBuzzer(UIEventType::ack); + #endif if (the_mesh.advert()) { MESH_DEBUG_PRINTLN("Advert sent!"); + sprintf(_alert, "Advert sent!"); } else { MESH_DEBUG_PRINTLN("Advert failed!"); + sprintf(_alert, "Advert failed.."); } + _need_refresh = true; } void UITask::handleButtonTriplePress() { diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index fff0bbe1..93a2ef89 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -33,6 +33,7 @@ class UITask { char _version_info[32]; char _origin[62]; char _msg[80]; + char _alert[80]; int _msgcount; bool _need_refresh = true; bool _displayWasOn = false; // Track display state before button press From 0f601752e4e9089a866c2e6d70c439e709c93b94 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 15:23:55 +1200 Subject: [PATCH 095/103] implement ls and cat commands for rescue mode --- examples/companion_radio/DataStore.cpp | 10 ++++++ examples/companion_radio/DataStore.h | 1 + examples/companion_radio/MyMesh.cpp | 48 ++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index a37cfa49..795e38cc 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -42,6 +42,16 @@ void DataStore::begin() { #include #endif +File DataStore::openRead(const char* filename) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return _fs->open(filename, FILE_O_READ); +#elif defined(RP2040_PLATFORM) + return _fs->open(filename, "r"); +#else + return _fs->open(filename, "r", true); +#endif +} + bool DataStore::formatFileSystem() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return _fs->format(); diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 139131e1..201dac01 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -37,4 +37,5 @@ public: void saveChannels(DataStoreHost* host); uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); + File openRead(const char* filename); }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 054d0cba..79910aee 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1305,6 +1305,54 @@ void MyMesh::checkCLIRescueCmd() { } else { Serial.println(" Error: erase failed"); } + } else if (memcmp(cli_command, "ls", 2) == 0) { + + // get path from command e.g: "ls /adafruit" + const char *path = &cli_command[3]; + + // log each file and directory + File root = _store->openRead(path); + File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("[dir] "); + Serial.println(file.name()); + } else { + Serial.print("[file] "); + Serial.print(file.name()); + Serial.print(" ("); + Serial.print(file.size()); + Serial.println(" bytes)"); + } + + // move to next file + file = root.openNextFile(); + + } + + } else if (memcmp(cli_command, "cat", 3) == 0) { + + // get path from command e.g: "cat /contacts3" + const char *path = &cli_command[4]; + + // log file content as hex + File file = _store->openRead(path); + if(file){ + + // get file content + int file_size = file.available(); + uint8_t buffer[file_size]; + file.read(buffer, file_size); + + // print hex + mesh::Utils::printHex(Serial, buffer, file_size); + Serial.print("\n"); + + file.close(); + + } + } else if (strcmp(cli_command, "reboot") == 0) { board.reboot(); // doesn't return } else { From a22c176d45123361523c9d0209056e78dae8ef3b Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 15:44:36 +1200 Subject: [PATCH 096/103] add rm command to remove file --- examples/companion_radio/DataStore.cpp | 4 ++++ examples/companion_radio/DataStore.h | 1 + examples/companion_radio/MyMesh.cpp | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 795e38cc..508b270e 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -52,6 +52,10 @@ File DataStore::openRead(const char* filename) { #endif } +bool DataStore::removeFile(const char* filename) { + return _fs->remove(filename); +} + bool DataStore::formatFileSystem() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return _fs->format(); diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 201dac01..32ccd196 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -38,4 +38,5 @@ public: uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); File openRead(const char* filename); + bool removeFile(const char* filename); }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 79910aee..63fbf142 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1353,6 +1353,19 @@ void MyMesh::checkCLIRescueCmd() { } + } else if (memcmp(cli_command, "rm ", 3) == 0) { + + // get path from command e.g: "rm /adv_blobs" + const char *path = &cli_command[4]; + + // remove file + bool removed = _store->removeFile(path); + if(removed){ + Serial.println("File removed"); + } else { + Serial.println("Failed to remove file"); + } + } else if (strcmp(cli_command, "reboot") == 0) { board.reboot(); // doesn't return } else { From 9d574b2de0a3ba9cd2ef27359d745343c02420fc Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 16:03:04 +1200 Subject: [PATCH 097/103] ensure user isn't removing invalid path --- examples/companion_radio/MyMesh.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 63fbf142..4d3860c3 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1357,13 +1357,20 @@ void MyMesh::checkCLIRescueCmd() { // get path from command e.g: "rm /adv_blobs" const char *path = &cli_command[4]; - - // remove file - bool removed = _store->removeFile(path); - if(removed){ - Serial.println("File removed"); + + // ensure path is not empty, or root dir + if(!path || strlen(path) == 0 || strcmp(path, "/") == 0){ + Serial.println("Invalid path provided"); } else { - Serial.println("Failed to remove file"); + + // remove file + bool removed = _store->removeFile(path); + if(removed){ + Serial.println("File removed"); + } else { + Serial.println("Failed to remove file"); + } + } } else if (strcmp(cli_command, "reboot") == 0) { From a814bfb00be1fabb164c1986fc5c8ab850b7bbeb Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 16:17:45 +1200 Subject: [PATCH 098/103] don't create file when trying to open for read --- 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 508b270e..2ba5ccfb 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -48,7 +48,7 @@ File DataStore::openRead(const char* filename) { #elif defined(RP2040_PLATFORM) return _fs->open(filename, "r"); #else - return _fs->open(filename, "r", true); + return _fs->open(filename, "r", false); #endif } From 7dd7b715cdeb16961ff268d7c403ba8f5f4a0da8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 7 Jun 2025 14:20:59 +1000 Subject: [PATCH 099/103] * enabling _PRIVATE_KEY import/export for ALL companions. --- platformio.ini | 2 ++ variants/generic_espnow/platformio.ini | 2 -- variants/heltec_tracker/platformio.ini | 2 -- variants/heltec_v2/platformio.ini | 2 -- variants/heltec_v3/platformio.ini | 8 -------- variants/lilygo_t3s3/platformio.ini | 4 ---- variants/lilygo_tbeam/platformio.ini | 2 -- variants/lilygo_tbeam_SX1262/platformio.ini | 2 -- variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 2 -- variants/lilygo_tlora_v2_1/platformio.ini | 4 ---- variants/nano_g2_ultra/platformio.ini | 2 -- variants/picow/platformio.ini | 6 ------ variants/promicro/platformio.ini | 4 ---- variants/rak4631/platformio.ini | 6 ------ variants/t114/platformio.ini | 4 ---- variants/techo/platformio.ini | 2 -- variants/thinknode_m1/platformio.ini | 2 -- variants/xiao_nrf52/platformio.ini | 2 -- variants/xiao_s3_wio/platformio.ini | 2 -- 19 files changed, 2 insertions(+), 58 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0c16ead4..2c3ffd83 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,6 +27,8 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=869.525 -D LORA_BW=250 -D LORA_SF=11 + -D ENABLE_PRIVATE_KEY_IMPORT=1 ; NOTE: comment these out for more secure firmware + -D ENABLE_PRIVATE_KEY_EXPORT=1 build_src_filter = +<*.cpp> + diff --git a/variants/generic_espnow/platformio.ini b/variants/generic_espnow/platformio.ini index b3ae7e45..dbc902f0 100644 --- a/variants/generic_espnow/platformio.ini +++ b/variants/generic_espnow/platformio.ini @@ -56,8 +56,6 @@ build_flags = ${Generic_ESPNOW.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; 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/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index ed4cbe28..8d9013a5 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -47,8 +47,6 @@ build_flags = -D BLE_PIN_CODE=123456 ; HWT will use display for pin -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=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 495f20f8..562b309d 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -99,8 +99,6 @@ build_flags = -D BLE_PIN_CODE=0 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=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 28c6d562..e8818fdd 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -91,8 +91,6 @@ build_flags = -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; 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} @@ -112,8 +110,6 @@ build_flags = -D BLE_PIN_CODE=0 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -134,8 +130,6 @@ build_flags = -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' -D WIFI_PWD='"mypwd"' -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -189,8 +183,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index 94ec87af..f3a95e96 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -92,8 +92,6 @@ build_flags = -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; 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} @@ -113,8 +111,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} diff --git a/variants/lilygo_tbeam/platformio.ini b/variants/lilygo_tbeam/platformio.ini index c471e44c..4369243d 100644 --- a/variants/lilygo_tbeam/platformio.ini +++ b/variants/lilygo_tbeam/platformio.ini @@ -33,8 +33,6 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D RADIOLIB_DEBUG_BASIC=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam.build_src_filter} diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 517fc2e0..153fc6fa 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -38,8 +38,6 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D RADIOLIB_DEBUG_BASIC=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=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_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 4e6721f9..bcc51703 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -69,8 +69,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=8 ; -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 5591a400..d9cecfc2 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -72,8 +72,6 @@ build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; 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} @@ -92,8 +90,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 98feb35c..20928bdf 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -41,8 +41,6 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display -D PIN_BUZZER=4 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Nano_G2_Ultra.build_src_filter} diff --git a/variants/picow/platformio.ini b/variants/picow/platformio.ini index ec27e6ee..0e925486 100644 --- a/variants/picow/platformio.ini +++ b/variants/picow/platformio.ini @@ -49,8 +49,6 @@ extends = picow build_flags = ${picow.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${picow.build_src_filter} @@ -65,8 +63,6 @@ lib_deps = ${picow.lib_deps} ; -D MAX_GROUP_CHANNELS=8 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -; ; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${picow.build_src_filter} @@ -82,8 +78,6 @@ lib_deps = ${picow.lib_deps} ; -D WIFI_DEBUG_LOGGING=1 ; -D WIFI_SSID='"myssid"' ; -D WIFI_PWD='"mypwd"' -; ; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${picow.build_src_filter} diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 49771624..e7099d1e 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -103,8 +103,6 @@ build_flags = ${Faketec.build_flags} -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 - -D ENABLE_PRIVATE_KEY_EXPORT=1 - -D ENABLE_PRIVATE_KEY_IMPORT=1 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SSD1306Display ; -D MESH_PACKET_LOGGING=1 @@ -198,8 +196,6 @@ build_flags = ${ProMicroLLCC68.build_flags} -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 - -D ENABLE_PRIVATE_KEY_EXPORT=1 - -D ENABLE_PRIVATE_KEY_IMPORT=1 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index c7f1fa53..3014bc59 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -64,8 +64,6 @@ build_flags = -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} @@ -85,8 +83,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} @@ -108,8 +104,6 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D ENV_INCLUDE_GPS=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index fd9d6f34..4aba03e1 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -72,8 +72,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} @@ -96,8 +94,6 @@ build_flags = -D MAX_GROUP_CHANNELS=8 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index 95c9ff84..8ad5ca03 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -67,8 +67,6 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D DISPLAY_CLASS=GxEPDDisplay -D OFFLINE_QUEUE_SIZE=256 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_Techo.build_src_filter} diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 06910c0a..1a4837d8 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -75,8 +75,6 @@ build_flags = -D DISPLAY_CLASS=GxEPDDisplay -D OFFLINE_QUEUE_SIZE=256 -D PIN_BUZZER=6 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ThinkNode_M1.build_src_filter} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 50baccad..c4934e04 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -65,7 +65,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} @@ -81,7 +80,6 @@ build_flags = ${Xiao_nrf52.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index bb5ae698..4d6fed88 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -85,8 +85,6 @@ build_flags = -D DISPLAY_CLASS=SSD1306Display -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_S3_WIO.build_src_filter} From a50f89f16f1a67b066e3414ae2e6fff4184ecc8b Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 17:38:22 +1200 Subject: [PATCH 100/103] ensure root path is usable --- examples/companion_radio/MyMesh.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 4d3860c3..68e9d615 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1312,8 +1312,9 @@ void MyMesh::checkCLIRescueCmd() { // log each file and directory File root = _store->openRead(path); - File file = root.openNextFile(); - while (file) { + if(root){ + File file = root.openNextFile(); + while (file) { if (file.isDirectory()) { Serial.print("[dir] "); @@ -1329,6 +1330,7 @@ void MyMesh::checkCLIRescueCmd() { // move to next file file = root.openNextFile(); + } } } else if (memcmp(cli_command, "cat", 3) == 0) { From 28edff43fd91038456046a095e2a399a1c78dab6 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 17:42:18 +1200 Subject: [PATCH 101/103] simplify serial print --- examples/companion_radio/MyMesh.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 68e9d615..9c8b96b1 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1317,14 +1317,9 @@ void MyMesh::checkCLIRescueCmd() { while (file) { if (file.isDirectory()) { - Serial.print("[dir] "); - Serial.println(file.name()); + Serial.printf("[dir] %s\n", file.name()); } else { - Serial.print("[file] "); - Serial.print(file.name()); - Serial.print(" ("); - Serial.print(file.size()); - Serial.println(" bytes)"); + Serial.printf("[file] %s (%d bytes)\n", file.name(), file.size()); } // move to next file From 7f79d0c5142a9b43e87d29d1e865c368ca3bf934 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 7 Jun 2025 17:56:20 +1200 Subject: [PATCH 102/103] close roor dir after listing files --- examples/companion_radio/MyMesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 9c8b96b1..2b98ec04 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1326,6 +1326,7 @@ void MyMesh::checkCLIRescueCmd() { file = root.openNextFile(); } + root.close(); } } else if (memcmp(cli_command, "cat", 3) == 0) { From af0c409cbbc0aad2065a80218a97c2981013e92a Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 7 Jun 2025 16:15:17 +1000 Subject: [PATCH 103/103] * ver bump to v1.7.0 --- examples/companion_radio/MyMesh.h | 4 ++-- examples/simple_repeater/main.cpp | 4 ++-- examples/simple_room_server/main.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 43194f09..f6603feb 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -10,11 +10,11 @@ #define FIRMWARE_VER_CODE 5 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "24 May 2025" +#define FIRMWARE_BUILD_DATE "7 Jun 2025" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.6.2" +#define FIRMWARE_VERSION "v1.7.0" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index c2a8ae92..effc177a 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "24 May 2025" + #define FIRMWARE_BUILD_DATE "7 Jun 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.6.2" + #define FIRMWARE_VERSION "v1.7.0" #endif #ifndef LORA_FREQ diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 32fe4a04..400b31dd 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "24 May 2025" + #define FIRMWARE_BUILD_DATE "7 Jun 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.6.2" + #define FIRMWARE_VERSION "v1.7.0" #endif #ifndef LORA_FREQ