Compare commits

..

2 Commits

Author SHA1 Message Date
a0f6e875be Slow the spinel link down so it survives traffic bursts
otbr-agent ran for hours, then died on a burst of Matter traffic:

  [W] P-RadioSpinel-: radio tx timeout
  [C] P-RadioSpinel-: Failed to communicate with RCP
  otbr-agent exited with code 6

The RCP itself was fine -- its frame timestamps showed 18 hours of
uninterrupted uptime across the crash, so nothing on the device reset.
The host simply lost bytes and desynchronised.

Nordic's nrf54l15dk overlay runs uart20 at 1 Mbaud, and this board's
overlay copied that, but the DK pairs it with hw-flow-control. The XIAO
cannot: uart20's pinctrl defines only TX and RX, and the CMSIS-DAP
bridge carries no RTS/CTS. Without back-pressure a single late RX
interrupt drops a byte, and a dropped byte corrupts the HDLC frame
around it.

460800 keeps roughly 4x headroom over peak 802.15.4 throughput while
widening the per-byte service window from 10 us to 21.7 us. The receive
queue goes to 8192 bytes for the same reason -- with no flow control,
whatever does not fit is lost rather than deferred.

The RadioURL on the border router host must set the matching
uart-baudrate=460800.
2026-08-22 14:20:57 +02:00
0ce983b5ed Power the RF switch and transmit at full power
Two defects, both specific to running this sample on a XIAO rather than a DK,
and both only on the border router side -- which is why the link was so lopsided
and why a Matter device could attach but not sustain SRP updates.

The coprocessor sample sets CONFIG_GPIO=n, which is reasonable on a DK whose RCP
has no GPIO-controlled RF hardware. This board selects its antenna path with
one: P2.03 powers the RF switch and P2.05 picks ceramic or external, declared as
regulator-fixed nodes with regulator-boot-on. With GPIO off, REGULATOR_FIXED
cannot build (it depends on GPIO), so those nodes got no driver, neither pin was
ever driven, and the border router ran with an unpowered antenna switch. The
devicetree looks correct throughout, which is what made this hard to see.

Separately, Zephyr defaults OPENTHREAD_DEFAULT_TX_POWER to 0 dBm and the sample
never overrides it, while Nordic's Matter samples use 8 -- so the border router
was transmitting 8 dB weaker than the devices talking to it. That asymmetry hits
beacon responses during a joiner's active scan, which is exactly what kept
failing.

Measured: -93 dBm at close range before, -71 dBm after, against a -101 dBm
sensitivity floor.
2026-08-21 22:12:23 +02:00
2 changed files with 55 additions and 2 deletions

View File

@@ -16,3 +16,37 @@ CONFIG_LOG_MAX_LEVEL=1
CONFIG_LOG_BACKEND_UART=n CONFIG_LOG_BACKEND_UART=n
CONFIG_LOG_BACKEND_SPINEL=y CONFIG_LOG_BACKEND_SPINEL=y
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048 CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
# Transmit at the nRF54L15's full +8 dBm.
#
# Zephyr's default is 0 dBm and the coprocessor sample never overrides it, while
# Nordic's Matter samples set 8 -- so an RCP built from this repo was running 8 dB
# weaker than the Matter devices talking to it. That asymmetry hurts the
# border-router-to-device direction specifically, which is the one that matters
# for beacon responses during a joiner's active scan.
CONFIG_OPENTHREAD_DEFAULT_TX_POWER=8
# The XIAO needs GPIO, unlike the DKs this sample targets.
#
# The upstream coprocessor prj.conf sets CONFIG_GPIO=n ("Disable GPIO"), which
# is fine on a DK whose RCP has no GPIO-controlled RF hardware. This board
# selects its antenna path with one: P2.03 powers the RF switch and P2.05
# selects ceramic vs external, both declared as regulator-fixed nodes with
# regulator-boot-on in the board DTS.
#
# With GPIO off, REGULATOR_FIXED cannot build (it depends on GPIO), so those
# nodes get no driver and neither pin is ever driven -- the RF switch stays
# unpowered and the border router runs with a crippled antenna path. Measured
# -93 dBm at close range before this was found, against a -101 dBm sensitivity
# floor.
CONFIG_GPIO=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED=y
# Give the spinel link a deeper receive queue.
#
# The default 2048 bytes is sized for a link with RTS/CTS, where the host stops
# sending once the buffer fills. This one has no flow control, so anything that
# does not fit is simply lost. 8192 covers a burst of full-size frames arriving
# while the OpenThread thread is busy.
CONFIG_OPENTHREAD_COPROCESSOR_UART_RING_BUFFER_SIZE=8192

View File

@@ -14,7 +14,26 @@
* *
* Unlike Nordic's nrf54l15dk overlay, hw-flow-control is NOT set: the XIAO's * Unlike Nordic's nrf54l15dk overlay, hw-flow-control is NOT set: the XIAO's
* pinctrl only defines TX and RX for uart20, and the CMSIS-DAP bridge does not * pinctrl only defines TX and RX for uart20, and the CMSIS-DAP bridge does not
* carry RTS/CTS. See docs/ for the baud rate this was validated at. * carry RTS/CTS.
*
* That missing back-pressure is why the spinel link runs at 460800 rather than
* the 1000000 Nordic uses. Bandwidth was never the constraint -- 802.15.4 tops
* out around 250 kbps -- but without RTS/CTS a single late RX interrupt loses a
* byte outright, and a lost byte corrupts the HDLC frame around it. At 1 Mbaud
* the driver has 10 us to service each byte; the radio ISR can hold it off
* longer than that during a burst. 460800 widens the window to 21.7 us while
* still leaving roughly 4x headroom over peak Thread throughput.
*
* The symptom this fixes: otbr-agent runs for hours, then a traffic burst
* desynchronises the stream, the next transmit gets no response, and
* HandleRcpTimeout aborts the agent with exit code 6.
*
* [W] P-RadioSpinel-: radio tx timeout
* [C] P-RadioSpinel-: Failed to communicate with RCP - no response from RCP
*
* The host must agree on the rate. In the RadioURL on the border router host:
*
* spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800
*/ */
/ { / {
@@ -27,7 +46,7 @@
&uart20 { &uart20 {
status = "okay"; status = "okay";
current-speed = <1000000>; current-speed = <460800>;
}; };
&uart21 { &uart21 {