feat: Add POWERTEST command, update roadmap with v2.0 FTM milestone
Add 7-phase power profiling command (POWERTEST) that cycles through idle, CSI 10/100 Hz, BLE-only, combined, tx_low/tx_high with EVENT markers for external power meter correlation. Saves/restores all settings on completion. Update roadmap: mark v1.4 done, add v2.0 hardware upgrade milestone for ESP32-S3/C6 with WiFi FTM / 802.11mc inter-sensor ranging.
This commit is contained in:
@@ -162,6 +162,9 @@ static struct { int64_t ts; } s_deauth_ring[FLOOD_RING_SIZE];
|
||||
static int s_deauth_ring_head = 0;
|
||||
static int s_deauth_ring_count = 0;
|
||||
|
||||
/* Power test */
|
||||
static volatile bool s_powertest_running = false;
|
||||
|
||||
/* --- NVS helpers --- */
|
||||
|
||||
static void config_load_nvs(void)
|
||||
@@ -1071,6 +1074,117 @@ static void reboot_after_delay(void *arg)
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* --- Power test --- */
|
||||
|
||||
static void send_powertest_event(const char *phase, int dwell_s)
|
||||
{
|
||||
char buf[128];
|
||||
int len = snprintf(buf, sizeof(buf), "EVENT,%s,powertest,%s,%d\n",
|
||||
s_hostname, phase, dwell_s);
|
||||
if (s_udp_socket >= 0) {
|
||||
sendto(s_udp_socket, buf, len, 0,
|
||||
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||
}
|
||||
}
|
||||
|
||||
static void powertest_task(void *arg)
|
||||
{
|
||||
int dwell_s = (int)(intptr_t)arg;
|
||||
|
||||
/* Save current settings */
|
||||
int saved_freq = s_send_frequency;
|
||||
bool saved_adaptive = s_adaptive;
|
||||
bool saved_ble = s_ble_enabled;
|
||||
int8_t saved_tx_power = s_tx_power_dbm;
|
||||
led_mode_t saved_led = s_led_mode;
|
||||
|
||||
/* Disable adaptive during test */
|
||||
s_adaptive = false;
|
||||
s_motion_detected = false;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
int rate; /* 0 = stop ping */
|
||||
bool ble;
|
||||
int8_t tx_dbm; /* 0 = no change */
|
||||
bool led;
|
||||
} phase_t;
|
||||
|
||||
static const phase_t phases[] = {
|
||||
{ "idle", 0, false, 0, false },
|
||||
{ "csi_10", 10, false, 0, true },
|
||||
{ "csi_100", 100, false, 0, true },
|
||||
{ "ble_only", 0, true, 0, true },
|
||||
{ "all", 100, true, 0, true },
|
||||
{ "tx_low", 100, false, 2, true },
|
||||
{ "tx_high", 100, false, 20, true },
|
||||
};
|
||||
int n_phases = sizeof(phases) / sizeof(phases[0]);
|
||||
|
||||
ESP_LOGI(TAG, "POWERTEST: starting %d phases, dwell=%ds", n_phases, dwell_s);
|
||||
|
||||
for (int i = 0; i < n_phases; i++) {
|
||||
const phase_t *p = &phases[i];
|
||||
|
||||
send_powertest_event(p->name, dwell_s);
|
||||
ESP_LOGI(TAG, "POWERTEST: phase %s (rate=%d ble=%d tx=%d)", p->name, p->rate, p->ble, p->tx_dbm);
|
||||
|
||||
/* LED */
|
||||
s_led_mode = p->led ? LED_FAST_BLINK : LED_OFF;
|
||||
|
||||
/* BLE */
|
||||
if (p->ble && !s_ble_enabled) {
|
||||
s_ble_enabled = true;
|
||||
ble_scan_start();
|
||||
} else if (!p->ble && s_ble_enabled) {
|
||||
s_ble_enabled = false;
|
||||
ble_gap_disc_cancel();
|
||||
}
|
||||
|
||||
/* TX power */
|
||||
if (p->tx_dbm > 0) {
|
||||
s_tx_power_dbm = p->tx_dbm;
|
||||
esp_wifi_set_max_tx_power(s_tx_power_dbm * 4);
|
||||
}
|
||||
|
||||
/* Ping rate */
|
||||
if (p->rate > 0) {
|
||||
s_send_frequency = p->rate;
|
||||
wifi_ping_router_start();
|
||||
} else {
|
||||
if (s_ping_handle) {
|
||||
esp_ping_stop(s_ping_handle);
|
||||
esp_ping_delete_session(s_ping_handle);
|
||||
s_ping_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(dwell_s * 1000));
|
||||
}
|
||||
|
||||
/* Restore settings */
|
||||
s_adaptive = saved_adaptive;
|
||||
s_ble_enabled = saved_ble;
|
||||
s_tx_power_dbm = saved_tx_power;
|
||||
esp_wifi_set_max_tx_power(s_tx_power_dbm * 4);
|
||||
s_led_mode = saved_led;
|
||||
s_send_frequency = saved_freq;
|
||||
|
||||
if (saved_ble) {
|
||||
ble_scan_start();
|
||||
} else {
|
||||
ble_gap_disc_cancel();
|
||||
}
|
||||
wifi_ping_router_start();
|
||||
|
||||
int total_s = dwell_s * n_phases;
|
||||
send_powertest_event("done", total_s);
|
||||
ESP_LOGI(TAG, "POWERTEST: done total=%ds", total_s);
|
||||
|
||||
s_powertest_running = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
||||
{
|
||||
/* REBOOT */
|
||||
@@ -1469,6 +1583,26 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
||||
return strlen(reply);
|
||||
}
|
||||
|
||||
/* POWERTEST [dwell_s] */
|
||||
if (strncmp(cmd, "POWERTEST", 9) == 0) {
|
||||
if (s_powertest_running) {
|
||||
snprintf(reply, reply_size, "ERR POWERTEST already running");
|
||||
return strlen(reply);
|
||||
}
|
||||
int dwell = 15;
|
||||
if (cmd[9] == ' ') {
|
||||
dwell = atoi(cmd + 10);
|
||||
if (dwell < 5 || dwell > 60) {
|
||||
snprintf(reply, reply_size, "ERR POWERTEST dwell range 5-60");
|
||||
return strlen(reply);
|
||||
}
|
||||
}
|
||||
s_powertest_running = true;
|
||||
xTaskCreate(powertest_task, "powertest", 4096, (void *)(intptr_t)dwell, 3, NULL);
|
||||
snprintf(reply, reply_size, "OK POWERTEST started dwell=%ds phases=7 total=~%ds", dwell, dwell * 7);
|
||||
return strlen(reply);
|
||||
}
|
||||
|
||||
snprintf(reply, reply_size, "ERR UNKNOWN");
|
||||
return strlen(reply);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user