fix: move radio TX to a dedicated task so HA WebSocket stays connected

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>
This commit is contained in:
cjkas 2026-10-10 20:13:31 +02:00
parent b117c34cb8
commit 9a9afa5e86
5 changed files with 202 additions and 81 deletions

View file

@ -6,8 +6,9 @@
#ifndef webserver_h
#define webserver_h
#define WEB_CMD_QUEUE_SIZE 8
#define WEB_CMD_TIMEOUT_MS 3000
// Commands are queued by the async web server and drained by the main loop.
// Big enough for Home Assistant firing a command at every shade at once.
#define WEB_CMD_QUEUE_SIZE 32
enum class web_cmd_t : uint8_t {
shade_command, // moveToTarget or sendCommand
@ -18,6 +19,8 @@ enum class web_cmd_t : uint8_t {
set_positions, // set shade position directly
shade_sensor, // shade sensor command
group_sensor, // group sensor command
set_my_position, // shade setMyPosition (position/tiltPosition)
unpair_shade, // shade unpair (Prog) and clear paired flag
};
struct web_command_t {
@ -67,8 +70,8 @@ class Web {
void handleBackup(AsyncWebServerRequest *request);
void handleReboot(AsyncWebServerRequest *request);
void handleNotFound(AsyncWebServerRequest *request);
bool queueCommand(const web_command_t &cmd);
private:
void processQueue();
bool queueCommand(const web_command_t &cmd);
};
#endif