5 Commits
Author SHA1 Message Date
swaits 688938d8e8 chore: release 1.5.2 2026-05-11 15:03:13 -06:00
swaits e9360c28ba chore: release 1.5.1 2026-05-11 11:38:38 -06:00
swaits d7a1ee86d0 fix(radio): cache mod params on SET_CONFIG so TX works without prior RX
The TX state machine's `prep_for_cad_act` (TxCadPrep entry) and
`prep_for_tx_act` (TxPrep entry) both read `ctx.mdltn` and emit
`TransmitFailed` → `ERR(ERadio)` if it's None. But `ctx.mdltn` was
only ever assigned on the RX path — `try_start_rx_hw` (RxArming entry)
and `cache_rx_pkt` (RxSession entry). `reconfigure_radio` already
called `to_lora_mod_params(lora, cfg)` during SET_CONFIG but discarded
the result via `let _ = …?;` (comment claimed image calibration; the
function is pure compute on SX126x).

Result: a freshly-booted dongle that issues TX before ever calling
RX_START gets `ERR(ERadio)` on the first CmdTx because the chart's
TX-entry actions find `ctx.mdltn = None`. The bug was hidden in
practice by any prior RX_START (ai-bot, ping_pong --role rx, etc.)
leaving `ctx.mdltn` populated across sessions until the next reflash.

Fix: thread the `ModulationParams` out of `reconfigure_radio` as its
return value, and have `apply_set_config` cache it in `self.mdltn`
alongside `self.config`. The failure path clears `self.mdltn` along
with `self.config` so a SET_CONFIG that fails leaves a clean
Unconfigured state.

Surfaced while testing the matching sync-word fix on freshly-flashed
dongles via `examples/sync_word_test.py` — the first TX failed with
`ERR(ERadio)` until this path was wired up.
2026-05-11 11:15:30 -06:00
swaits 0b9f9ee882 fix(radio): apply LoRaConfig.sync_word to the chip on SET_CONFIG
`reconfigure_radio` previously consumed `cfg.sync_word` from the host
in name only — it called `enter_standby` and constructed a fresh set
of LoRa modulation params (which carry sf/bw/cr/freq but not the sync
word), then returned. lora-phy v3 has no public sync-word setter for
SX126x, and there's no codepath in this crate that touches the chip's
LoRaSyncword registers (0x0740/0x0741) after boot. The chip therefore
sat on whatever `LoRa::new(_, false, _)` set at cold init —
`LORAWAN_PRIVATE_SYNCWORD` (byte 0x12, register pair 0x1424) — for the
entire session, regardless of what the host requested.

That happened to work for every existing user because everyone wants
0x1424 (MeshCore's sync byte). It manifested as soon as a host actually
needed a different value: `anon_ping.py` survived because MeshCore
matches, but any custom mesh / non-MeshCore network would get silent
wrong-sync-word TXes that no peer could hear and no host-side diagnostic
could pin down.

This commit applies the host-supplied sync word inside
`reconfigure_radio`, between the existing `enter_standby` and the
mod-params construction:

  - `cfg.sync_word` (the protocol's u16, RadioLib-style; e.g.
    0x1424 == byte 0x12, 0x3444 == byte 0x34) is decoded back to its
    underlying single byte: `byte = (hi & 0xF0) | (lo >> 4)` after
    `to_be_bytes()`, the inverse of lora-phy's `convert_sync_word`
    nibble-expansion.

  - That byte is handed to lora-phy's new
    `LoRa<RK,DLY>::set_lora_sync_word` (added in the matching
    lora-phy commit), which honours SX1261/2 datasheet §8.3.1 by
    driving `ensure_ready` + conditional `set_standby` before
    writing the register pair, and caches the new value in the
    `LoRa` struct so a later cold-reset NRESET (Recovering state)
    resumes with the host's sync word rather than reverting to the
    construction-time default.

  - Failure of the sync-word write lands the same way `enter_standby`
    failure already did: `apply_set_config` wipes `self.config`,
    sends `ERR(ERadio)`, and routes the chart to `Recovering` to
    NRESET + cold-init. No new error-handling plumbing required.

Cargo.lock bumps lora-phy from `fixed#e8a52ebd` to `fixed#8d66d1c4`,
which is the merge of three pre-existing `fix/*` branches plus a
new `feat/set-sync-word` branch carrying the lora-phy API addition.

Verified on hardware with a two-dongle bench test
(`examples/sync_word_test.py`): one dongle pinned to sync 0x5464,
the other rotating through six sync words. RX hears exactly the
matching cycles with realistic RSSI / SNR; non-matching cycles are
correctly filtered by the SX126x packet engine before reaching the
host.
2026-05-11 11:10:47 -06:00
swaits 04dc58c943 chore(deps): bump hsmc 0.5 -> 0.6
0.6.0 is a perf pass + bench harness migration; chart syntax and
public API are unchanged. All 10 board features check clean.
2026-05-11 10:49:32 -06:00
4 changed files with 169 additions and 72 deletions

No files matched your search

+75
View File
@@ -7,6 +7,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
## [1.5.2] - 2026-05-11
### Changed
- Refreshed `Cargo.lock` to pull in the latest compatible patch/minor
releases of dependencies. Notable bumps:
- `donglora-protocol` 1.1.0 → 1.2.0
- `esp-hal` 1.1.0 → 1.1.1
- `heapless` 0.9.2 → 0.9.3 (propagates through embassy + esp crates)
- `embassy-usb-driver` 0.2.0 → 0.2.1
- `embassy-usb-synopsys-otg` 0.3.2 → 0.3.3
- `enumset` 1.1.10 → 1.1.12
- `digest` 0.11.2 → 0.11.3, `hashbrown` 0.17.0 → 0.17.1,
`hybrid-array` 0.4.11 → 0.4.12, `cc` 1.2.61 → 1.2.62
No source changes; all 10 board targets compile clean and host-side
protocol tests pass.
## [1.5.1] - 2026-05-11
### Fixed
- **`LoRaConfig.sync_word` is now actually applied to the SX126x.**
`reconfigure_radio` accepted the host's `sync_word` field in name
only — the value was stored in `self.config` but never written to
the chip's `LoRaSyncwordMSB`/`LoRaSyncwordLSB` register pair
(0x0740/0x0741, SX1261/2 datasheet Rev 2.2 §13.4.4). The chip sat
on whatever `LoRa::new(_, false, _)` set at cold init —
`LORAWAN_PRIVATE_SYNCWORD` (byte `0x12`, register pair `0x1424`).
Everyone got byte `0x12` regardless of the requested value. Worked
by accident for MeshCore (which uses byte `0x12`); silently broke
any host trying to talk to a non-MeshCore network. Fix wires
`cfg.sync_word` through to `lora.set_lora_sync_word(byte)` after
`enter_standby`, using the new lora-phy API from
[lora-rs/lora-rs#433](https://github.com/lora-rs/lora-rs/pull/433).
The `u16` protocol field is decoded back to its underlying byte
(`byte = (hi & 0xF0) | (lo >> 4)` after `to_be_bytes()`), the
inverse of lora-phy's `convert_sync_word` nibble-expansion. Honors
SX1261/2 §8.3.1 (BUSY-low precondition) via the existing
`ensure_ready + conditional set_standby` preamble in
`LoRa::set_lora_sync_word`.
- **First TX after `SET_CONFIG` no longer fails with `ERR(ERadio)` on
a freshly-booted chip.** The chart's `prep_for_cad_act`
(`TxCadPrep` entry) and `prep_for_tx_act` (`TxPrep` entry) both
read `ctx.mdltn` and emit `TransmitFailed` → `ERR(ERadio)` if it's
`None`. But `ctx.mdltn` was only ever assigned on the RX path —
`try_start_rx_hw` (`RxArming` entry) and `cache_rx_pkt`
(`RxSession` entry). `reconfigure_radio` already called
`to_lora_mod_params(lora, cfg)` during `SET_CONFIG` but discarded
the result via `let _ = …?;` (the dropped comment claimed image
calibration; the function is pure compute on SX126x). Hidden in
practice by any prior `RX_START` (ai-bot, `ping_pong --role rx`,
etc.) leaving `ctx.mdltn` populated until the next reflash. Fix
threads the `ModulationParams` out of `reconfigure_radio` as its
return value, and `apply_set_config` caches it in `self.mdltn`
alongside `self.config`. First TX on a fresh boot now works
without a preceding `RX_START`.
### Changed
- **`lora-phy` `swaits/lora-rs#fixed` bumped from `e8a52ebd` →
`043d7659`.** The fork's `fixed` merge now includes a new
`feat/set-sync-word` branch alongside the three pre-existing
`fix/*` branches.
[lora-rs/lora-rs#433](https://github.com/lora-rs/lora-rs/pull/433)
adds the runtime `LoRa::set_lora_sync_word(u8)` API the radio fix
above depends on (was unavailable in stock lora-phy; the only
public sync-word entry points were `with_syncword` /
`new(_, public_network: bool, _)` at construction time). Strictly
additive in lora-phy: pure addition, no behavioral change to
`init_lora` / `with_syncword` / any existing path.
- **`hsmc` 0.5 → 0.6.** Patch bump tracked via `cargo update -p
hsmc`; no behavior change in this crate's usage. Pulled in
separately from the radio fixes so the dep churn doesn't blur the
fix commits.
## [1.5.0] - 2026-04-30
### Added
Generated
+46 -44
View File
@@ -167,9 +167,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.61"
version = "1.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -210,7 +210,7 @@ checksum = "ac78aa94ce13e432b332a4d1bf2eff167d3a2520188ee05b337180a42fd2e62e"
dependencies = [
"cipher",
"dbl",
"digest 0.11.2",
"digest 0.11.3",
]
[[package]]
@@ -537,9 +537,9 @@ dependencies = [
[[package]]
name = "digest"
version = "0.11.2"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
"crypto-common 0.2.1",
@@ -586,7 +586,7 @@ dependencies = [
[[package]]
name = "donglora"
version = "1.5.0"
version = "1.5.2"
dependencies = [
"cfg-if",
"cortex-m",
@@ -615,7 +615,7 @@ dependencies = [
"esp-hal",
"esp-println",
"esp-rtos",
"heapless 0.9.2",
"heapless 0.9.3",
"hsmc",
"lcd-async",
"lora-phy",
@@ -630,12 +630,12 @@ dependencies = [
[[package]]
name = "donglora-protocol"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bae9e9e5a8cebc6b4167f9ef696b61ce25b94c6d8283dbe1b2e97075a481b9d"
checksum = "0eea2a87a5ad31629140accc2bae6c931434bf1946706f2184dec9b3d356505d"
dependencies = [
"defmt 1.0.1",
"heapless 0.9.2",
"heapless 0.9.3",
"ucobs",
]
@@ -853,7 +853,7 @@ dependencies = [
"embedded-io-async 0.7.0",
"futures-core",
"futures-sink",
"heapless 0.9.2",
"heapless 0.9.3",
]
[[package]]
@@ -888,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168297bf80aaf114b3c9ad589bf38b01b3009b9af7f97cd18086c5bbf96f5693"
dependencies = [
"embassy-executor-timer-queue",
"heapless 0.9.2",
"heapless 0.9.3",
]
[[package]]
@@ -905,30 +905,32 @@ dependencies = [
"embassy-time",
"embassy-usb-driver",
"embedded-io-async 0.7.0",
"heapless 0.9.2",
"heapless 0.9.3",
"ssmarshal",
"usbd-hid",
]
[[package]]
name = "embassy-usb-driver"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a"
checksum = "6a189cbf1d8ab3c591f88632f85c1df892b6ab04116037cbee39dc37cbdcd79f"
dependencies = [
"defmt 1.0.1",
"embedded-io-async 0.6.1",
"embedded-io-async 0.7.0",
]
[[package]]
name = "embassy-usb-synopsys-otg"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbe46f4083109c7ea12a03ca61095d1e87c76fec52c7ca9ee06a42935606dacb"
checksum = "fb205e26d59e483e8c48f91f637ec217ec7e2cfb0b61d50482301b991b4ff431"
dependencies = [
"critical-section",
"embassy-sync 0.8.0",
"embassy-time",
"embassy-usb-driver",
"portable-atomic",
]
[[package]]
@@ -1071,18 +1073,18 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "enumset"
version = "1.1.10"
version = "1.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634"
checksum = "7f96a4a12fe60ac746ae295a1a4ecb5bb02debc20856506c8635288065f142de"
dependencies = [
"enumset_derive",
]
[[package]]
name = "enumset_derive"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce"
checksum = "4bd536557b58c682b217b8fb199afdff47cd3eff260623f19e77074eb073d63a"
dependencies = [
"darling 0.21.3",
"proc-macro2",
@@ -1108,7 +1110,7 @@ dependencies = [
"esp-config",
"esp-metadata-generated",
"esp-println",
"heapless 0.9.2",
"heapless 0.9.3",
"riscv",
"semihosting",
"xtensa-lx",
@@ -1147,9 +1149,9 @@ dependencies = [
[[package]]
name = "esp-hal"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af8fa8216bc126941bd43b5a200a50eab16e43881ccd0dd0b6792f4a82805f0"
checksum = "2bfcf2a0842903717f4663f6a08512c32b0f6b2d7fb7db3c8a6895d2e6d49f72"
dependencies = [
"bitfield 0.19.4",
"bitflags 2.11.1",
@@ -1553,9 +1555,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heapless"
@@ -1569,9 +1571,9 @@ dependencies = [
[[package]]
name = "heapless"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed"
checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4"
dependencies = [
"defmt 1.0.1",
"hash32",
@@ -1592,9 +1594,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hsmc"
version = "0.5.1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497ed9593e4baf6ef120b24aff80cb898f6832d32cdf01be9d90be0c06624e77"
checksum = "18bb06fda289e60e17bc7483a222fc6d9d6865b92648f836242b7a79115cc91d"
dependencies = [
"defmt 0.3.100",
"embassy-futures",
@@ -1606,9 +1608,9 @@ dependencies = [
[[package]]
name = "hsmc-macros"
version = "0.5.1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49d0b1413ed69ba168f9d0edbf4a8ea21569eaf352edf4e11335ae3d765ba004"
checksum = "289a7c9f1b677168229c8ad4e992380c3951d0908b52b1d2bfae8ff3c980c35d"
dependencies = [
"proc-macro2",
"quote",
@@ -1617,9 +1619,9 @@ dependencies = [
[[package]]
name = "hybrid-array"
version = "0.4.11"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5"
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
dependencies = [
"typenum",
]
@@ -1637,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.0",
"hashbrown 0.17.1",
]
[[package]]
@@ -1812,7 +1814,7 @@ dependencies = [
[[package]]
name = "lora-modulation"
version = "0.1.5"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#e8a52ebd35352ce0267148fd392a0b1956d7735f"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#043d7659dfe2148913f16b2f7ea3e9241642b770"
dependencies = [
"defmt 0.3.100",
]
@@ -1820,7 +1822,7 @@ dependencies = [
[[package]]
name = "lora-phy"
version = "3.0.2-alpha"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#e8a52ebd35352ce0267148fd392a0b1956d7735f"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#043d7659dfe2148913f16b2f7ea3e9241642b770"
dependencies = [
"defmt 0.3.100",
"document-features",
@@ -1834,7 +1836,7 @@ dependencies = [
[[package]]
name = "lorawan"
version = "0.9.1"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#e8a52ebd35352ce0267148fd392a0b1956d7735f"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#043d7659dfe2148913f16b2f7ea3e9241642b770"
dependencies = [
"aes",
"cmac",
@@ -1846,13 +1848,13 @@ dependencies = [
[[package]]
name = "lorawan-device"
version = "0.12.2"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#e8a52ebd35352ce0267148fd392a0b1956d7735f"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#043d7659dfe2148913f16b2f7ea3e9241642b770"
dependencies = [
"defmt 0.3.100",
"document-features",
"fastrand",
"futures",
"heapless 0.9.2",
"heapless 0.9.3",
"lora-modulation",
"lorawan",
"rand_core 0.6.4",
@@ -1861,7 +1863,7 @@ dependencies = [
[[package]]
name = "lorawan-macros"
version = "0.1.0"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#e8a52ebd35352ce0267148fd392a0b1956d7735f"
source = "git+https://github.com/swaits/lora-rs.git?branch=fixed#043d7659dfe2148913f16b2f7ea3e9241642b770"
dependencies = [
"proc-macro2",
"quote",
@@ -2551,9 +2553,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]]
name = "smallvec"
+2 -2
View File
@@ -3,7 +3,7 @@ members = ["."]
[package]
name = "donglora"
version = "1.5.0"
version = "1.5.2"
edition = "2021"
build = "build.rs"
authors = ["Stephen Waits <steve@waits.net>"]
@@ -29,7 +29,7 @@ embassy-usb-driver = "0.2"
ucobs = { version = "0.3", features = ["legacy-msrv"] }
heapless = "0.9"
donglora-protocol = "1"
hsmc = { version = "0.5", default-features = false, features = ["embassy", "trace-defmt"] }
hsmc = { version = "0.6", default-features = false, features = ["embassy", "trace-defmt"] }
# Fork branch carrying CAD IRQ-clear / standby-after-CAD fixes awaiting upstream PRs.
# Switch back to crates.io once changes are upstreamed.
lora-phy = { git = "https://github.com/swaits/lora-rs.git", branch = "fixed", features = ["defmt-03"] }
+46 -26
View File
@@ -1252,29 +1252,33 @@ impl RadioActions for RadioActionContext<'_> {
Some(lora) => reconfigure_radio(lora, &cfg).await,
None => Err(RadioError::Busy),
};
if recfg_result.is_err() {
// Hardware failure: drop to Unconfigured (spec §6.3).
// Clear all per-session state — including any in-flight
// RxStart tag — so the next CmdRxStart in Unconfigured
// is rejected cleanly (rather than racing a stale tag
// through respond_err_start_rx on an unrelated path).
self.config = None;
self.cad_retry_delay_us = 0;
self.pending_rx_start_tag = None;
info!("config cleared (apply_set_config failure tag={=u16})", tag);
self.send(tag, DeviceMessage::Err(ErrorCode::ERadio)).await;
// Wedge takes precedence over ConfigFailedRadio so the
// chart enters Recovering (NRESET + lora.init()) instead
// of bouncing back to Unconfigured with a still-wedged
// chip that the next SET_CONFIG would also fail on.
if take_wedge_flag() {
let _ = self.emit(RadioInput::WedgeDetected);
} else {
let _ = self.emit(RadioInput::ConfigFailedRadio);
let mdltn = match recfg_result {
Ok(m) => m,
Err(_) => {
// Hardware failure: drop to Unconfigured (spec §6.3).
// Clear all per-session state — including any in-flight
// RxStart tag — so the next CmdRxStart in Unconfigured
// is rejected cleanly (rather than racing a stale tag
// through respond_err_start_rx on an unrelated path).
self.config = None;
self.cad_retry_delay_us = 0;
self.pending_rx_start_tag = None;
info!("config cleared (apply_set_config failure tag={=u16})", tag);
self.send(tag, DeviceMessage::Err(ErrorCode::ERadio)).await;
// Wedge takes precedence over ConfigFailedRadio so the
// chart enters Recovering (NRESET + lora.init()) instead
// of bouncing back to Unconfigured with a still-wedged
// chip that the next SET_CONFIG would also fail on.
if take_wedge_flag() {
let _ = self.emit(RadioInput::WedgeDetected);
} else {
let _ = self.emit(RadioInput::ConfigFailedRadio);
}
return;
}
return;
}
};
self.config = Some(cfg);
self.mdltn = Some(mdltn);
info!("config set (apply_set_config success tag={=u16})", tag);
self.cad_retry_delay_us = toa_us(&cfg, CAD_ASSUMED_PEER_PAYLOAD_B);
if self.cad_retry_delay_us > CAD_BUDGET_US {
@@ -2094,12 +2098,28 @@ fn to_lora_mod_params(
lora.create_modulation_params(sf, bw, cr, cfg.freq_hz)
}
async fn reconfigure_radio(lora: &mut LoRaDriver, cfg: &LoRaConfig) -> Result<(), RadioError> {
async fn reconfigure_radio(
lora: &mut LoRaDriver,
cfg: &LoRaConfig,
) -> Result<lora_phy::mod_params::ModulationParams, RadioError> {
lora_setup("reconfigure_radio.enter_standby", lora.enter_standby()).await?;
// Calibrate image etc. via a throwaway create_modulation_params call
// — inside the SET_CONFIG OK window so first TX/RX is fast.
let _ = to_lora_mod_params(lora, cfg)?;
Ok(())
// Apply the host-supplied LoRa sync word. The protocol field is a
// u16 holding the SX126x register pair in big-endian order
// (RadioLib convention: 0x1424 == byte 0x12 == LoRa "private" /
// MeshCore; 0x3444 == byte 0x34 == LoRaWAN "public"). lora-phy's
// set_lora_sync_word takes the underlying single-byte form, so
// undo the nibble encoding: convert_sync_word(byte) puts the
// chip's mandatory 0x4 in the low nibble of each register byte,
// and the high nibble of each register byte holds the original
// byte's high/low nibble respectively.
let [hi, lo] = cfg.sync_word.to_be_bytes();
let sync_byte = (hi & 0xF0) | (lo >> 4);
lora_setup(
"reconfigure_radio.set_lora_sync_word",
lora.set_lora_sync_word(sync_byte),
)
.await?;
to_lora_mod_params(lora, cfg)
}
fn validate_lora(cfg: &LoRaConfig, info: &Info) -> Result<(), ErrorCode> {