Make controller input best-effort and harden OTA rollback.

Accel/tap use fire-and-forget ESP-NOW with a 10 ms FreeRTOS-safe cadence, OTA recovers from seq gaps and auto-reboots after success, and bootloader app rollback confirms after a stable boot.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-01 22:16:28 +01:00
co-authored by Cursor
parent ae8ef40820
commit 178ab46cec
35 changed files with 236 additions and 79 deletions
+15 -5
View File
@@ -23,7 +23,12 @@
#define SLAVE_MASTER_LOST_MS (ESPNOW_HEARTBEAT_INTERVAL_MS * 5)
/** While master or slave OTA is in progress (discover may be sparse). */
#define SLAVE_MASTER_OTA_GRACE_MS 300000u
#define ESPNOW_ACCEL_INTERVAL_MS 16
/**
* Best-effort gaming input cadence (loss OK; send_best_effort).
* Must be >= one FreeRTOS tick (CONFIG_FREERTOS_HZ=100 → 10 ms); shorter
* values become 0 ticks and assert in vTaskDelayUntil.
*/
#define ESPNOW_ACCEL_INTERVAL_MS 10
#define ESPNOW_BATTERY_INTERVAL_MS 30000
#define SLAVE_BATTERY_AFTER_JOIN_MS 150
@@ -165,7 +170,8 @@ static esp_err_t send_accel_sample(const uint8_t *dest_mac, uint32_t slave_id,
msg.payload.accel_sample.x = x;
msg.payload.accel_sample.y = y;
msg.payload.accel_sample.z = z;
return esp_now_core_send(dest_mac, &msg);
/* Fire-and-forget: controller input prefers low latency over delivery. */
return esp_now_core_send_best_effort(dest_mac, &msg);
}
static esp_err_t send_tap_event(const uint8_t *dest_mac, uint32_t slave_id,
@@ -175,7 +181,7 @@ static esp_err_t send_tap_event(const uint8_t *dest_mac, uint32_t slave_id,
msg.which_payload = alox_EspNowMessage_tap_event_tag;
msg.payload.tap_event.slave_id = slave_id;
msg.payload.tap_event.kind = kind;
return esp_now_core_send(dest_mac, &msg);
return esp_now_core_send_best_effort(dest_mac, &msg);
}
static esp_err_t send_battery_report(const uint8_t *dest_mac,
@@ -579,11 +585,15 @@ static void check_master_timeout(void) {
static void accel_stream_task(void *param) {
(void)param;
ESP_LOGI(TAG, "accel stream task (interval %u ms)",
ESP_LOGI(TAG, "accel stream task (interval %u ms, best-effort)",
(unsigned)ESPNOW_ACCEL_INTERVAL_MS);
TickType_t last_wake = xTaskGetTickCount();
const TickType_t period_ticks = pdMS_TO_TICKS(ESPNOW_ACCEL_INTERVAL_MS);
/* pdMS_TO_TICKS can be 0 when ms < tick period — DelayUntil asserts. */
const TickType_t delay_ticks = period_ticks > 0 ? period_ticks : 1;
while (1) {
vTaskDelay(pdMS_TO_TICKS(ESPNOW_ACCEL_INTERVAL_MS));
vTaskDelayUntil(&last_wake, delay_ticks);
if (!s_joined || s_assigned_id == 0 || !s_accel_stream_enabled ||
!bma456_is_ready()) {
continue;