- Abort in-flight transmissions on flush so config save, frequency scan
and OTA never wait on the radio lock for a whole job
- Suspend TX during frequency scan and OTA
- Refuse commands the radio queue cannot take before consuming a rolling
code or moving simulated shade state; shift movement start when a job
goes on air late
- Apply radio config changes under the radio lock
- Re-check receiver idle before repeater transmissions
- Return 503 from web handlers when the command queue is full
- Restore 200 ms settle delay before the set-My command
- Report RSSI 0 instead of a stale value when the radio is busy
- Encode each frame once and centralise the repeat clamp
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Web command handlers ran on the async_tcp task and blocked up to 3s
waiting for loop() to bit-bang radio frames. async_tcp also services the
WebSocket on port 8080, so pongs stalled and Home Assistant
(ping_timeout=15) disconnected when several covers were commanded.
- Add a radio_tx FreeRTOS task with a job queue; sendFrame/repeatFrame
and repeater frames enqueue instead of transmitting inline.
- Serialize CC1101 access with a recursive mutex (TX task, apply, end,
frequency scan, RSSI read).
- Make Web::queueCommand fire-and-forget; drop the done semaphore.
- Route /setMyPosition and /unpairShade through the command queue.
- Clamp repeats to MAX_TX_REPEATS (40).
- Pin AsyncTCP to core 0 on dual-core targets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>