From c85cc0a5edc25fe64fbbaea2b50acd9cddbae5ff Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Oct 2026 17:56:47 +0000 Subject: [PATCH] Latch the LoRa IRQ skip-sleep give-up until the line is seen low doLightSleep() skips a light sleep when the LoRa IRQ line is already asserted, and bounds that to LORA_IRQ_SKIP_SLEEP_MAX consecutive skips so a line stuck high cannot stop the node sleeping for good. But the counter was reset on the sleep it finally allowed, so the bound never stuck: a genuinely stuck-high line produced five skips, one sleep, five skips, one sleep, for as long as it stayed high. Each of those skipped attempts is not free. It runs waitEnterSleep() and the full notifyLightSleep / notifyLightSleepEnd observer pass, which detaches, arms and restores every wake pin - the path #12100 and #12131's interrupt storm live on - so the loop costs far more than the sleep it is protecting. Reset the counter only when the line is actually read low, which is the condition that says the radio is clearing its IRQs again. Nothing changes for the transient case the skip exists for: the line goes low as soon as the main loop services the packet, and the next sleep clears the count. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GTiifXrVc4YdonmLRCeWhw --- src/sleep.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sleep.cpp b/src/sleep.cpp index 6ba5be5bd3..0017030ff9 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -579,7 +579,8 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r assert(res == ESP_OK); console->flush(); - const bool loraIrqPending = loraWakeIrqAsserted() && loraIrqSkippedSleeps < LORA_IRQ_SKIP_SLEEP_MAX; + const bool loraIrqAsserted = loraWakeIrqAsserted(); + const bool loraIrqPending = loraIrqAsserted && loraIrqSkippedSleeps < LORA_IRQ_SKIP_SLEEP_MAX; if (loraIrqPending) { // A LoRa IRQ is already pending (see loraWakeIrqAsserted). Don't sleep on top of it - return so // the main loop services the packet now instead of stranding it until the next wake. @@ -591,7 +592,13 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r } else { // Either nothing pending, or the line has stayed high past LORA_IRQ_SKIP_SLEEP_MAX and the main // loop is clearly not going to clear it. Never sleeping costs more than one stranded packet. - loraIrqSkippedSleeps = 0; + // + // Only reset the count once the line is seen low again. Resetting on every sleep we allow made a + // stuck-high line give LORA_IRQ_SKIP_SLEEP_MAX skips, one sleep, repeat, forever - and each + // skipped attempt still runs waitEnterSleep() and the whole notifyLightSleep/End pass, which + // detaches, arms and restores every wake pin. Once we have given up, stay given up. + if (!loraIrqAsserted) + loraIrqSkippedSleeps = 0; res = esp_light_sleep_start(); } if (res != ESP_OK) {