347 lines
16 KiB
Markdown
347 lines
16 KiB
Markdown
# Two LoRa profiles on one radio
|
|
|
|
`radio2` lets one LoRa transceiver alternate between two frequencies or modulation
|
|
settings. It is useful for listening to two networks, or keeping your usual
|
|
channel available during a LoRa firmware update.
|
|
|
|
There are **at most two active profiles**. `tempradio` temporarily replaces
|
|
`radio`; `tempradio2` temporarily replaces `radio2`. These are time-shared receive
|
|
windows, not simultaneous receivers. Receiving or transmitting a packet on one
|
|
profile makes the other unavailable until that packet finishes.
|
|
|
|
The second-profile commands are shared by repeater, Companion, room server,
|
|
sensor and standalone terminal-chat firmware with a LoRa radio. ESP-NOW-only
|
|
devices reject LoRa profile commands. KISS is a raw host-controlled modem and
|
|
does not expose these Mesh CLI commands or Mesh retry queues.
|
|
|
|
## Quick setup
|
|
|
|
Keep the primary channel configured with `radio`, then add the second:
|
|
|
|
```text
|
|
set radio2 910.5,500,8,5,rx
|
|
get radio2
|
|
get radio2.status
|
|
```
|
|
|
|
The tuple is **frequency in MHz, bandwidth in kHz, spreading factor, coding-rate
|
|
denominator, mode, optional preamble in symbols**. `5` means coding rate 4/5.
|
|
|
|
| Mode | Receive on profile 2 | Transmit on profile 2 |
|
|
| --- | --- | --- |
|
|
| `rx` | Yes | No |
|
|
| `rxtx` (also `rx&tx`) | Yes | Yes |
|
|
| `off` | No | No |
|
|
|
|
For transmission on both permanent profiles:
|
|
|
|
```text
|
|
set radio2 910.5,500,8,5,rxtx
|
|
```
|
|
|
|
The same encoded message is queued separately for each allowed transmit profile.
|
|
Each has its own direct/flood retry slots and channel-busy backoff. A forwarding
|
|
echo on one profile cannot cancel the other profile's retry. Packet storage and
|
|
the physical transceiver's airtime budget are shared; exhausted queues can still
|
|
drop packets.
|
|
|
|
## Keep the main channel during an update
|
|
|
|
```text
|
|
set tempradio2 910.5,500,8,5,rxtx,120
|
|
get tempradio2
|
|
```
|
|
|
|
This enables the second profile for 120 minutes (2 hours). Locally generated
|
|
LoRa OTA packets use the temporary second profile. Ordinary local traffic stays
|
|
on the primary profile by default. Use an OTA-capable build and the usual OTA
|
|
setup on every participating node; a temporary profile does not add OTA support
|
|
to firmware built without it.
|
|
|
|
New settings take effect after a short reply allowance. Temporary periods and
|
|
schedules live in RAM and disappear on reboot. Timer expiry also uses monotonic
|
|
time, so setting the clock backwards cannot extend a temporary session.
|
|
|
|
## Companion messages on both profiles
|
|
|
|
To send ordinary Companion messages on both `radio` and an active `tempradio2`,
|
|
configure the temporary profile in `rxtx` mode and enable crossing:
|
|
|
|
```text
|
|
set radio2.cross on
|
|
get tempradio2
|
|
get radio2.cross
|
|
get radio2.status
|
|
```
|
|
|
|
`rxtx` permits transmission on the second profile; `radio2.cross on` copies new
|
|
messages between the two profiles. `TX=a,b` reports primary and secondary
|
|
transmit counts. Crossing also applies to OTA traffic.
|
|
|
|
The crossing setting is saved and remains enabled after the temporary session
|
|
ends or the node reboots. Run `set radio2.cross auto` to restore the default
|
|
isolation between permanent and temporary profiles. The firmware picker's
|
|
Companion tempradio2 instructions include both choices and the status commands.
|
|
|
|
## Choose whether traffic crosses between profiles
|
|
|
|
```text
|
|
get radio2.cross
|
|
set radio2.cross auto
|
|
set radio2.cross on
|
|
set radio2.cross off
|
|
```
|
|
|
|
| Active primary | Active secondary | `auto` (default) | `on` | `off` |
|
|
| --- | --- | --- | --- | --- |
|
|
| `radio` | `radio2` | Cross | Cross | Isolated |
|
|
| `tempradio` | `tempradio2` | Cross | Cross | Isolated |
|
|
| `radio` | `tempradio2` | Isolated | Cross | Isolated |
|
|
| `tempradio` | `radio2` | Isolated | Cross | Isolated |
|
|
|
|
Crossing always respects `rx`: nothing transmits on an RX-only second profile.
|
|
With crossing allowed, a packet received there may still be forwarded on the
|
|
primary profile. Existing routing, forwarding and packet-filter settings apply.
|
|
An RX-only `tempradio2` also blocks locally generated OTA transmissions under
|
|
`auto` or `off`; they do not fall back to the normal primary channel. Use `rxtx`
|
|
for an update session that needs to exchange requests and data.
|
|
|
|
## Preamble
|
|
|
|
Append the preamble to the end of a command:
|
|
|
|
```text
|
|
set radio2 910.5,500,8,5,rxtx,88
|
|
set tempradio2 910.5,500,7,5,rxtx,120,128
|
|
set radio 909.5,62.5,7,5,32
|
|
set tempradio 909.5,62.5,7,5,120,32
|
|
```
|
|
|
|
Existing primary `radio`, `tempradio`, `radioat` and `tempradioat` commands accept
|
|
the optional trailing preamble where those primary commands are supported by
|
|
the role. Getters include the preamble. Omit it, or use `auto`/`0`, to calculate
|
|
it automatically. Explicit values are used as entered, subject to the radio's
|
|
limits and safe airtime arithmetic.
|
|
|
|
Automatic dual-profile preambles round **up to a multiple of eight**, with a
|
|
32-symbol minimum. Each idle scan starts with **4.6 symbols on the slower
|
|
profile**, then spends the remaining budgeted time on the faster profile,
|
|
also with a **4.6-symbol floor**. Slower
|
|
means a longer LoRa symbol (`2^SF / bandwidth`), regardless of profile number.
|
|
The slower preamble sets the return deadline. The calculation allows two slow
|
|
visits within that preamble, reserving 0.6 ms per switch and 0.3 ms of main-loop
|
|
margin. Fast-channel transmit preambles include sixteen acquisition symbols.
|
|
For a primary SF7 / 62.5 kHz profile:
|
|
|
|
| Secondary profile | Automatic secondary preamble | Previously tested explicit preamble | Primary preamble |
|
|
| --- | ---: | ---: | ---: |
|
|
| SF9 / 500 kHz | 32 | 32 | 32 |
|
|
| SF8 / 500 kHz | 88 | 64 | 32 |
|
|
| SF7 / 500 kHz | 64 | 80 | 32 |
|
|
|
|
With preamble 32 on that slower profile, its visit is 9.421 ms and the faster
|
|
visit is 21.847 ms, plus the actual switching time. Increasing the slow preamble
|
|
also increases the time available on the fast channel. If the symbols are equal
|
|
in length, the primary profile goes first. Detected packets hold the current
|
|
channel until reception finishes.
|
|
|
|
The SF8 / 500 kHz value also retains the measured 88-symbol floor from the
|
|
production V4/XIAO tests, where 72 and 80 each missed a packet.
|
|
The explicit values come from the
|
|
[production validation](radio_profiles_validation.md): the final fast-channel
|
|
attempts each received 50/50 packets at 4.8 slow symbols. Automatic values use
|
|
the switching and loop-jitter allowances below.
|
|
Those explicit-preamble tests used the older 4.8-symbol policy; they do not
|
|
validate the new 4.6-symbol slow dwell / 0.3 ms reserve. The
|
|
[4.6/0.3 ms two-profile bench](radio_dwell_policy_validation.md#hardware-context) received 197/200
|
|
at SF7/62.5 + SF8/500 with explicit 32-symbol preambles. The newer four-channel
|
|
5.1/6.1/7.7 tests also had misses, so this is a timing policy, not a claim of zero packet loss.
|
|
The automatic fast preamble covers the blind interval during the slow visit,
|
|
both switches and the acquisition margin. These settings do not guarantee
|
|
reception during overlapping packets or long pauses in the firmware loop.
|
|
**Transmitters on each channel also need a long enough preamble.** Changing the
|
|
receiver's setting does not lengthen packets sent by other nodes.
|
|
|
|
### Chirp calculator and warnings
|
|
|
|
`get radio2.timing` (alias `get radio.timing`) reports the active pair in
|
|
`radio,radio2` order: dwell in chirps, recommended preambles in symbols, and
|
|
the switching/loop allowances. For SF7/62.5 on `radio` and SF7/500 on `radio2`:
|
|
|
|
```text
|
|
> chirps=4.60,85.34; need=32,64; switch=600us; loop=300us (estimate); WARN recommended preamble: radio2=64
|
|
```
|
|
|
|
`WARN recommended preamble` identifies **each** profile whose recommendation
|
|
exceeds the standard 32 symbols or its explicitly selected value. For example,
|
|
with SF7/62.5 on the primary profile, `set radio2 910.5,500,7,5,rx,32` replies:
|
|
|
|
```text
|
|
OK - radio2 rx; preamble=32; WARN recommended preamble: radio2=64; short override: radio2
|
|
```
|
|
|
|
Successful `set radio ...` replies also include the recommended preamble for
|
|
each affected profile, calculated with the requested settings. A `short override`
|
|
note identifies an explicit value below the estimate; it is not silently
|
|
rewritten. Warnings are also included in profile settings/getters, scan info,
|
|
temporary settings and schedule acknowledgments. `off` disables the dual-profile
|
|
warning. A timing readout does not change settings or transmit a packet.
|
|
|
|
The shared `RadioProfiles::calculateChirpTiming()` function accepts both tuples,
|
|
optional visit times in microseconds, a per-switch allowance and loop margin.
|
|
Using microseconds throughout:
|
|
|
|
```text
|
|
T[i] = 2^SF[i] * 1000 / BW_kHz[i]
|
|
slow = profile with the longer T (radio wins ties)
|
|
L[slow] = max(requested_visit_us[slow], ceil(4.6 * T[slow]))
|
|
L[fast] = max(requested_visit_us[fast], ceil(4.6 * T[fast]))
|
|
O = 2 * switch_us + loop_margin_us
|
|
C = L[radio] + L[radio2] + O
|
|
|
|
P[slow] = round_up_to_8(max(32, 2 * C / T[slow]))
|
|
P[fast] = round_up_to_8(max(32, (L[slow] + O) / T[fast] + 16))
|
|
```
|
|
|
|
The previous SF8/500 versus SF7/62.5 measured 88-symbol floor remains in force.
|
|
Automatic selection uses the minimum visits to choose a preamble, then allocates
|
|
the fast visit from half the slow preamble's duration. Diagnostics feed the
|
|
actual allocated visits back into the same function, including longer visits
|
|
caused by an explicit slow preamble. Too-short slow preambles that cannot fit
|
|
both minimum visits remain rejected by the existing configuration validation.
|
|
|
|
The automatic scheduler uses these same role-specific minima and computes:
|
|
|
|
```text
|
|
slow_dwell = ceil(4.6 * T[slow])
|
|
fast_dwell = max(ceil(4.6 * T[fast]),
|
|
floor(P_configured[slow] * T[slow] / 2 - slow_dwell - O))
|
|
```
|
|
|
|
For the tested SF7/62.5 + SF8/500 pair with slow preamble 32, this yields
|
|
9,421 us slow and 21,847 us fast (42.67 fast chirps). Swapping `radio` and
|
|
`radio2` swaps the allocations automatically. The 0.3 ms reserve is not a sleep.
|
|
|
|
The calculator accepts board-specific allowances, but production scheduling
|
|
uses the requested **600 us per switch plus 300 us per cycle**. This is a
|
|
nominal fast-switch allowance, not a worst-case bound. Diagnostics use the
|
|
larger of 600 us and the observed maximum
|
|
switch time; an overrun increases the warning estimate without automatically
|
|
changing wire preambles or saved configuration. Recent V4 tests peaked at
|
|
836 us (827 us in the 4.6/0.3 ms run), and Indicator reached 8428 us:
|
|
both can exceed the nominal allowance.
|
|
The model preserves two slow-profile return opportunities and the existing
|
|
16-symbol fast-profile acquisition allowance. These are conservative policies,
|
|
not a fitted success curve, and neither packet overlap nor arbitrary application
|
|
stalls can be made safe by a finite preamble recommendation.
|
|
|
|
See the [implementation and validation notes](radio_dwell_policy_validation.md).
|
|
|
|
## Schedule the second profile
|
|
|
|
Use UTC Unix timestamps (seconds):
|
|
|
|
```text
|
|
set radioat2 910.5,500,8,5,rxtx,START[,PREAMBLE]
|
|
set tempradioat2 910.5,500,8,5,rxtx,START,END[,PREAMBLE]
|
|
get radioat2
|
|
get radioat2 1
|
|
get tempradioat2
|
|
get tempradioat2 1
|
|
del radioat2 1
|
|
del tempradioat2 all
|
|
```
|
|
|
|
Replace `START`, `END` and the optional `PREAMBLE` with numbers; omit the square
|
|
brackets. Each schedule family has four slots. Times must be in the future and
|
|
within 24 days. Overlapping temporary second-profile sessions are rejected.
|
|
`radioat2` changes the saved second profile when it starts; `tempradioat2`
|
|
restores the saved profile at its end. Pending schedules do not survive reboot.
|
|
|
|
## Return to one profile
|
|
|
|
```text
|
|
set radio2 off
|
|
```
|
|
|
|
This saves the disabled state and cancels all second-profile temporary activity
|
|
and schedules.
|
|
|
|
```text
|
|
set tempradio2 off
|
|
```
|
|
|
|
This ends temporary second-profile activity, cancels its temporary schedules and
|
|
restores saved `radio2`. If the saved second profile is off, this returns to one
|
|
profile. Packets/retries bound to an expired or changed session are discarded.
|
|
|
|
## Power saving and diagnostics
|
|
|
|
Dual mode suspends RX power saving and MCU idle sleep so receive visits can run
|
|
promptly. The saved RX power-saving setting returns when profile 2 is off.
|
|
The scan uses **normal receive**, following the
|
|
[CAD/RX measurements](cad_scan_validation.md), with the updated 4.6-symbol dwell on the slower
|
|
channel, followed by the remaining fast-channel visit. It does not use CAD for scanning.
|
|
The separate CAD check before an initial transmission still follows `cad`.
|
|
|
|
SX1262 dual-profile mode keeps the oscillator running in XOSC standby between
|
|
visits, so each retune does not need to restart the TCXO. It restores the prior
|
|
standby policy when returning to one profile, together with the saved RXPS
|
|
setting. Radio reinitialization temporarily uses RC standby until the TCXO has
|
|
been configured again. Other radio families retain their existing behavior.
|
|
This does not disable the TCXO supply or its initial startup delay, and it is
|
|
not conditional on 500 kHz bandwidth. Keeping the oscillator on uses additional
|
|
power during the short standby intervals.
|
|
|
|
The requested 0.6 ms switch budget is a nominal scheduling allowance, not a
|
|
forced settling delay or a measured worst-case bound. The earlier standalone
|
|
[keep-warm scan experiment](mixed_scan_validation.md) averaged about 1.19 ms
|
|
of overhead per hop (scan-cycle time minus both receive windows, divided by
|
|
two). That experiment used a leaner receive path; it is not a measurement or
|
|
guarantee of the production implementation. Measure the target board before
|
|
shortening its timing margins or transmit preambles.
|
|
|
|
The [production-path XIAO timing test](radio_profile_switch_validation.md)
|
|
measured **1.434 ms average / 1.467 ms maximum** with warm standby, compared
|
|
with **3.166 ms average** using RC standby: about **1.732 ms saved per hop**.
|
|
It covered 12,000 retunes across SF7/8/9 at 500 kHz paired with SF7/62.5 kHz.
|
|
These measurements include observing BUSY low, but exclude application/UI
|
|
scheduling and are not a packet-delivery qualification or a guarantee for
|
|
other boards, especially those using I/O expanders.
|
|
|
|
A same-image follow-up batching SF/BW/CR into one modulation command reduced
|
|
warm switching from **1.444 ms to 1.158 ms average** (batched maximum **1.198 ms**),
|
|
saving another **0.286 ms / 19.8%**. RadioLib's modulation caches and LDRO
|
|
calculation remain synchronized; frequency, preamble, packet guards, and RX
|
|
restart are unchanged. A later [HIL-only screening](radio_profile_switch_validation.md#expanded-screening-redundant-rx-setup-and-spi-v5)
|
|
tested redundant standby/IRQ/buffer/packet setup and SPI transfer changes.
|
|
Its fastest candidate measured 0.535 ms mean on XIAO but 8.203 ms on the
|
|
Indicator, whose radio GPIOs go through an I2C expander. The validated production
|
|
implementation now opts XIAO S3 WIO and Indicator LoRa into buffered 8 MHz SPI
|
|
and a guarded fast-RX state machine. It skips redundant standby, IRQ mapping,
|
|
buffer-base writes and modem queries only during an owned continuous RX-to-RX
|
|
retune. Preamble/packet parameters (including the IQ workaround), stale-IRQ
|
|
clearing, RX commands and BUSY waits remain. TX, CAD, sleep, reset, RXPS and
|
|
failed commands revoke reuse; failed RX startup rolls back the profile.
|
|
Sleep entry uses standby and wake restores the existing TCXO voltage/delay.
|
|
Other variants do not opt in to faster SPI or the fast-RX state machine.
|
|
See [production and USB validation](radio_profile_switch_validation.md#production-integration-and-usb-recovery-v8)
|
|
for current measurements, lifecycle tests and limitations.
|
|
The Indicator result also shows that the
|
|
nominal 0.6 ms allowance is not an upper bound for every board; its scheduling
|
|
and preambles need board-specific qualification. Measured overruns raise the
|
|
recommended preambles shown by the CLI without rewriting configured values.
|
|
|
|
Single-channel noise-floor calibration and RSSI interference comparison are
|
|
suspended while scanning. Retries use the radio's preamble/header activity
|
|
indicators. LR2021 extra-SF side detectors are suspended and restored as well.
|
|
|
|
`get radio2.status` reports the active second-profile mode, MeshCore RX/TX counts
|
|
for each profile, switch count, switch failures, longest measured switch and
|
|
both effective preambles. A long switch time or rising error count indicates
|
|
that the chosen settings need investigation on that board.
|
|
|
|
`get radio2.scan` shows which profile goes first, the two idle receive-window
|
|
lengths in microseconds, and both effective preambles. It reports `off` in
|
|
single-profile mode.
|