feat: Add POWERSAVE command with DFS + light sleep (v1.6)

Enable ESP-IDF power management framework (DFS 240/80 MHz + light sleep)
and add POWERSAVE command to toggle WiFi modem sleep. NVS-persisted,
default off. Automatically disabled during POWERTEST.
This commit is contained in:
user
2026-02-04 22:34:13 +01:00
parent b8f568890f
commit 47db176619
2 changed files with 71 additions and 4 deletions

View File

@@ -31,6 +31,7 @@
#include "esp_now.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "esp_pm.h"
#include "esp_heap_caps.h"
#include "esp_ota_ops.h"
#include "esp_https_ota.h"
@@ -164,6 +165,7 @@ static int s_deauth_ring_count = 0;
/* Power test */
static volatile bool s_powertest_running = false;
static bool s_powersave = false;
/* Probe dedup rate (moved before config_load_nvs for NVS access) */
#define PROBE_DEDUP_DEFAULT_US 10000000LL
@@ -236,10 +238,14 @@ static void config_load_nvs(void)
if (nvs_get_i32(h, "probe_rate", &probe_r) == ESP_OK && probe_r >= 1 && probe_r <= 300) {
s_probe_dedup_us = (int64_t)probe_r * 1000000LL;
}
int8_t powersave;
if (nvs_get_i8(h, "powersave", &powersave) == ESP_OK) {
s_powersave = (powersave != 0);
}
nvs_close(h);
ESP_LOGI(TAG, "NVS loaded: hostname=%s rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d target=%s:%d csi_mode=%d hybrid_n=%d",
ESP_LOGI(TAG, "NVS loaded: hostname=%s rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d target=%s:%d csi_mode=%d hybrid_n=%d powersave=%d",
s_hostname, s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled,
s_target_ip, s_target_port, (int)s_csi_mode, s_hybrid_interval);
s_target_ip, s_target_port, (int)s_csi_mode, s_hybrid_interval, s_powersave);
} else {
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
}
@@ -1108,11 +1114,18 @@ static void powertest_task(void *arg)
bool saved_ble = s_ble_enabled;
int8_t saved_tx_power = s_tx_power_dbm;
led_mode_t saved_led = s_led_mode;
bool saved_powersave = s_powersave;
/* Disable adaptive during test */
s_adaptive = false;
s_motion_detected = false;
/* Disable powersave during test */
if (s_powersave) {
s_powersave = false;
esp_wifi_set_ps(WIFI_PS_NONE);
}
typedef struct {
const char *name;
int rate; /* 0 = stop ping */
@@ -1188,6 +1201,9 @@ static void powertest_task(void *arg)
}
wifi_ping_router_start();
s_powersave = saved_powersave;
esp_wifi_set_ps(s_powersave ? WIFI_PS_MIN_MODEM : WIFI_PS_NONE);
int total_s = dwell_s * n_phases;
send_powertest_event("done", total_s);
ESP_LOGI(TAG, "POWERTEST: done total=%ds", total_s);
@@ -1256,7 +1272,7 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
"OK STATUS uptime=%s uptime_s=%lld heap=%lu rssi=%d channel=%d tx_power=%d rate=%d csi_rate=%d"
" hostname=%s version=%s adaptive=%s motion=%d ble=%s target=%s:%d"
" temp=%.1f csi_count=%lu boots=%lu rssi_min=%d rssi_max=%d"
" csi_mode=%s hybrid_n=%d auth=%s flood_thresh=%d/%d",
" csi_mode=%s hybrid_n=%d auth=%s flood_thresh=%d/%d powersave=%s",
uptime_str, (long long)up, (unsigned long)heap, rssi, channel, (int)s_tx_power_dbm,
s_send_frequency, actual_rate,
s_hostname, app_desc->version,
@@ -1266,7 +1282,8 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
(int)s_rssi_min, (int)s_rssi_max,
csi_mode_str, s_hybrid_interval,
s_auth_secret[0] ? "on" : "off",
s_flood_thresh, s_flood_window_s);
s_flood_thresh, s_flood_window_s,
s_powersave ? "on" : "off");
return strlen(reply);
}
@@ -1616,6 +1633,29 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
return strlen(reply);
}
/* POWERSAVE [ON|OFF] */
if (strcmp(cmd, "POWERSAVE") == 0) {
snprintf(reply, reply_size, "OK POWERSAVE %s", s_powersave ? "on" : "off");
return strlen(reply);
}
if (strncmp(cmd, "POWERSAVE ", 10) == 0) {
const char *arg = cmd + 10;
if (strncmp(arg, "ON", 2) == 0) {
s_powersave = true;
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
config_save_i8("powersave", 1);
snprintf(reply, reply_size, "OK POWERSAVE on (modem sleep)");
} else if (strncmp(arg, "OFF", 3) == 0) {
s_powersave = false;
esp_wifi_set_ps(WIFI_PS_NONE);
config_save_i8("powersave", 0);
snprintf(reply, reply_size, "OK POWERSAVE off");
} else {
snprintf(reply, reply_size, "ERR POWERSAVE ON or OFF");
}
return strlen(reply);
}
snprintf(reply, reply_size, "ERR UNKNOWN");
return strlen(reply);
}
@@ -1743,6 +1783,27 @@ void app_main()
esp_wifi_set_max_tx_power(s_tx_power_dbm * 4);
ESP_LOGI(TAG, "TX power set to %d dBm", (int)s_tx_power_dbm);
/* Power management: DFS (240→80 MHz) + light sleep */
esp_pm_config_t pm_config = {
.max_freq_mhz = 240,
.min_freq_mhz = 80,
.light_sleep_enable = true,
};
esp_err_t pm_err = esp_pm_configure(&pm_config);
if (pm_err == ESP_OK) {
ESP_LOGI(TAG, "PM: DFS 240/80 MHz, light sleep enabled");
} else {
ESP_LOGW(TAG, "PM configure failed: %s", esp_err_to_name(pm_err));
}
/* Apply saved WiFi power save mode */
if (s_powersave) {
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
ESP_LOGI(TAG, "WiFi modem sleep enabled");
} else {
esp_wifi_set_ps(WIFI_PS_NONE);
}
/* Chip temperature sensor (ESP32-S2/S3/C3/C6 only) */
#if SOC_TEMP_SENSOR_SUPPORTED
temperature_sensor_config_t temp_cfg = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);

View File

@@ -76,3 +76,9 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_ESP_WIFI_IRAM_OPT=n
#
# Power Management
#
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y