Every LoRa / LoRaWAN application in the lab, in one repo, sharing one copy of the vendored Semtech stack.
These four applications were briefly four repos. Two of them —
lorawan_uplink and lorawan_lrfhss — each carried their own git submodules
of Lora-net/usp and Lora-net/usp_zephyr, pinned to the identical pair of
commits, plus their own copy of the same board.yml patch and their own
near-identical setup.sh. That is 18 MB of duplicate clone, a patch free to
drift out of sync with its twin, and two setup scripts to keep in step, in
exchange for nothing.
The other two are 30 KB of source each, which does not justify a repository.
So: one repo, one modules/, one patches/, one setup.sh, four apps.
labsc_lora/
├── modules/usp submodule, Lora-net/usp @ 351b2015
├── modules/usp_zephyr submodule, Lora-net/usp_zephyr @ bfacd435
├── patches/ applied to modules/usp_zephyr by setup.sh
├── setup.sh run ONCE per clone, not per app
└── apps/
├── lorawan_uplink/ LR1121 + ESP32-C6 — USP / LBM
├── lorawan_lrfhss/ SX1262 + Heltec V3 — USP / LBM
├── lorawan_join/ SX1262 + Heltec V3 — Zephyr subsys/lorawan
└── lr_fhss_tx/ SX1262 + Heltec V3 — raw LR-FHSS PHY
Only the two USP apps consume modules/. lorawan_join and lr_fhss_tx build
against the container's in-tree Zephyr alone — they cost nothing to keep here
and belong with their siblings.
| App | Board | Stack | Pin AU915 sub-band | LR-FHSS |
|---|---|---|---|---|
lorawan_uplink |
ESP32-C6 + Core1121 (LR1121) | USP / LoRa Basics Modem | no | capable, not the focus |
lorawan_lrfhss |
Heltec V3 (SX1262) | USP / LoRa Basics Modem | no | yes — as AU915 DR7 |
lorawan_join |
Heltec V3 (SX1262) | Zephyr subsys/lorawan (loramac-node) |
yes | no |
lr_fhss_tx |
Heltec V3 (SX1262) | Zephyr LoRa driver on lora-basics-modem | n/a | raw PHY only |
lorawan_join and lorawan_lrfhss are complementary, not redundant. The
two LoRaWAN stacks in Zephyr fork on exactly one axis: loramac-node can pin an
AU915 sub-band before joining but has no LR-FHSS at all; LBM has full LR-FHSS
but deliberately implements the specified join, scanning the whole 72-channel
plan and narrowing only from the network's ChMask. Neither does both.
git clone --recurse-submodules git@github.com:glmoritz/labsc_lora.git
cd labsc_lora
./setup.shsetup.sh is idempotent. It initialises the submodules and applies both
patches in patches/. Patch 0002 is not optional — without it every
SX126x build fails at CMake time. See apps/lorawan_lrfhss/README.md.
Inside the zephyr_container devcontainer, with this repo at
workspace/projects/labsc_lora/:
bash /workspace/scripts/zephyr-project.sh set projects/labsc_lora/apps/lorawan_join
bash /workspace/scripts/zephyr-project.sh build
bash /workspace/scripts/zephyr-project.sh flash /dev/ttyUSB0The Heltec V3's CP2102 enumerates as /dev/ttyUSB0; the script defaults to
/dev/ttyACM0, which is the ESP32-C6 devkit's native USB. Pass the port.
Or directly:
source /opt/toolchains/zephyr/zephyr-env.sh
cd apps/<app> && west build -p always -b "$(cat .board)" .Each app's .board names its default target.
All four build clean. None has been run on hardware yet — no Heltec V3 has
been attached. Each app's README states its own untested assumptions; the
tx-power-offset = <0> in the Heltec overlays is marked [VERIFY] and is
uncalibrated.
Suggested bench order, shortest path to a diagnosis first:
lorawan_join— pinned to AU915 sub-band 3, with alwshell for inspecting state. Fastest route to a confirmed join.lorawan_lrfhsswithLRFHSS_FORCE 0— proves the USP path joins and uplinks at all, under network-controlled ADR.lorawan_lrfhsswithLRFHSS_FORCE 1— then check in ChirpStack that the uplinks arrive at DR7. If they arrive at another DR, the custom ADR profile did not take, and that is the whole point of the app.