From 3f55c95b34cfd1045227c33a2ee243ac230ac5bd Mon Sep 17 00:00:00 2001 From: ruv Date: Mon, 11 May 2026 10:47:48 -0400 Subject: [PATCH] fix(esp32): disable WiFi modem sleep so CSI capture isn't starved (#521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit csi_collector_init() never called esp_wifi_set_ps(), leaving the radio on the ESP-IDF STA default WIFI_PS_MIN_MODEM. The modem then sleeps between DTIM beacons; combined with the MGMT-only promiscuous filter (#396) the CSI callback is starved and the per-second yield collapses toward 0 pps, which is what users on a clean multi-node setup were seeing (motion=0.00 presence=0.00 yield=0pps). Force WIFI_PS_NONE before enabling promiscuous mode — the textbook requirement for reliable CSI capture (every ESP-IDF CSI example does it). New boot line: "csi_collector: WiFi modem sleep disabled (WIFI_PS_NONE) for CSI capture". Battery duty-cycling is unaffected: power_mgmt_init() runs after this and re-enables modem sleep when provision.py is given --duty-cycle <100. Builds clean for esp32s3 (idf.py build, 48% flash free). Closes #521 Co-Authored-By: claude-flow --- firmware/esp32-csi-node/main/csi_collector.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/firmware/esp32-csi-node/main/csi_collector.c b/firmware/esp32-csi-node/main/csi_collector.c index c8d5eb7d..5fadb445 100644 --- a/firmware/esp32-csi-node/main/csi_collector.c +++ b/firmware/esp32-csi-node/main/csi_collector.c @@ -336,6 +336,21 @@ void csi_collector_init(void) /* Update the hop table's first channel to match. */ s_hop_channels[0] = csi_channel; + /* Disable WiFi modem sleep — reliable CSI capture needs the radio awake. + * The ESP-IDF STA default is WIFI_PS_MIN_MODEM, which lets the modem + * sleep between DTIM beacons; with the MGMT-only promiscuous filter + * (RuView#396) that starves the CSI callback and the per-second yield + * collapses toward 0 pps (RuView#521). Operators who want battery + * duty-cycling opt back in via power_mgmt_init() (provision.py + * --duty-cycle ), which runs after this and re-enables modem sleep. */ + esp_err_t ps_err = esp_wifi_set_ps(WIFI_PS_NONE); + if (ps_err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_set_ps(WIFI_PS_NONE) failed: %s — CSI yield may be low", + esp_err_to_name(ps_err)); + } else { + ESP_LOGI(TAG, "WiFi modem sleep disabled (WIFI_PS_NONE) for CSI capture"); + } + /* Enable promiscuous mode — required for reliable CSI callbacks. * Without this, CSI only fires on frames destined to this station, * which may be very infrequent on a quiet network. */