fix(esp32): disable WiFi modem sleep so CSI capture isn't starved (#521)

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 <ruv@ruv.net>
This commit is contained in:
ruv 2026-05-11 10:47:48 -04:00
parent e7904786f0
commit 3f55c95b34
1 changed files with 15 additions and 0 deletions

View File

@ -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 <N>), 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. */