From 7302db1672b452df9ffd602dba23c5c6b1503a0d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 4 Aug 2026 06:03:30 -0700 Subject: [PATCH] Update LoRa firmware on Thinknode M7 (#11337) --- src/mesh/LR11x0Interface.cpp | 70 ++++++++++++++++++- src/mesh/LR11x0Interface.h | 5 ++ .../esp32s3/ELECROW-ThinkNode-M7/variant.h | 8 +++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index c54ae91a6..8bd22bd35 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -4,6 +4,25 @@ #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" + +// A variant may define LR11X0_UPDATE_FIRMWARE_TO to a Semtech transceiver firmware version (e.g. 0x0402) to +// bake that image in and update the radio on first boot. Every supported image is 61320 words, so this costs +// ~240 kB of flash regardless of the version chosen - only enable it on a variant with the headroom, and +// only for as long as it takes to update the affected units. +#ifdef LR11X0_UPDATE_FIRMWARE_TO +#if LR11X0_UPDATE_FIRMWARE_TO == 0x0402 +#define RADIOLIB_LR1110_FIRMWARE_0402 +#elif LR11X0_UPDATE_FIRMWARE_TO == 0x0401 +#define RADIOLIB_LR1110_FIRMWARE_0401 +#elif LR11X0_UPDATE_FIRMWARE_TO == 0x0307 +#define RADIOLIB_LR1110_FIRMWARE_0307 +#else +// Note: RadioLib ships lr1110_transceiver_0308.h but has no selector for it in LR11x0_firmware.h. +#error "LR11X0_UPDATE_FIRMWARE_TO must be one of 0x0307, 0x0401, 0x0402" +#endif +#include +#endif + #ifdef LR11X0_DIO_AS_RF_SWITCH #include "rfswitch.h" #elif ARCH_PORTDUINO @@ -117,14 +136,59 @@ template bool LR11x0Interface::init() // \todo Display actual typename of the adapter, not just `LR11x0` LOG_INFO("LR11x0 init result %d", res); - if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) - return false; + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) { +#ifdef LR11X0_UPDATE_FIRMWARE_TO + // An interrupted update leaves the radio sitting in bootloader mode, where begin() fails. Retry the + // flash from here rather than giving up, otherwise the device could never recover on its own. + LOG_WARN("LR11x0 did not start; attempting firmware recovery in case an update was interrupted"); + if (lora.updateFirmware(lr11xx_firmware_image, LR11XX_FIRMWARE_IMAGE_SIZE, true) == RADIOLIB_ERR_NONE) { + LOG_INFO("LR1110 firmware recovery succeeded, re-initializing radio"); + res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + } +#endif + if (res != RADIOLIB_ERR_NONE) + return false; + } LR11x0VersionInfo_t version; res = lora.getVersionInfo(&version); - if (res == RADIOLIB_ERR_NONE) + if (res == RADIOLIB_ERR_NONE) { LOG_DEBUG("LR11x0 Device %d, HW %d, FW %d.%d, WiFi %d.%d, GNSS %d.%d", version.device, version.hardware, version.fwMajor, version.fwMinor, version.fwMajorWiFi, version.fwMinorWiFi, version.fwGNSS, version.almanacGNSS); + transceiverFw = ((uint16_t)version.fwMajor << 8) | version.fwMinor; + transceiverDevice = version.device; + } + +#ifdef LR11X0_UPDATE_FIRMWARE_TO + // One-shot transceiver firmware update, opt-in per variant. Only runs when the part is an LR1110 running + // older firmware than the baked-in image, so once it has succeeded it is a no-op on subsequent boots. + if (transceiverDevice == RADIOLIB_LR11X0_DEVICE_LR1110 && transceiverFw != 0 && transceiverFw < LR11X0_UPDATE_FIRMWARE_TO) { + LOG_WARN("LR1110 transceiver FW %d.%d is older than %d.%d - updating now. DO NOT POWER OFF: this " + "erases and rewrites the radio's own flash.", + transceiverFw >> 8, transceiverFw & 0xFF, LR11X0_UPDATE_FIRMWARE_TO >> 8, LR11X0_UPDATE_FIRMWARE_TO & 0xFF); + + int upd = lora.updateFirmware(lr11xx_firmware_image, LR11XX_FIRMWARE_IMAGE_SIZE, true); + if (upd != RADIOLIB_ERR_NONE) { + // The radio is likely sitting in bootloader mode. It is not bricked - the update is retried on + // the next boot because the version check above will still see old (or unreadable) firmware. + LOG_ERROR("LR1110 firmware update FAILED %s%d - power-cycle to retry", radioLibErr, upd); + return false; + } + + LOG_INFO("LR1110 firmware update complete, re-initializing radio"); + res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + if (res != RADIOLIB_ERR_NONE) { + LOG_ERROR("LR11x0 re-init after firmware update failed %s%d", radioLibErr, res); + return false; + } + + if (lora.getVersionInfo(&version) == RADIOLIB_ERR_NONE) { + transceiverFw = ((uint16_t)version.fwMajor << 8) | version.fwMinor; + transceiverDevice = version.device; + LOG_INFO("LR1110 now running transceiver FW %d.%d", version.fwMajor, version.fwMinor); + } + } +#endif LOG_INFO("Frequency set to %f", getFreq()); LOG_INFO("Bandwidth set to %f", bw); diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h index ee8761177..552dd5e5e 100644 --- a/src/mesh/LR11x0Interface.h +++ b/src/mesh/LR11x0Interface.h @@ -39,6 +39,11 @@ template class LR11x0Interface : public RadioLibInterface int16_t getCurrentRSSI() override; + /// Transceiver firmware version as (major << 8 | minor), and which LR11x0 part this is. Captured at + /// init() from getVersionInfo(); 0 if the query failed. + uint16_t transceiverFw = 0; + uint8_t transceiverDevice = 0; + /** * Glue functions called from ISR land */ diff --git a/variants/esp32s3/ELECROW-ThinkNode-M7/variant.h b/variants/esp32s3/ELECROW-ThinkNode-M7/variant.h index 52d2948a1..7c63a225c 100644 --- a/variants/esp32s3/ELECROW-ThinkNode-M7/variant.h +++ b/variants/esp32s3/ELECROW-ThinkNode-M7/variant.h @@ -33,6 +33,14 @@ #define LR11X0_DIO3_TCXO_VOLTAGE 1.8 #define LR11X0_DIO_AS_RF_SWITCH +// TEMPORARY: units shipped with LR1110 transceiver FW 0x0303 (the original 2020 release), which cannot +// reliably demodulate 500 kHz LoRa - Turbo presets fail RX with ~41% byte errors while TX and narrower +// bandwidths are fine. 0x0303 also predates GetLoRaRxHeaderInfos, which computePacketTime() calls on every +// received packet. Confirmed fixed by updating to 0x0307; targeting 0x0402 additionally picks up the +// out-of-band emission fix for consecutive LoRa transmissions (0x0401) and three CVE fixes (0x0402). +// Costs ~240 kB of flash. Remove once the affected units are updated. +#define LR11X0_UPDATE_FIRMWARE_TO 0x0402 + #define HAS_ETHERNET 1 #define USE_CH390D 1