From b60a1c04200c1b96113adfcbcf8235aa0881dc4f Mon Sep 17 00:00:00 2001 From: DatanoiseTV <6614616+DatanoiseTV@users.noreply.github.com> Date: Fri, 8 May 2026 21:32:23 +0200 Subject: [PATCH] radio: return real status from reconfigure() and stop panicking on SPI errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reconfigure() in all five chip drivers (SX126x, SX128x, LR11x0, LR20x0, RF95) was returning true unconditionally, even when individual SPI setters failed. The caller in RadioInterface::cppInit() reboots the device when reconfigure returns false, so the failure-recovery path was silently disabled. Track failures in a local bool and return its accumulated state. Same drivers also asserted on remote SPI calls in reconfigure() and setStandby() — a momentary glitch on the SPI bus would panic-reset the firmware. Replaced with log-and-continue; the new return value lets the caller make the reboot decision instead of crashing inline. --- src/mesh/LR11x0Interface.cpp | 40 +++++++++++++++++++++++--------- src/mesh/LR20x0Interface.cpp | 40 +++++++++++++++++++++++--------- src/mesh/RF95Interface.cpp | 42 ++++++++++++++++++++++----------- src/mesh/SX126xInterface.cpp | 45 +++++++++++++++++++++++------------- src/mesh/SX128xInterface.cpp | 39 ++++++++++++++++++++----------- 5 files changed, 142 insertions(+), 64 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 1d1616ed673..8f8521758d8 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -173,21 +173,33 @@ template bool LR11x0Interface::reconfigure() // set mode to standby setStandby(); - // configure publicly accessible settings + // The caller reboots the device when reconfigure returns false; track + // every SPI setter so we don't silently report success after a glitch. + bool ok = true; + int err = lora.setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setBandwidth(bw, wideLora() && (getFreq() > 1000.0f)); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setSyncWord(syncWord); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR11x0 setSyncWord %s%d", radioLibErr, err); + ok = false; + } if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range limitPower(LR1120_MAX_POWER); @@ -196,14 +208,22 @@ template bool LR11x0Interface::reconfigure() } err = lora.setPreambleLength(preambleLength); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR11x0 setPreambleLength %s%d", radioLibErr, err); + ok = false; + } err = lora.setFrequency(getFreq()); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setOutputPower(power); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR11x0 setOutputPower %s%d", radioLibErr, err); + ok = false; + } // Apply RX gain mode — valid in STDBY, matches resetAGC() pattern err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain); @@ -212,7 +232,7 @@ template bool LR11x0Interface::reconfigure() startReceive(); // restart receiving - return true; + return ok; } template void LR11x0Interface::disableInterrupt() @@ -230,8 +250,6 @@ template void LR11x0Interface::setStandby() LOG_DEBUG("LR11x0 standby failed with error %d", err); } - assert(err == RADIOLIB_ERR_NONE); - isReceiving = false; // If we were receiving, not any more activeReceiveStart = 0; disableInterrupt(); diff --git a/src/mesh/LR20x0Interface.cpp b/src/mesh/LR20x0Interface.cpp index 699663cf032..ed2fe5628c8 100644 --- a/src/mesh/LR20x0Interface.cpp +++ b/src/mesh/LR20x0Interface.cpp @@ -201,21 +201,33 @@ template bool LR20x0Interface::reconfigure() // set mode to standby setStandby(); - // configure publicly accessible settings + // The caller reboots the device when reconfigure returns false; track + // every SPI setter so we don't silently report success after a glitch. + bool ok = true; + int err = lora.setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setBandwidth(bw); // different form than LR11xx - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setSyncWord(syncWord); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR20x0 setSyncWord %s%d", radioLibErr, err); + ok = false; + } if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range limitPower(LR2021_MAX_POWER_HF); @@ -224,14 +236,22 @@ template bool LR20x0Interface::reconfigure() } err = lora.setPreambleLength(preambleLength); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR20x0 setPreambleLength %s%d", radioLibErr, err); + ok = false; + } err = lora.setFrequency(getFreq()); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setOutputPower(power); - assert(err == RADIOLIB_ERR_NONE); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR20x0 setOutputPower %s%d", radioLibErr, err); + ok = false; + } // Apply RX gain mode — valid in STDBY, matches resetAGC() pattern err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain); @@ -240,7 +260,7 @@ template bool LR20x0Interface::reconfigure() startReceive(); // restart receiving - return true; + return ok; } template void LR20x0Interface::disableInterrupt() @@ -258,8 +278,6 @@ template void LR20x0Interface::setStandby() LOG_DEBUG("LR20x0 standby failed with error %d", err); } - assert(err == RADIOLIB_ERR_NONE); - isReceiving = false; // If we were receiving, not any more activeReceiveStart = 0; disableInterrupt(); diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 43149ef8b40..561f0a7baae 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -208,37 +208,51 @@ bool RF95Interface::reconfigure() // set mode to standby setStandby(); - // configure publicly accessible settings + // The caller reboots the device when reconfigure returns false; track + // every SPI setter so we don't silently report success after a glitch. + bool ok = true; + int err = lora->setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora->setBandwidth(bw); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora->setCodingRate(cr); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora->setSyncWord(syncWord); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("RF95 setSyncWord %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora->setCurrentLimit(currentLimit); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("RF95 setCurrentLimit %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora->setPreambleLength(preambleLength); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("RF95 setPreambleLength %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora->setFrequency(getFreq()); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } limitPower(RF95_MAX_POWER); @@ -247,12 +261,14 @@ bool RF95Interface::reconfigure() #else err = lora->setOutputPower(power); #endif - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } startReceive(); // restart receiving - return true; + return ok; } /** diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 3513bbba3f3..703a11421fc 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -213,37 +213,51 @@ template bool SX126xInterface::reconfigure() // set mode to standby setStandby(); - // configure publicly accessible settings + // The caller reboots the device when reconfigure returns false; track + // every SPI setter so we don't silently report success after a glitch. + bool ok = true; + int err = lora.setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setBandwidth(bw); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setCodingRate(cr); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setSyncWord(syncWord); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX126X setSyncWord %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora.setCurrentLimit(currentLimit); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX126X setCurrentLimit %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora.setPreambleLength(preambleLength); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX126X setPreambleLength %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora.setFrequency(getFreq()); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } limitPower(SX126X_MAX_POWER); // Make sure we reach the minimum power supported to turn the chip on (-9dBm) @@ -251,9 +265,10 @@ template bool SX126xInterface::reconfigure() power = -9; err = lora.setOutputPower(power); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX126X setOutputPower %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } // Apply RX gain mode — valid in STDBY (datasheet §9.6), matches resetAGC() pattern err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain); @@ -262,7 +277,7 @@ template bool SX126xInterface::reconfigure() startReceive(); // restart receiving - return true; + return ok; } template void SX126xInterface::disableInterrupt() @@ -281,8 +296,6 @@ template void SX126xInterface::setStandby() #ifdef ARCH_PORTDUINO if (err != RADIOLIB_ERR_NONE) portduino_status.LoRa_in_error = true; -#else - assert(err == RADIOLIB_ERR_NONE); #endif isReceiving = false; // If we were receiving, not any more activeReceiveStart = 0; diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 64d71921a58..60dc3457247 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -117,43 +117,57 @@ template bool SX128xInterface::reconfigure() // set mode to standby setStandby(); - // configure publicly accessible settings + // The caller reboots the device when reconfigure returns false; track + // every SPI setter so we don't silently report success after a glitch. + bool ok = true; + int err = lora.setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setBandwidth(bw); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } err = lora.setSyncWord(syncWord); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX128X setSyncWord %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora.setPreambleLength(preambleLength); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX128X setPreambleLength %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } err = lora.setFrequency(getFreq()); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + ok = false; + } limitPower(SX128X_MAX_POWER); err = lora.setOutputPower(power); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { LOG_ERROR("SX128X setOutputPower %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); + ok = false; + } startReceive(); // restart receiving - return true; + return ok; } template void SX128xInterface::disableInterrupt() @@ -174,7 +188,6 @@ template void SX128xInterface::setStandby() if (err != RADIOLIB_ERR_NONE) LOG_ERROR("SX128x standby %s%d", radioLibErr, err); - assert(err == RADIOLIB_ERR_NONE); #if ARCH_PORTDUINO if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) { digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);