From 283ba7dfae3e2bf4b34e3e4742d0f8705814d76a Mon Sep 17 00:00:00 2001 From: Mohamad EL CHAMAA Date: Wed, 8 Jul 2026 15:46:57 +0200 Subject: [PATCH] refactoring and cleaning --- .devcontainer/setup-zephyr.sh | 29 +++-- .github/workflows/build-zephyr-companion.yml | 109 +++++++++++++++++++ src/helpers/radiolib/CustomLR2021.h | 18 +++ src/helpers/radiolib/CustomLR2021Wrapper.h | 14 +++ src/helpers/radiolib/RadioLibWrappers.cpp | 5 +- src/helpers/radiolib/RadioLibWrappers.h | 4 + zephyr-port/07_companion/CMakeLists.txt | 10 +- zephyr-port/07_companion/README.md | 9 +- zephyr-port/07_companion/src/target.cpp | 49 +-------- zephyr-port/07_companion/src/target.h | 14 +++ 10 files changed, 199 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/build-zephyr-companion.yml diff --git a/.devcontainer/setup-zephyr.sh b/.devcontainer/setup-zephyr.sh index fc04b676b..e05c56d2b 100755 --- a/.devcontainer/setup-zephyr.sh +++ b/.devcontainer/setup-zephyr.sh @@ -9,8 +9,10 @@ # 1. a west/Zephyr workspace from zephyrproject-rtos/zephyr -> $ZEPHYR_WORKSPACE # 2. the matching Zephyr SDK (compilers) via `west sdk install` # 3. pyocd (CMSIS-DAP flash/RTT for the XIAO's on-board debugger) -# 4. arduino-cli + the three Arduino libraries the CMake references by -# absolute path ($HOME/Arduino/libraries/{RadioLib,Crypto,base64}) +# 4. arduino-cli + the three Arduino libraries the companion actually +# compiles ($HOME/Arduino/libraries/{RadioLib,Crypto,base64}); the other +# libs its CMakeLists.txt names (CayenneLPP/RTClib/ArduinoJson) are +# header-shimmed in compat/ and not installed # # It is intentionally idempotent: re-running skips work that's already done. # It is HEAVY (multi-GB clone + Zephyr SDK). Expect the first run to take a while. @@ -81,16 +83,18 @@ pip install -r zephyr/scripts/requirements.txt >/dev/null # 4) Zephyr SDK (toolchain). `west sdk install` auto-selects the version that # matches this Zephyr tree, so we don't hardcode (and mis-pin) an SDK version. +# Only the Arm toolchain is needed (nRF54L15 = Cortex-M33); without -t it +# would download every architecture's toolchain. echo ">> Installing the matching Zephyr SDK (compilers)..." -west sdk install || { +west sdk install -t arm-zephyr-eabi || { echo "!! 'west sdk install' failed. On older west you may need to install the" echo "!! Zephyr SDK manually: https://docs.zephyrproject.org/latest/develop/getting_started/index.html" } -# 5) arduino-cli + the Arduino libraries the companion CMake references by -# absolute path. arduino-cli's default sketchbook is $HOME/Arduino, so the -# libraries land in $HOME/Arduino/libraries/{RadioLib,Crypto,base64} exactly -# where CMakeLists.txt looks. +# 5) arduino-cli + the Arduino libraries the companion build compiles. +# arduino-cli's default sketchbook is $HOME/Arduino, so the libraries land +# in $HOME/Arduino/libraries/{RadioLib,Crypto,base64} exactly where +# CMakeLists.txt looks. if ! command -v arduino-cli >/dev/null 2>&1; then echo ">> Installing arduino-cli (user space -> ~/.local/bin)..." mkdir -p "$HOME/.local/bin" @@ -99,7 +103,8 @@ if ! command -v arduino-cli >/dev/null 2>&1; then export PATH="$HOME/.local/bin:$PATH" fi # Keep libraries in $HOME/Arduino so they match the CMake absolute paths. -arduino-cli config init --overwrite >/dev/null 2>&1 || true +# No --overwrite: an existing config is kept; directories.user is pinned below. +arduino-cli config init >/dev/null 2>&1 || true arduino-cli config set directories.user "${ARDUINO_DIR}" >/dev/null 2>&1 || true echo ">> Installing Arduino libraries (RadioLib/Crypto/base64) into ${ARDUINO_DIR}/libraries..." arduino-cli lib update-index >/dev/null @@ -124,11 +129,11 @@ cat <preambleLengthLoRa, this->headerType, + RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, + this->invertIQEnabled); + return LR2021::startReceive(); + } +#endif + bool isReceiving() { uint32_t irq = getIrqFlags(); return (irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED) diff --git a/src/helpers/radiolib/CustomLR2021Wrapper.h b/src/helpers/radiolib/CustomLR2021Wrapper.h index 5259c58e8..4b3ccb472 100644 --- a/src/helpers/radiolib/CustomLR2021Wrapper.h +++ b/src/helpers/radiolib/CustomLR2021Wrapper.h @@ -13,10 +13,24 @@ public: CustomLR2021Wrapper(CustomLR2021& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { + ((CustomLR2021 *)_radio)->setFrequency(freq); + ((CustomLR2021 *)_radio)->setSpreadingFactor(sf); + ((CustomLR2021 *)_radio)->setBandwidth(bw); + ((CustomLR2021 *)_radio)->setCodingRate(cr); + updatePreamble(sf); + } + bool isReceivingPacket() override { return ((CustomLR2021 *)_radio)->isReceiving(); } + void onBeforeStartRecv() override { + // re-arming (setRxPath) while still in continuous RX -> CMD_PERR (-706) + // and a wedged receiver; drop to standby before every startReceive() + _radio->standby(); + } + float getCurrentRSSI() override { float rssi = -110; ((CustomLR2021 *)_radio)->getRssiInst(&rssi); diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 7dec32a3a..86d9abc73 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -104,7 +104,7 @@ void RadioLibWrapper::loop() { } void RadioLibWrapper::startRecv() { - _radio->standby(); // LR2021: re-arming (setRxPath) while still in continuous RX -> CMD_PERR + onBeforeStartRecv(); int err = _radio->startReceive(); if (err == RADIOLIB_ERR_NONE) { state = STATE_RX; @@ -136,8 +136,7 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { } if (state != STATE_RX) { - _radio->standby(); // back to standby first: re-arming from continuous RX makes the - // LR2021 reject setRxPath with CMD_PERR (-706) and wedges the receiver + onBeforeStartRecv(); int err = _radio->startReceive(); if (err == RADIOLIB_ERR_NONE) { state = STATE_RX; diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index f0c98aecc..76ad90c15 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -19,6 +19,10 @@ protected: float packetScoreInt(float snr, int sf, int packet_len); virtual bool isReceivingPacket() =0; virtual void doResetAGC(); + // Called right before every startReceive(). Default no-op; radios that cannot + // re-arm RX while already in continuous RX (LR2021) override this to drop to + // standby first. + virtual void onBeforeStartRecv() { } public: RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _preamble_sf(0) { n_recv = n_sent = 0; } diff --git a/zephyr-port/07_companion/CMakeLists.txt b/zephyr-port/07_companion/CMakeLists.txt index 89f735770..b5fbe49d4 100644 --- a/zephyr-port/07_companion/CMakeLists.txt +++ b/zephyr-port/07_companion/CMakeLists.txt @@ -65,7 +65,15 @@ target_compile_definitions(app PRIVATE # instead of the default 865 MHz sub-GHz. The app has no 2.4 GHz preset, so this is how to put # the node on 2.4 GHz for the over-the-air RF test (the choice then persists in prefs). option(MC_BAND_2G4 "Boot on the 2.4 GHz LoRa preset instead of sub-GHz" OFF) +# LoRa boot params must be global -D flags (mirroring platformio.ini), not header +# defines: core headers (e.g. RadioLibWrappers.h getSpreadingFactor) expand LORA_SF +# in translation units that never include target.h. Keep in sync with the presets +# documented in src/target.h. if(MC_BAND_2G4) - target_compile_definitions(app PRIVATE MC_BAND_2G4=1) + target_compile_definitions(app PRIVATE MC_BAND_2G4=1 + LORA_FREQ=2450.0f LORA_BW=500.0f LORA_SF=8 LORA_CR=5 LORA_TX_POWER=12) message(STATUS "MeshCore: building for 2.4 GHz band (2450 MHz / 500 kHz / SF8 / CR5)") +else() + target_compile_definitions(app PRIVATE + LORA_FREQ=869.618f LORA_BW=62.5f LORA_SF=8 LORA_CR=5 LORA_TX_POWER=22) endif() diff --git a/zephyr-port/07_companion/README.md b/zephyr-port/07_companion/README.md index d8eb3809c..533bcc9f3 100644 --- a/zephyr-port/07_companion/README.md +++ b/zephyr-port/07_companion/README.md @@ -10,14 +10,17 @@ straight from this repo; the radio driver is stock RadioLib in generic (non-Ardu | Path | Role | |---|---| | `src/main.cpp` | Zephyr entry point; runs the MeshCore companion loop | -| `src/target.cpp`, `src/target.h` | board/radio bring-up: `LR2021` subclass (RX/length fixes), pin map, band presets, TX-power clamp | +| `src/target.cpp`, `src/target.h` | board/radio bring-up: pin map, band presets, TX-power clamp; radio = the shared `CustomLR2021`/`CustomLR2021Wrapper` | | `src/serial_ble_interface.cpp/.h` | BLE transport: custom encryption-gated Nordic-UART (NUS) GATT service; central-driven pairing | | `src/zephyr_internal_fs.cpp` | `InternalFileSystem` backed by a Zephyr flash partition (LittleFS) for prefs/identity | | `compat/` | header-only shims (CayenneLPP, RTClib, Arduino glue) so the core builds without those libs | | `dts/`, `app.overlay`, `prj.conf` | devicetree overlay (SPI/GPIO for the LR2021) and Kconfig | -The companion also pulls one modified core file, `src/helpers/radiolib/RadioLibWrappers.cpp` -(standby-before-`startReceive` for the LR2021), from the repo root. +All LR2021-specific radio fixes (standby before re-arming RX, header-CRC recovery, RX +max-length re-assert) live in `src/helpers/radiolib/CustomLR2021.h` / +`CustomLR2021Wrapper.h` at the repo root, shared with the bare-metal variant +(`variants/xiao_nrf54l15`). The core `RadioLibWrapper` exposes a neutral +`onBeforeStartRecv()` hook for the standby quirk, so other radios keep stock behaviour. ## Prerequisites diff --git a/zephyr-port/07_companion/src/target.cpp b/zephyr-port/07_companion/src/target.cpp index 59b0e3dc6..df7713874 100644 --- a/zephyr-port/07_companion/src/target.cpp +++ b/zephyr-port/07_companion/src/target.cpp @@ -1,56 +1,19 @@ #include "target.h" #include "zephyr_radiolib_hal.h" -#include -#include +/* Chip quirks (header-CRC standby, RX max-length re-assert) and the mesh wrapper + * (standby-before-re-arm, RSSI hooks) are shared with the bare-metal variant via + * CustomLR2021 / CustomLR2021Wrapper — single source for the LR2021 fixes. */ +#include #include -#define LR2021_IRQ_DIO 8 -#ifndef LR2021_RX_BOOST_LEVEL -#define LR2021_RX_BOOST_LEVEL 7 /* matches Semtech usp_zephyr rx-boost-cfg / CustomLR2021 */ -#endif - static ZephyrHal hal; -class LR2021Z : public LR2021 { - public: - explicit LR2021Z(Module *m) : LR2021(m) {} - void setIrqDio(uint8_t n) { this->irqDioNum = n; } - /* Match the known-good bare-metal CustomLR2021: on a corrupted LoRa header the - * stock driver returns len 0 and can leave RX half-wedged; force standby so the - * wrapper re-arms cleanly. */ - size_t getPacketLength(bool update) override { - size_t len = LR2021::getPacketLength(update); - if (len == 0 && (getIrqFlags() & RADIOLIB_LR2021_IRQ_LORA_HDR_CRC_ERROR)) { - standby(); - } - return len; - } - - int16_t startReceive() override { - setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, - RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, - this->invertIQEnabled); - return LR2021::startReceive(); - } -}; - static Module s_mod(&hal, LR_PIN_NSS, LR_PIN_DIO1, LR_PIN_RESET, LR_PIN_BUSY); -static LR2021Z s_lora(&s_mod); +static CustomLR2021 s_lora(&s_mod); ZBoard board; /* defined before the radio wrapper that references it */ -class LR2021Mesh : public RadioLibWrapper { - public: - LR2021Mesh(LR2021 &r, mesh::MainBoard &b) : RadioLibWrapper(r, b) {} - - bool isReceivingPacket() override { - uint32_t irq = ((LR2021 *)_radio)->getIrqFlags(); - return (irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED) || - (irq & RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID); - } - float getCurrentRSSI() override { return ((LR2021 *)_radio)->getRSSI(); } -}; -static LR2021Mesh s_radio(s_lora, board); +static CustomLR2021Wrapper s_radio(s_lora, board); RadioLibWrapper &radio_driver = s_radio; VolatileRTCClock rtc_clock; diff --git a/zephyr-port/07_companion/src/target.h b/zephyr-port/07_companion/src/target.h index 3a3d4e877..bb85df13e 100644 --- a/zephyr-port/07_companion/src/target.h +++ b/zephyr-port/07_companion/src/target.h @@ -26,12 +26,26 @@ * build; a 2G4 build needs no app interaction (and the choice persists in prefs). The app * has no 2.4 GHz preset of its own, presets are app-side and sub-GHz only, so this flag is * the practical way to put the node on 2.4 GHz for the RF test. */ +/* NOTE: the real build-time source of these is CMakeLists.txt (global -D flags, + * mirroring platformio.ini): core headers such as RadioLibWrappers.h expand + * LORA_SF in translation units that never include this file. The block below is + * only a fallback for tooling (clangd/IDE) parsing this header standalone. */ #ifdef MC_BAND_2G4 + #ifndef LORA_FREQ #define LORA_FREQ LORA_FREQ_2G4 + #endif + #ifndef LORA_BW #define LORA_BW LORA_BW_2G4 + #endif + #ifndef LORA_SF #define LORA_SF LORA_SF_2G4 + #endif + #ifndef LORA_CR #define LORA_CR LORA_CR_2G4 + #endif + #ifndef LORA_TX_POWER #define LORA_TX_POWER LORA_TX_POWER_2G4 + #endif #else #ifndef LORA_FREQ #define LORA_FREQ 869.618f