|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Status: Accepted, amended 2026-10 (see Amendment) · Recorded: 2026-09
The 868 MHz band's listen-before-talk rule asks a transmitter to check that the channel it is about to use is clear. The exchange engine did the check itself: before each frame it called read_rssi(), retried up to lbt_max_retries times while the reading was at or above lbt_rssi_threshold_dbm, then called send_packet(), which retuned to the frame's channel and sent.
read_rssi() reads the channel the receiver is on. While the hub is idle the receiver hops over all three channels, and every frame names its own channel, so for the first frame after idle hopping the check usually measured a different channel from the one the frame then went out on. A busy command channel could pass the check because the receiver sat on a quiet one, and a quiet command channel could be delayed by traffic elsewhere.
A separate RSSI reading also assumes the host can read the radio's receiver directly and immediately before sending, which not every radio arrangement allows.
Recorded: 2026-10. Issue #178: a wired Somfy OXIMO 50 io roller shutter on a Heltec V2 (SX1276). Across the reporter's four logs, every challenge response (0x3D) that listen-before-talk held back by at least one back-off got no final reply (25 of 25), and every one sent without a delay got one (33 of 33). A single 5 ms back-off was enough to lose the exchange. Back-offs before the command itself did no harm: the shutter still answered with its challenge.
The device's window is device-specific. The corpus capture somfy_awning_exchange_open_lbt_contention_sx1276 shows a Somfy Sunea io awning answering a 0x3D that was held back by four to five back-offs. A strict receiver is enough to break the exchange, on any chip, since the policy lives in transmit_frame().
The check cannot help a frame that answers within a session. The peer has just transmitted on this channel and is listening there for the answer, and after the last busy result the frame goes out anyway. So the check can only delay the answer, possibly past the peer's window.
Decision. transmit_frame() checks the channel only for a frame with the CTRL0 START bit set, the frame that opens a session. A frame without START goes out at once with no cca_threshold_dbm, on the channel its caller names. The session's opening frame was checked by whoever sent it. The frame builders already set START this way: every command, discovery, key-init and broadcast request carries it, and the 0x3D, an inbound 0x3C, the key transfer 0x32 and the in-session acknowledgements do not. The rule needs no per-call-site flag and no new tunable. It is the same signal tx_wake_for() already reads (ADR 0044).
Consequences.