From 0263b6632c2f0954e3f529070084e3d949173611 Mon Sep 17 00:00:00 2001 From: hank Date: Mon, 5 May 2025 23:03:14 -0700 Subject: [PATCH 01/16] 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 02/16] 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 03/16] 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 04/16] 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 2f8d9cf96ab06bc24c258d9f77b48465a5ca2613 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 24 May 2025 20:42:00 +1000 Subject: [PATCH 05/16] * 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 06/16] * 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 07/16] * 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 b3d78ac8a798279e45338c2d2b49cf09bb454474 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 26 May 2025 17:18:49 +1000 Subject: [PATCH 08/16] * 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 09/16] * 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 10/16] * 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 11/16] 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 12/16] 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 13/16] * 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 14/16] * 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 15/16] * 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 16/16] * 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; }