Browse Source

refactoring and cleaning

pull/2944/head
Mohamad EL CHAMAA 3 weeks ago
parent
commit
283ba7dfae
  1. 29
      .devcontainer/setup-zephyr.sh
  2. 109
      .github/workflows/build-zephyr-companion.yml
  3. 18
      src/helpers/radiolib/CustomLR2021.h
  4. 14
      src/helpers/radiolib/CustomLR2021Wrapper.h
  5. 5
      src/helpers/radiolib/RadioLibWrappers.cpp
  6. 4
      src/helpers/radiolib/RadioLibWrappers.h
  7. 10
      zephyr-port/07_companion/CMakeLists.txt
  8. 9
      zephyr-port/07_companion/README.md
  9. 49
      zephyr-port/07_companion/src/target.cpp
  10. 14
      zephyr-port/07_companion/src/target.h

29
.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 <<EOF
============================================================================
Zephyr toolchain + companion deps ready.
Zephyr workspace : ${ZEPHYR_WORKSPACE} (mainline, VERSION 4.4.99)
Zephyr workspace : ${ZEPHYR_WORKSPACE} (mainline @ ${ZEPHYR_REV})
Zephyr base : ${ZEPHYR_WORKSPACE}/zephyr
Python venv : ${VENV_DIR}
Arduino libs : ${ARDUINO_DIR}/libraries/{RadioLib,Crypto,base64}
Flash/RTT tool : pyocd (target: nrf54)
Flash/RTT tool : pyocd (target: nrf54l)
To build & flash the companion:
source ${VENV_DIR}/bin/activate
@ -136,8 +141,8 @@ To build & flash the companion:
cd zephyr-port/07_companion
west build -b ${BOARD} -d build . --pristine
pyocd flash -t nrf54 -e chip build/zephyr/zephyr.hex
pyocd reset -t nrf54
pyocd flash -t nrf54l -e chip build/zephyr/zephyr.hex
pyocd reset -t nrf54l
PlatformIO is untouched and still drives the existing ESP32/nRF52/etc. variants.
============================================================================

109
.github/workflows/build-zephyr-companion.yml

@ -0,0 +1,109 @@
name: Build Zephyr Companion (XIAO nRF54L15 + LR2021)
# Builds zephyr-port/07_companion against the mainline Zephyr revision pinned in
# .devcontainer/setup-zephyr.sh (single source for the pin and the Arduino library
# versions), for both band presets. This is the only build coverage the nRF54L15 +
# LR2021 port has — keep the path filters in sync with what the companion compiles.
on:
push:
branches: [ main ]
paths:
- 'zephyr-port/**'
- 'src/**'
- 'examples/companion_radio/**'
- 'arch/stm32/Adafruit_LittleFS_stm32/**'
- 'lib/ed25519/**'
- '.devcontainer/setup-zephyr.sh'
- '.github/workflows/build-zephyr-companion.yml'
pull_request:
paths:
- 'zephyr-port/**'
- 'src/**'
- 'examples/companion_radio/**'
- 'arch/stm32/Adafruit_LittleFS_stm32/**'
- 'lib/ed25519/**'
- '.devcontainer/setup-zephyr.sh'
- '.github/workflows/build-zephyr-companion.yml'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- band: subghz
extra_args: ''
- band: 2g4
extra_args: '-- -DMC_BAND_2G4=ON'
steps:
- uses: actions/checkout@v4
- name: Read pinned versions from setup-zephyr.sh
id: pins
run: |
eval "$(grep -E '^(ZEPHYR_REV|RADIOLIB_VER|CRYPTO_VER|BASE64_VER)=' .devcontainer/setup-zephyr.sh)"
{
echo "zephyr_rev=${ZEPHYR_REV}"
echo "radiolib=${RADIOLIB_VER}"
echo "crypto=${CRYPTO_VER}"
echo "base64=${BASE64_VER}"
} >> "$GITHUB_OUTPUT"
- name: Install host dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cmake ninja-build gperf ccache device-tree-compiler \
wget xz-utils file make python3-dev python3-venv
- name: Cache Zephyr workspace and SDK
uses: actions/cache@v4
with:
path: |
~/zephyrproject
~/zephyr-sdk-*
key: zephyr-${{ steps.pins.outputs.zephyr_rev }}-arm-v1
- name: Set up west workspace (pinned mainline Zephyr)
run: |
python3 -m venv ~/.zephyr-venv
source ~/.zephyr-venv/bin/activate
pip install --quiet --upgrade pip west
if [ ! -d ~/zephyrproject/.west ]; then
west init -m https://github.com/zephyrproject-rtos/zephyr \
--mr "${{ steps.pins.outputs.zephyr_rev }}" ~/zephyrproject
fi
cd ~/zephyrproject
west update --narrow -o=--depth=1
west zephyr-export
pip install --quiet -r zephyr/scripts/requirements.txt
west sdk install -t arm-zephyr-eabi
- name: Install Arduino libraries (RadioLib/Crypto/base64)
run: |
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
| BINDIR="$HOME/.local/bin" sh
export PATH="$HOME/.local/bin:$PATH"
arduino-cli config init >/dev/null 2>&1 || true
arduino-cli config set directories.user "$HOME/Arduino"
arduino-cli lib update-index
arduino-cli lib install \
"RadioLib@${{ steps.pins.outputs.radiolib }}" \
"Crypto@${{ steps.pins.outputs.crypto }}" \
"base64@${{ steps.pins.outputs.base64 }}"
- name: Build companion (${{ matrix.band }})
run: |
source ~/.zephyr-venv/bin/activate
export ZEPHYR_BASE=$HOME/zephyrproject/zephyr
cd zephyr-port/07_companion
west build -b xiao_nrf54l15/nrf54l15/cpuapp -d build . --pristine ${{ matrix.extra_args }}
- name: Upload firmware
uses: actions/upload-artifact@v4
with:
name: meshcore-companion-xiao_nrf54l15-lr2021-${{ matrix.band }}
path: zephyr-port/07_companion/build/zephyr/zephyr.hex

18
src/helpers/radiolib/CustomLR2021.h

@ -22,6 +22,11 @@ class CustomLR2021 : public LR2021 {
public:
CustomLR2021(Module *mod) : LR2021(mod) { }
// route the host IRQ to the DIO the board actually wires (std_init does this
// too; callers that do their own begin() sequence use this directly)
void setIrqDio(uint8_t n) { irqDioNum = n; }
#if defined(ARDUINO) // Arduino-core bring-up; Zephyr (compat shim, no SPIClass) drives begin() itself
bool std_init(SPIClass* spi = NULL) {
// route the host IRQ to the DIO the board actually wires (default DIO8)
irqDioNum = LR2021_IRQ_DIO;
@ -62,6 +67,7 @@ public:
return true; // success
}
#endif // ARDUINO
size_t getPacketLength(bool update) override {
size_t len = LR2021::getPacketLength(update);
@ -73,6 +79,18 @@ public:
return len;
}
#if RADIOLIB_GODMODE
int16_t startReceive() override {
// re-assert max payload length before every RX: a TX leaves the chip's
// packet-length param at the last TX size, which would clip longer
// incoming packets. Needs GODMODE (setLoRaPacketParams is private).
setLoRaPacketParams(this->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)

14
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);

5
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;

4
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; }

10
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()

9
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

49
zephyr-port/07_companion/src/target.cpp

@ -1,56 +1,19 @@
#include "target.h"
#include "zephyr_radiolib_hal.h"
#include <helpers/radiolib/RadioLibWrappers.h>
#include <RadioLib.h>
/* 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 <helpers/radiolib/CustomLR2021Wrapper.h>
#include <zephyr/sys/reboot.h>
#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;

14
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

Loading…
Cancel
Save