Fix: radio stays permanently disabled after an init hang

When the CC1101 init hangs and the watchdog reboots the ESP, the NVS
"radioInit" flag is left false. transceiver_config_t::apply() then did
"if(!radioInit) return;" on every subsequent boot without ever resetting
the flag, so the radio was never re-initialized again. The web UI and the
HA websocket keep working, but no RF is transmitted and shades stop
responding - a reboot does not help because each boot reads false again.
Only a /saveRadio POST recovered it.

This retries init for up to 3 consecutive incomplete boots and only then
gives up (booting cleanly with the radio off to avoid a hard boot loop).
The counter resets on a successful init or a /saveRadio POST.

Also stop advancing the rolling code while the radio is not initialized,
otherwise commands issued during the dead window drift the stored code
ahead of the motor and desync the remote once the radio recovers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rene 2026-07-24 15:43:33 +02:00
parent eb75868adb
commit 7aa869198d

View file

@ -4087,6 +4087,15 @@ bool SomfyShadeController::deleteGroup(uint8_t groupId) {
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); } bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
uint16_t SomfyRemote::getNextRollingCode() { uint16_t SomfyRemote::getNextRollingCode() {
// Do not advance the rolling code when the radio is not initialized. Otherwise
// commands issued while the radio is down (see the radioInit recovery logic in
// transceiver_config_t::apply) keep incrementing the stored code without ever
// transmitting, which drifts it ahead of what the motor expects and desyncs the
// remote once the radio comes back.
if(!somfy.transceiver.config.radioInit) {
Serial.println("WARNING: Radio not initialized - rolling code NOT incremented");
return this->lastRollingCode > 0 ? this->lastRollingCode : 1;
}
pref.begin("ShadeCodes"); pref.begin("ShadeCodes");
uint16_t code = pref.getUShort(this->m_remotePrefId, 0); uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
code++; code++;
@ -4840,6 +4849,7 @@ void transceiver_config_t::save() {
pref.putFloat("rxBandwidth", this->rxBandwidth); // float pref.putFloat("rxBandwidth", this->rxBandwidth); // float
pref.putBool("enabled", this->enabled); pref.putBool("enabled", this->enabled);
pref.putBool("radioInit", true); pref.putBool("radioInit", true);
pref.putUChar("initCrashes", 0); // Saving config is an explicit user action - clear the crash counter.
pref.putChar("txPower", this->txPower); pref.putChar("txPower", this->txPower);
pref.putChar("proto", static_cast<uint8_t>(this->proto)); pref.putChar("proto", static_cast<uint8_t>(this->proto));
@ -4961,14 +4971,31 @@ void transceiver_config_t::apply() {
somfy.transceiver.disableReceive(); somfy.transceiver.disableReceive();
bit_length = this->type; bit_length = this->type;
if(this->enabled) { if(this->enabled) {
bool radioInit = true;
pref.begin("CC1101"); pref.begin("CC1101");
radioInit = pref.getBool("radioInit", true); bool radioInit = pref.getBool("radioInit", true);
// If the radio locks up then we can simply reboot and re-enable the radio. uint8_t initCrashes = pref.getUChar("initCrashes", 0);
pref.putBool("radioInit", false); // If a previous boot hung during CC1101 init the "radioInit" flag was left
// false. The original code returned here without ever resetting the flag, so
// EVERY subsequent boot skipped radio init -> the radio stayed dead until a
// /saveRadio POST. Instead we retry init for up to 3 consecutive crashes and
// only then give up (to avoid a hard boot loop with truly dead hardware).
// The counter is reset on a successful init or a /saveRadio POST.
if(!radioInit) {
initCrashes++;
if(initCrashes >= 3) {
pref.putUChar("initCrashes", initCrashes);
pref.end();
this->radioInit = false;
Serial.printf("CC1101 init skipped after %u consecutive crashes - POST /saveRadio to reset\n", initCrashes);
return;
}
Serial.printf("CC1101 init did not complete last boot (%u/3), retrying...\n", initCrashes);
}
else initCrashes = 0;
pref.putUChar("initCrashes", initCrashes);
pref.putBool("radioInit", false); // Mark init as in-progress; reset to true once it completes.
this->radioInit = false; this->radioInit = false;
pref.end(); pref.end();
if(!radioInit) return;
Serial.print("Applying radio settings "); Serial.print("Applying radio settings ");
Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin); Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
//if(this->TXPin != this->RXPin) //if(this->TXPin != this->RXPin)
@ -5027,8 +5054,9 @@ void transceiver_config_t::apply() {
} }
pref.begin("CC1101"); pref.begin("CC1101");
pref.putBool("radioInit", true); pref.putBool("radioInit", true);
pref.putUChar("initCrashes", 0); // Init completed - clear the crash counter.
pref.end(); pref.end();
} }
else { else {
if(this->radioInit) ELECHOUSE_cc1101.setSidle(); if(this->radioInit) ELECHOUSE_cc1101.setSidle();