From 8ebf3f0bed0fab3f93b86aca96855e54d85c8405 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Sun, 23 Aug 2026 20:13:12 +0200 Subject: [PATCH 1/5] FAQ: Fixed instructions on wiping and resetting nrf52 devices --- docs/faq.md | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index ade59bf0090..f012e133ee3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -691,23 +691,28 @@ You can get the epoch time on and use it to set ### 6.6. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection. **A:** Heltec V3 has a very small coil antenna on its PCB for Wi-Fi and Bluetooth connectivity. It has a very short range, only a few feet. It is possible to remove the coil antenna and replace it with a 31mm wire. The BT range is much improved with the modification. -### 6.7. Q: My RAK/T1000-E/xiao_nRF52 device seems to be corrupted, how do I wipe it clean to start fresh? -**A:** - -1. Connect USB-C cable to your device, per your device's instruction, get it to flash mode: - - For RAK, click the reset button **TWICE** - - For T1000-e, quickly disconnect and reconnect the magnetic side of the cable from the device **TWICE** - - For Heltec T114, click the reset button **TWICE** (the bottom button) - - For Xiao nRF52, click the reset button once. If that doesn't work, quickly double-click the reset button twice. If that doesn't work, disconnect the board from your PC and reconnect again ([seeed studio wiki](https://wiki.seeedstudio.com/XIAO_BLE/#access-the-swd-pins-for-debugging-and-reflashing-bootloader)) -2. A new folder will appear on your computer's desktop -3. Download the `flash_erase*.uf2` file for your device on - - RAK WisBlock and Heltec T114: `Flash_erase-nRF32_softdevice_v6.uf2` - - Seeed Studio Xiao nRF52 WIO: `Flash_erase-nRF52_softdevice_v7.uf2` -4. drag and drop the uf2 file for your device to the root of the new folder -5. Wait for the copy to complete. You might get an error dialog, you can ignore it -6. Go to , click `Console` and select the serial port for your connected device -7. In the console, press enter. Your flash should now be erased -8. You may now flash the latest MeshCore firmware onto your device +### 6.7. Q: My RAK/T1000-E/xiao_nRF52/... device seems to be corrupted, how do I wipe it clean to start fresh? +**A: If you're able to connect to the device from your MeshCore app** +1. Navigate to Gear icon(Settings) on the upper right corner +2. Click on `Export Settings` button, choose `Select All` and Confirm. This will save your Node configuration +3. Now choose `Factory Reset` option +4. Confirm that you want to reset your device +5. Go to Bluetooth System settings and remove pairing of the device, so you can connect again +6. Connect your device in the app and enter the PIN +7. Navigate to Gear icon(Settings) on the upper right corner +8. Click on `Import Settings` and choose the file from 2., click on `Select All` and Confirm +9. Find `Reboot` button on of the Settings list + +**B: You're not able to connect to the App** +1. Connect USB cable to your device +2. Go to https://flasher.meshcore.io +3. Search for your device in the list +4. Choose `Companion Bluetooth` +5. Press `Enter DFU mode` button, choose your USB device +6. Press `Erase Flash` button, choose the device again and wait until it completes +7. Press `Flash!` button and choose the USB device last time +8. The device is erased and newest firmware is installed +9. You might need to remove the pairing in Bluetooth System Settings in order to re-pair the app again. Separately, starting in firmware version 1.7.0, there is a CLI Rescue mode. If your device has a user button (e.g. some RAK, T114), you can activate the rescue mode by holding down the user button of the device within 8 seconds of boot. Then you can use the 'Console' on @@ -716,9 +721,9 @@ Separately, starting in firmware version 1.7.0, there is a CLI Rescue mode. If y `NetworkError: Failed to execute 'open' on 'SerialPort': Failed to open serial port.` -Allow the browser user on it: +Allow user access on your USB port: -`# setfacl -m u:YOUR_USER_HERE:rw /dev/ttyUSB0` +`sudo setfacl -m u:$USER:rw /dev/ttyUSB0` --- From 7f83f62e5d9cbb3430ae6774121c83d4a67cf253 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Fri, 18 Sep 2026 16:59:26 +0200 Subject: [PATCH 2/5] docs: fix frame format tables --- docs/kiss_modem_protocol.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/kiss_modem_protocol.md b/docs/kiss_modem_protocol.md index 3f4dbf9c4bf..7ff48786840 100644 --- a/docs/kiss_modem_protocol.md +++ b/docs/kiss_modem_protocol.md @@ -18,10 +18,10 @@ Standard KISS framing per the KA9Q/K3MC specification. | `0xDD` | TFESC | Escaped FESC (FESC + TFESC = 0xDB) | ``` -┌──────┬───────────┬──────────────┬──────┐ -│ FEND │ Type Byte │ Data (escaped)│ FEND │ -│ 0xC0 │ 1 byte │ 0-510 bytes │ 0xC0 │ -└──────┴───────────┴──────────────┴──────┘ +┌──────┬───────────┬────────────────┬──────┐ +│ FEND │ Type Byte │ Data (escaped) │ FEND │ +│ 0xC0 │ 1 byte │ 0-510 bytes │ 0xC0 │ +└──────┴───────────┴────────────────┴──────┘ ``` ### Type Byte @@ -82,10 +82,10 @@ MeshCore-specific functionality uses the standard KISS SetHardware command. The ### Frame Format ``` -┌──────┬──────┬─────────────┬──────────────┬──────┐ -│ FEND │ 0x06 │ Sub-command │ Data (escaped)│ FEND │ -│ 0xC0 │ │ 1 byte │ variable │ 0xC0 │ -└──────┴──────┴─────────────┴──────────────┴──────┘ +┌──────┬──────┬─────────────┬────────────────┬──────┐ +│ FEND │ 0x06 │ Sub-command │ Data (escaped) │ FEND │ +│ 0xC0 │ │ 1 byte │ variable │ 0xC0 │ +└──────┴──────┴─────────────┴────────────────┴──────┘ ``` ### Request Sub-commands (Host to TNC) From 389d695aac14a103d3ca51b7593f21a03a6792ce Mon Sep 17 00:00:00 2001 From: liamcottle Date: Wed, 30 Sep 2026 14:52:58 +1300 Subject: [PATCH 3/5] add radio preset docs --- docs/radio_presets.md | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/radio_presets.md diff --git a/docs/radio_presets.md b/docs/radio_presets.md new file mode 100644 index 00000000000..ea3b07e51f8 --- /dev/null +++ b/docs/radio_presets.md @@ -0,0 +1,66 @@ +# Radio Presets + +The radio presets are currently stored on the MeshCore App API server. + +This allows us to update the presets without releasing new application updates. + +The presets can be fetched from [https://api.meshcore.nz/api/v1/config](https://api.meshcore.nz/api/v1/config) + +If you'd like a new radio preset to be added, this will need to be brought up for discussion in the [MeshCore Discord](https://meshcore.gg) server, or via the [issues section](https://github.com/meshcore-dev/MeshCore/issues) in our GitHub repo. + +Before a new preset will be considered for being added, local radio regulations must be researched by the person suggesting the preset, and investigation of the frequencies with an SDR is advised to ensure the least interference to provide a decent user experience. + +Once a preset is added, it's unlikely to be changed. Adding too many presets can lead to user frustration, as new users won't know which preset to select during setup. + +At this time, presets provided by the MeshCore App API are just suggestions, but you are free to configure any radio settings through the companion applications, provided they comply with local laws/regulations. + +> Note: We are planning to implement deep linking such as meshcore:// URLs, and QR codes so local mesh networks can easily distribute their own settings, without relying on the internet, or all the companion applications to provide all the settings. + +## JSON Response + +See the example JSON response below, which shows the expected format provided by the API server. + +Your application should be programmed to expect any of the JSON keys to be missing from the response, as the format could change at any time without any notification. + +Please configure an appropriate User Agent header string when sending your request to the server. We should be able to reach out to you if needed. + +> Note: Some presets contain an optional `network_settings` section which defines the `path_hash_size` to use for this mesh network. + +```json +{ + "config": { + "suggested_radio_settings": { + "info_message": "These presets are suggested by the community.", + "entries": [ + { + "title": "Australia (Narrow)", + "description": "916.575MHz / SF7 / BW62.5 / CR7", + "frequency": "916.575", + "spreading_factor": "7", + "bandwidth": "62.5", + "coding_rate": "7" + }, + { + "title": "New Zealand (Narrow)", + "description": "917.375MHz / SF7 / BW62.5 / CR5 / 2B", + "frequency": "917.375", + "spreading_factor": "7", + "bandwidth": "62.5", + "coding_rate": "5", + "network_settings": { + "path_hash_size": 2 + } + }, + { + "title": "Portugal 868", + "description": "869.618MHz / SF7 / BW62.5 / CR6", + "frequency": "869.618", + "spreading_factor": "7", + "bandwidth": "62.5", + "coding_rate": "6" + } + ] + } + } +} +``` \ No newline at end of file From cd9b43bb4bf62238b407b92ec11c807eaaa9463f Mon Sep 17 00:00:00 2001 From: ctrlaltca Date: Sat, 21 Mar 2026 16:41:06 +0100 Subject: [PATCH 4/5] Promicro power management stage 1 --- docs/nrf52_power_management.md | 2 +- variants/promicro/PromicroBoard.cpp | 35 ++++++++++++++++++++++++++++- variants/promicro/PromicroBoard.h | 6 +++++ variants/promicro/platformio.ini | 1 + variants/promicro/variant.h | 4 ++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/nrf52_power_management.md b/docs/nrf52_power_management.md index 417ee6dd63b..72899412289 100644 --- a/docs/nrf52_power_management.md +++ b/docs/nrf52_power_management.md @@ -41,7 +41,7 @@ Shutdown reason codes (stored in GPREGRET2): | RAK4631 (`rak4631`) | Yes | Yes | Yes | | Heltec T114 (`heltec_t114`) | Yes | Yes | Yes | | GAT562 Mesh Watch13 | Yes | Yes | Yes | -| Promicro nRF52840 | No | No | No | +| Promicro nRF52840 | Yes | Yes | Yes | | RAK WisMesh Tag | No | No | No | | Heltec Mesh Solar | No | No | No | | LilyGo T-Echo / T-Echo Lite | No | No | No | diff --git a/variants/promicro/PromicroBoard.cpp b/variants/promicro/PromicroBoard.cpp index aba808786a5..8935e79dd71 100644 --- a/variants/promicro/PromicroBoard.cpp +++ b/variants/promicro/PromicroBoard.cpp @@ -3,6 +3,36 @@ #include "PromicroBoard.h" +void PromicroBoard::powerOff() { +#ifdef NRF52_POWER_MANAGEMENT + shutdownPeripherals(); + initiateShutdown(SHUTDOWN_REASON_USER); +#else + NRF52Board::powerOff(); +#endif +} + +#ifdef NRF52_POWER_MANAGEMENT +// Static configuration for power management +// Values come from variant.h defines +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + +void PromicroBoard::initiateShutdown(uint8_t reason) { + digitalWrite(SX126X_POWER_EN, LOW); + + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + void PromicroBoard::begin() { NRF52Board::begin(); @@ -19,6 +49,9 @@ void PromicroBoard::begin() { Wire.begin(); pinMode(SX126X_POWER_EN, OUTPUT); + #ifdef NRF52_POWER_MANAGEMENT + checkBootVoltage(&power_config); + #endif digitalWrite(SX126X_POWER_EN, HIGH); delay(10); // give sx1262 some time to power up -} \ No newline at end of file +} diff --git a/variants/promicro/PromicroBoard.h b/variants/promicro/PromicroBoard.h index 605b0365218..54f20124172 100644 --- a/variants/promicro/PromicroBoard.h +++ b/variants/promicro/PromicroBoard.h @@ -47,4 +47,10 @@ class PromicroBoard : public NRF52BoardDCDC { const char* getManufacturerName() const override { return "ProMicro DIY"; } + + void powerOff() override; + + #ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; + #endif }; diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index da27c2fab20..2b8f68c74fc 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -7,6 +7,7 @@ build_flags = ${nrf52_base.build_flags} -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper + -D NRF52_POWER_MANAGEMENT -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 diff --git a/variants/promicro/variant.h b/variants/promicro/variant.h index e8a7833169b..f1f1d701ea1 100644 --- a/variants/promicro/variant.h +++ b/variants/promicro/variant.h @@ -25,6 +25,10 @@ #define BATTERY_PIN (17) #define ADC_RESOLUTION 12 +#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +#define PWRMGT_LPCOMP_AIN 7 // AIN channel for voltage sensing +#define PWRMGT_LPCOMP_REFSEL 4 // REFSEL 4/8 equal voltage divider resistances + //////////////////////////////////////////////////////////////////////////////// // Number of pins From 4dd313ad46c0d6dd6906c4d86534c246d778afa3 Mon Sep 17 00:00:00 2001 From: ctrlaltca Date: Thu, 12 Nov 2026 21:57:18 +0100 Subject: [PATCH 5/5] Promicro power management stage 2.3 --- docs/nrf52_power_management.md | 103 +++++++++++++++++++++++++++- src/helpers/NRF52Board.cpp | 9 +-- variants/promicro/PromicroBoard.cpp | 37 ++++++++++ variants/promicro/PromicroBoard.h | 14 +++- variants/promicro/variant.h | 13 +++- 5 files changed, 166 insertions(+), 10 deletions(-) diff --git a/docs/nrf52_power_management.md b/docs/nrf52_power_management.md index 72899412289..227cc7d4850 100644 --- a/docs/nrf52_power_management.md +++ b/docs/nrf52_power_management.md @@ -57,7 +57,20 @@ Shutdown reason codes (stored in GPREGRET2): | Minewsemi ME25LS01 | No | No | No | Notes: -- "Implemented" reflects Phase 1 (boot lockout + shutdown reason capture). +- Promicro also checks battery voltage every 5 seconds while running. Three + consecutive readings below 3100 mV trigger peripheral shutdown and SYSTEMOFF + with LPCOMP recovery enabled. USB power bypasses this runtime check. +- On Promicro, `PWRMGT_VOLTAGE_BOOTLOCK` and `PWRMGT_VOLTAGE_SHUTDOWN` are ADC + thresholds, not the LPCOMP wake threshold. The retained empirical setting + `PWRMGT_LPCOMP_REFSEL=4` selects 5/8 VDD; measure VDD and the battery divider + to determine the actual recovery voltage. SYSTEMOFF does not disconnect the + battery from the board's other loads. +- Promicro releases radio SPI, reset, RF-switch and status pins to inputs + without pulls before disabling external VCC, to avoid feeding the unpowered + module through GPIO signals. Runtime shutdown also stops SPI first; boot + lockout skips SPI uninitialization because SPI has not been started yet. +- "Implemented" reflects Phase 1 (boot lockout + shutdown reason capture), + with the additional runtime cutoff described above for Promicro. - User power-off on Heltec T114 does not enable LPCOMP wake. - VBUS detection is used to skip boot lockout on external power, and VBUS wake is configured alongside LPCOMP when supported hardware exposes VBUS to the nRF52. @@ -138,7 +151,7 @@ The LPCOMP (Low Power Comparator) is configured to: - Monitor the specified AIN channel (0-7 corresponding to P0.02-P0.05, P0.28-P0.31) - Compare against VDD fraction reference (REFSEL: 0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16) - Detect UP events (voltage rising above threshold) -- Use 50mV hysteresis for noise immunity +- Use no comparator hysteresis (as of firmware 1.18) - Wake the device from SYSTEMOFF when triggered VBUS wake is enabled via the POWER peripheral USBDETECTED event whenever `configureVoltageWake()` is used. This requires USB VBUS to be routed to the nRF52 (typical on nRF52840 boards with native USB). @@ -199,6 +212,90 @@ does not override them reports `Not available` rather than an error. ESP32 boards override the reset half with `esp_reset_reason()`, so they return a real reset reason and `Not available` for the shutdown reason. +## Promicro / Faketec hardware and configuration + +The standard Promicro environments use the bench-tested battery divider below. +Replace both original `1002` (10 kOhm) resistors before using this configuration: + +```text +BAT+ --- 160 kOhm ---+--- P0.31 / AIN7 (Arduino pin 17) + | + 240 kOhm + | + GND +``` + +Confirm which resistor connects to BAT+ and which connects to GND before +replacement. This is a hardware-specific configuration, not a firmware-only +upgrade for an unchanged 10k/10k board. The divider draws approximately 8.25 uA +at 3.3 V, versus 165 uA for the original divider. Resistor tolerances and ADC +errors still require checking the reported voltage against a meter. + +| Setting | Default | +| --- | --- | +| ADC multiplier | 1.46484375 mV/count = (3600 / 4096) x (160 + 240) / 240 | +| ADC resolution / acquisition | 12 bits / 40 us | +| Samples per voltage reading | 8 | +| Boot lock / runtime shutdown | 3100 mV / 3100 mV | +| Runtime monitoring | Every 5 s; three consecutive low readings | +| LPCOMP input / reference | AIN7 / REFSEL=4, 5/8 VDD | + +Any saved `adc.multiplier` overrides the compiled default after preferences +are loaded, while the early boot check uses the compiled default. After the +hardware/firmware update, run `set adc.multiplier 0` to restore the board default +and compare the reading with a meter before testing shutdown. Only save a new +calibration after checking several voltages. + +Runtime shutdown first stops peripherals. Before disabling external VCC via +P0.13, the board stops SPI and releases radio SPI, NSS, RESET, BUSY, DIO1 and +RXEN pins to inputs without pulls. GPIO state is retained in SYSTEMOFF; leaving +NSS/RESET driven high was consistent with backfeeding the unpowered radio. +Boot lockout applies the same pin isolation but skips SPI uninitialization, +because SPI has not been initialized at that point. + +LPCOMP and VBUS recovery are armed for low-voltage and boot-protection shutdowns. +Manual `powerOff()` remains a user shutdown without LPCOMP recovery. USB power +bypasses the boot voltage check and runtime low-voltage shutdown. + +### Bench validation (2026-11-13) + +Results reported on one Promicro/Faketec assembly using a bench supply, with +battery disconnected. The final divider test used the repeater without debug +logging and with GPS disabled. Standard environments retain their existing GPS +configuration; GPS operation and additional peripheral loads were not validated +by these tests. + +| Measurement / test | Observed result | +| --- | --- | +| Final 160k/240k divider, REFSEL=4, firmware threshold 3100 mV | Shutdown at 3.08 V actual supply voltage | +| Recovery with the same configuration | Wake at 3.41 V actual supply voltage | +| Margin between observed shutdown and recovery | Approximately 330 mV | +| Battery voltage indication | Approximately 10 mV difference from supply/meter | +| Radio VCC and RESET after shutdown pin isolation | Fell from approximately 2.56 V to 0.02 V | +| P0.13 after shutdown pin isolation | 0 V (previously approximately 0.52 V) | +| Total shutdown current | Supply displayed 0.000 A, below its approximately 1 mA resolution; not a measured zero-current result | +| Earlier shutdown current before pin isolation | Approximately 7 mA | + +Diagnostic logs confirmed `Wake from LPCOMP` (RESETREAS 0x20000), previous +shutdown `Low Voltage` (0x4C), and successful radio initialization after wake. +VBUS wake was also reported working during the earlier diagnostic tests. Repeat +LPCOMP and VBUS tests after flashing the cleaned standard build; no hardware +validation of that rebuilt artifact is implied by successful compilation. + +With the earlier 150k/240k divider, REFSEL=4 recovered at 3.11-3.15 V, leaving +only 40-60 mV of observed margin. REFSEL=13 (11/16 VDD) raised recovery to +3.6-3.7 V. The final 160k/240k divider with REFSEL=4 provided the preferred +observed margin. LPCOMP compares against a fraction of VDD, not an absolute +battery reference: measure recovery on each hardware revision rather than +assuming an exact threshold from indirect VDD measurements. + +For repeat tests, remove board USB and the UART adapter, lower the supply slowly +and allow at least 20 s at each step, then raise it slowly without interrupting +power. Measure actual voltage, current and wake stability under load. When +checking VBUS wake, ensure USB charging cannot drive reverse current into the +bench supply. SYSTEMOFF reduces consumption but does not physically disconnect +the battery or replace hardware battery protection. + ## Debug Output When `MESH_DEBUG=1` is enabled, the power management module outputs: @@ -210,7 +307,7 @@ DEBUG: PWRMGT: LPCOMP wake configured (AIN7, ref=3/8 VDD) ## Phase 2 (Planned) -- Runtime voltage monitoring +- General runtime voltage monitoring (Promicro already implements the fixed policy above) - Voltage state machine (Normal -> Warning -> Critical -> Shutdown) - Configurable thresholds - Load shedding callbacks for power reduction diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index f2fa7743a73..07fee86f609 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -192,13 +192,14 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t lpcomp_refsel NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Disabled; // Select analog input (AIN0-7 maps to PSEL 0-7) - NRF_LPCOMP->PSEL = ((uint32_t)PWRMGT_LPCOMP_AIN << LPCOMP_PSEL_PSEL_Pos) & LPCOMP_PSEL_PSEL_Msk; + NRF_LPCOMP->PSEL = ((uint32_t)ain_channel << LPCOMP_PSEL_PSEL_Pos) & LPCOMP_PSEL_PSEL_Msk; // Reference: REFSEL (0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16) NRF_LPCOMP->REFSEL = ((uint32_t)lpcomp_refsel << LPCOMP_REFSEL_REFSEL_Pos) & LPCOMP_REFSEL_REFSEL_Msk; // Detect UP events (voltage rises above threshold for battery recovery) NRF_LPCOMP->ANADETECT = LPCOMP_ANADETECT_ANADETECT_Up; + NRF_LPCOMP->HYST = LPCOMP_HYST_HYST_NoHyst; // Clear stale events/interrupts before enabling wake NRF_LPCOMP->EVENTS_READY = 0; @@ -219,12 +220,12 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t lpcomp_refsel } if (lpcomp_refsel == 7) { - MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=ARef)", PWRMGT_LPCOMP_AIN); + MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=ARef)", ain_channel); } else if (lpcomp_refsel <= 6) { - MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/8 VDD)", PWRMGT_LPCOMP_AIN, lpcomp_refsel + 1); + MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/8 VDD)", ain_channel, lpcomp_refsel + 1); } else { uint8_t ref_num = (uint8_t)((lpcomp_refsel - 8) * 2 + 1); - MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/16 VDD)", PWRMGT_LPCOMP_AIN, ref_num); + MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/16 VDD)", ain_channel, ref_num); } } diff --git a/variants/promicro/PromicroBoard.cpp b/variants/promicro/PromicroBoard.cpp index 8935e79dd71..213fdd6cbd5 100644 --- a/variants/promicro/PromicroBoard.cpp +++ b/variants/promicro/PromicroBoard.cpp @@ -1,8 +1,31 @@ #include #include +#include #include "PromicroBoard.h" +void PromicroBoard::loop() { +#ifdef NRF52_POWER_MANAGEMENT + const uint32_t now = millis(); + if ((uint32_t)(now - last_battery_check) < PWRMGT_BATTERY_CHECK_INTERVAL_MS) return; + last_battery_check = now; + + if (isExternalPowered()) { + low_battery_samples = 0; + return; + } + + // Require consecutive low readings to reject brief load transients. + const uint16_t mv = getBattMilliVolts(); + if (mv >= PWRMGT_VOLTAGE_SHUTDOWN) { + low_battery_samples = 0; + } else if (++low_battery_samples >= PWRMGT_LOW_BATTERY_SAMPLES) { + shutdownPeripherals(); + initiateShutdown(SHUTDOWN_REASON_LOW_VOLTAGE); + } +#endif +} + void PromicroBoard::powerOff() { #ifdef NRF52_POWER_MANAGEMENT shutdownPeripherals(); @@ -22,7 +45,21 @@ const PowerMgtConfig power_config = { }; void PromicroBoard::initiateShutdown(uint8_t reason) { + // Disconnect signal drivers before removing the external radio supply. + // SYSTEMOFF retains GPIO configuration: NSS/RESET left high can otherwise + // feed the unpowered module through its signal pins. + // Boot protection runs before SPI is initialized; uninit is invalid there. + if (reason != SHUTDOWN_REASON_BOOT_PROTECT) SPI.end(); + const uint8_t radio_pins[] = { + P_LORA_NSS, P_LORA_RESET, P_LORA_SCLK, P_LORA_MOSI, + P_LORA_MISO, P_LORA_BUSY, P_LORA_DIO_1, SX126X_RXEN + }; + for (uint8_t pin : radio_pins) { + pinMode(pin, INPUT); // High impedance, no pull-up or pull-down. + } + // Preload the low latch before enabling the output, including boot lockout. digitalWrite(SX126X_POWER_EN, LOW); + pinMode(SX126X_POWER_EN, OUTPUT); if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || reason == SHUTDOWN_REASON_BOOT_PROTECT) { diff --git a/variants/promicro/PromicroBoard.h b/variants/promicro/PromicroBoard.h index 54f20124172..fc08b593589 100644 --- a/variants/promicro/PromicroBoard.h +++ b/variants/promicro/PromicroBoard.h @@ -5,20 +5,32 @@ #include #define PIN_VBAT_READ 17 -#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking +#ifndef ADC_MULTIPLIER + #define ADC_MULTIPLIER (1.46484375f) // 160k BAT+ / 240k GND, 3.6V ADC full scale. +#endif +#ifndef PROMICRO_ADC_SAMPLE_TIME_US + #define PROMICRO_ADC_SAMPLE_TIME_US 40 +#endif class PromicroBoard : public NRF52BoardDCDC { protected: float adc_mult = ADC_MULTIPLIER; +#ifdef NRF52_POWER_MANAGEMENT + uint32_t last_battery_check = 0; + uint8_t low_battery_samples = 0; +#endif public: PromicroBoard() : NRF52Board("ProMicro_OTA") {} void begin(); + void loop() override; #define BATTERY_SAMPLES 8 uint16_t getBattMilliVolts() override { + analogReference(AR_DEFAULT); analogReadResolution(12); + analogSampleTime(PROMICRO_ADC_SAMPLE_TIME_US); uint32_t raw = 0; for (int i = 0; i < BATTERY_SAMPLES; i++) { diff --git a/variants/promicro/variant.h b/variants/promicro/variant.h index f1f1d701ea1..01a88500c9f 100644 --- a/variants/promicro/variant.h +++ b/variants/promicro/variant.h @@ -25,9 +25,18 @@ #define BATTERY_PIN (17) #define ADC_RESOLUTION 12 -#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +#ifndef PWRMGT_VOLTAGE_BOOTLOCK + #define PWRMGT_VOLTAGE_BOOTLOCK 3100 // Won't boot below this voltage (mV) +#endif +#ifndef PWRMGT_VOLTAGE_SHUTDOWN + #define PWRMGT_VOLTAGE_SHUTDOWN 3100 // Runtime low-battery cutoff (mV) +#endif +#define PWRMGT_BATTERY_CHECK_INTERVAL_MS 5000 +#define PWRMGT_LOW_BATTERY_SAMPLES 3 #define PWRMGT_LPCOMP_AIN 7 // AIN channel for voltage sensing -#define PWRMGT_LPCOMP_REFSEL 4 // REFSEL 4/8 equal voltage divider resistances +#ifndef PWRMGT_LPCOMP_REFSEL + #define PWRMGT_LPCOMP_REFSEL 4 // 5/8 VDD; wake depends on VDD and divider +#endif //////////////////////////////////////////////////////////////////////////////// // Number of pins