feat(power): 1Hz chunked light-sleep; meter backoff; log throttling

- Replace delay() with light_sleep_chunked_ms() in sender idle path
  (100ms chunks preserve UART FIFO safety at 9600 baud)
- Add ENABLE_LIGHT_SLEEP_IDLE build flag (default: on, fallback: =0)
- Meter reader task: exponential backoff on consecutive poll failures
  (METER_FAIL_BACKOFF_BASE_MS..MAX_MS) to reduce idle Core-0 wakeups
- Configurable SENDER_DIAG_LOG_INTERVAL_MS (5s debug / 30s prod)
- Configurable METER_FRAME_TIMEOUT_CFG_MS, SENDER_CPU_MHZ
- New PlatformIO envs: lowpower, 868-lowpower, lowpower-debug
- Add docs/POWER_OPTIMIZATION.md with measurement plan and Go/No-Go
This commit is contained in:
2026-03-16 16:32:49 +01:00
parent 99aae76404
commit b9591ce9bb
7 changed files with 489 additions and 20 deletions

View File

@@ -9,7 +9,7 @@ static constexpr float BATTERY_DIVIDER = 2.0f;
static constexpr float ADC_REF_V = 3.3f;
void power_sender_init() {
setCpuFrequencyMhz(80);
setCpuFrequencyMhz(SENDER_CPU_MHZ);
WiFi.mode(WIFI_OFF);
esp_wifi_stop();
esp_wifi_deinit();
@@ -117,6 +117,33 @@ void light_sleep_ms(uint32_t ms) {
esp_light_sleep_start();
}
void light_sleep_chunked_ms(uint32_t total_ms, uint32_t chunk_ms) {
if (total_ms == 0) {
return;
}
if (chunk_ms == 0) {
chunk_ms = total_ms;
}
uint32_t start = millis();
for (;;) {
uint32_t elapsed = millis() - start;
if (elapsed >= total_ms) {
break;
}
uint32_t remaining = total_ms - elapsed;
uint32_t this_chunk = remaining > chunk_ms ? chunk_ms : remaining;
if (this_chunk < 10) {
// Light-sleep overhead (~1 ms save/restore) not worthwhile for tiny slices.
delay(this_chunk);
break;
}
light_sleep_ms(this_chunk);
// After wake the FreeRTOS scheduler runs higher-priority tasks (e.g. the
// meter_reader_task on Core 0) before returning here, so the UART HW FIFO
// is drained automatically between chunks.
}
}
void go_to_deep_sleep(uint32_t seconds) {
esp_sleep_enable_timer_wakeup(static_cast<uint64_t>(seconds) * 1000000ULL);
esp_deep_sleep_start();