feat: Add ALERT command for temp/heap threshold monitoring
ALERT TEMP <celsius> and ALERT HEAP <bytes> emit EVENT packets when thresholds are crossed (60s holdoff). NVS-persisted, shown in STATUS and CONFIG. Temp alerts require SOC_TEMP_SENSOR_SUPPORTED.
This commit is contained in:
@@ -211,6 +211,15 @@ static int64_t s_chanscan_last = 0;
|
|||||||
#define PROBE_DEDUP_DEFAULT_US 10000000LL
|
#define PROBE_DEDUP_DEFAULT_US 10000000LL
|
||||||
static int64_t s_probe_dedup_us = PROBE_DEDUP_DEFAULT_US;
|
static int64_t s_probe_dedup_us = PROBE_DEDUP_DEFAULT_US;
|
||||||
|
|
||||||
|
/* Alert thresholds (0 = disabled) */
|
||||||
|
#define ALERT_HOLDOFF_US 60000000LL /* 60s debounce between alerts */
|
||||||
|
static float s_alert_temp_thresh = 0.0f; /* celsius, e.g. 70.0 */
|
||||||
|
static uint32_t s_alert_heap_thresh = 0; /* bytes, e.g. 30000 */
|
||||||
|
#if SOC_TEMP_SENSOR_SUPPORTED
|
||||||
|
static int64_t s_alert_temp_last = 0;
|
||||||
|
#endif
|
||||||
|
static int64_t s_alert_heap_last = 0;
|
||||||
|
|
||||||
/* --- NVS helpers --- */
|
/* --- NVS helpers --- */
|
||||||
|
|
||||||
static void config_load_nvs(void)
|
static void config_load_nvs(void)
|
||||||
@@ -313,11 +322,20 @@ static void config_load_nvs(void)
|
|||||||
if (nvs_get_i8(h, "csi_enabled", &csi_en) == ESP_OK) {
|
if (nvs_get_i8(h, "csi_enabled", &csi_en) == ESP_OK) {
|
||||||
s_csi_enabled = (csi_en != 0);
|
s_csi_enabled = (csi_en != 0);
|
||||||
}
|
}
|
||||||
|
int32_t alert_temp;
|
||||||
|
if (nvs_get_i32(h, "alert_temp", &alert_temp) == ESP_OK && alert_temp > 0) {
|
||||||
|
s_alert_temp_thresh = (float)alert_temp / 10.0f; /* stored as 10ths of degree */
|
||||||
|
}
|
||||||
|
int32_t alert_heap;
|
||||||
|
if (nvs_get_i32(h, "alert_heap", &alert_heap) == ESP_OK && alert_heap > 0) {
|
||||||
|
s_alert_heap_thresh = (uint32_t)alert_heap;
|
||||||
|
}
|
||||||
nvs_close(h);
|
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 powersave=%d presence=%d pr_thresh=%.4f baseline_nsub=%d led_quiet=%d csi=%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 presence=%d pr_thresh=%.4f baseline_nsub=%d led_quiet=%d csi=%d alert_temp=%.1f alert_heap=%lu",
|
||||||
s_hostname, s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled,
|
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_powersave,
|
s_target_ip, s_target_port, (int)s_csi_mode, s_hybrid_interval, s_powersave,
|
||||||
s_presence_enabled, s_pr_threshold, s_baseline_nsub, s_led_quiet, s_csi_enabled);
|
s_presence_enabled, s_pr_threshold, s_baseline_nsub, s_led_quiet, s_csi_enabled,
|
||||||
|
s_alert_temp_thresh, (unsigned long)s_alert_heap_thresh);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
|
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
|
||||||
}
|
}
|
||||||
@@ -992,6 +1010,53 @@ static void adaptive_task(void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Alert threshold checks */
|
||||||
|
{
|
||||||
|
int64_t now_alert = esp_timer_get_time();
|
||||||
|
|
||||||
|
/* Heap alert */
|
||||||
|
if (s_alert_heap_thresh > 0) {
|
||||||
|
uint32_t free_heap = esp_get_free_heap_size();
|
||||||
|
if (free_heap < s_alert_heap_thresh &&
|
||||||
|
(now_alert - s_alert_heap_last) > ALERT_HOLDOFF_US) {
|
||||||
|
s_alert_heap_last = now_alert;
|
||||||
|
char event[128];
|
||||||
|
int len = snprintf(event, sizeof(event),
|
||||||
|
"EVENT,%s,alert=heap free=%lu thresh=%lu",
|
||||||
|
s_hostname, (unsigned long)free_heap,
|
||||||
|
(unsigned long)s_alert_heap_thresh);
|
||||||
|
if (s_udp_socket >= 0) {
|
||||||
|
sendto(s_udp_socket, event, len, 0,
|
||||||
|
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "ALERT: heap low (%lu < %lu)",
|
||||||
|
(unsigned long)free_heap, (unsigned long)s_alert_heap_thresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temperature alert */
|
||||||
|
#if SOC_TEMP_SENSOR_SUPPORTED
|
||||||
|
if (s_alert_temp_thresh > 0.0f && s_temp_handle) {
|
||||||
|
float temp = 0.0f;
|
||||||
|
if (temperature_sensor_get_celsius(s_temp_handle, &temp) == ESP_OK &&
|
||||||
|
temp > s_alert_temp_thresh &&
|
||||||
|
(now_alert - s_alert_temp_last) > ALERT_HOLDOFF_US) {
|
||||||
|
s_alert_temp_last = now_alert;
|
||||||
|
char event[128];
|
||||||
|
int len = snprintf(event, sizeof(event),
|
||||||
|
"EVENT,%s,alert=temp value=%.1f thresh=%.1f",
|
||||||
|
s_hostname, temp, s_alert_temp_thresh);
|
||||||
|
if (s_udp_socket >= 0) {
|
||||||
|
sendto(s_udp_socket, event, len, 0,
|
||||||
|
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "ALERT: temp high (%.1f > %.1f)",
|
||||||
|
temp, s_alert_temp_thresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (!s_adaptive || s_energy_idx < WANDER_WINDOW) continue;
|
if (!s_adaptive || s_energy_idx < WANDER_WINDOW) continue;
|
||||||
|
|
||||||
/* Compute mean */
|
/* Compute mean */
|
||||||
@@ -1602,6 +1667,7 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
" temp=%.1f csi_count=%lu boots=%lu rssi_min=%d rssi_max=%d"
|
" temp=%.1f csi_count=%lu boots=%lu rssi_min=%d rssi_max=%d"
|
||||||
" csi=%s csi_mode=%s hybrid_n=%d auth=%s flood_thresh=%d/%d powersave=%s"
|
" csi=%s csi_mode=%s hybrid_n=%d auth=%s flood_thresh=%d/%d powersave=%s"
|
||||||
" presence=%s pr_score=%.4f chanscan=%s led=%s"
|
" presence=%s pr_score=%.4f chanscan=%s led=%s"
|
||||||
|
" alert_temp=%.1f alert_heap=%lu"
|
||||||
" nvs_used=%lu nvs_free=%lu nvs_total=%lu part_size=%lu"
|
" nvs_used=%lu nvs_free=%lu nvs_total=%lu part_size=%lu"
|
||||||
" built=%s_%s idf=%s chip=%sr%dc%d",
|
" built=%s_%s idf=%s chip=%sr%dc%d",
|
||||||
uptime_str, (long long)up, (unsigned long)heap, rssi, channel, (int)s_tx_power_dbm,
|
uptime_str, (long long)up, (unsigned long)heap, rssi, channel, (int)s_tx_power_dbm,
|
||||||
@@ -1617,6 +1683,7 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
s_powersave ? "on" : "off",
|
s_powersave ? "on" : "off",
|
||||||
s_presence_enabled ? "on" : "off", s_pr_last_score,
|
s_presence_enabled ? "on" : "off", s_pr_last_score,
|
||||||
s_chanscan_enabled ? "on" : "off", s_led_quiet ? "quiet" : "auto",
|
s_chanscan_enabled ? "on" : "off", s_led_quiet ? "quiet" : "auto",
|
||||||
|
s_alert_temp_thresh, (unsigned long)s_alert_heap_thresh,
|
||||||
(unsigned long)nvs_stats.used_entries,
|
(unsigned long)nvs_stats.used_entries,
|
||||||
(unsigned long)nvs_stats.free_entries,
|
(unsigned long)nvs_stats.free_entries,
|
||||||
(unsigned long)nvs_stats.total_entries,
|
(unsigned long)nvs_stats.total_entries,
|
||||||
@@ -2194,6 +2261,48 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
return strlen(reply);
|
return strlen(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ALERT — set temp/heap alert thresholds */
|
||||||
|
if (strcmp(cmd, "ALERT") == 0) {
|
||||||
|
snprintf(reply, reply_size, "OK ALERT temp=%.1f heap=%lu (0=off)",
|
||||||
|
s_alert_temp_thresh, (unsigned long)s_alert_heap_thresh);
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
if (strncmp(cmd, "ALERT ", 6) == 0) {
|
||||||
|
const char *arg = cmd + 6;
|
||||||
|
if (strncmp(arg, "TEMP ", 5) == 0) {
|
||||||
|
float val = strtof(arg + 5, NULL);
|
||||||
|
if (val < 0 || val > 125) {
|
||||||
|
snprintf(reply, reply_size, "ERR ALERT TEMP range 0-125 (0=off)");
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
s_alert_temp_thresh = val;
|
||||||
|
config_save_i32("alert_temp", (int32_t)(val * 10.0f));
|
||||||
|
snprintf(reply, reply_size, "OK ALERT TEMP %.1f", val);
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
if (strncmp(arg, "HEAP ", 5) == 0) {
|
||||||
|
int val = atoi(arg + 5);
|
||||||
|
if (val < 0 || val > 300000) {
|
||||||
|
snprintf(reply, reply_size, "ERR ALERT HEAP range 0-300000 (0=off)");
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
s_alert_heap_thresh = (uint32_t)val;
|
||||||
|
config_save_i32("alert_heap", val);
|
||||||
|
snprintf(reply, reply_size, "OK ALERT HEAP %d", val);
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
if (strcmp(arg, "OFF") == 0) {
|
||||||
|
s_alert_temp_thresh = 0.0f;
|
||||||
|
s_alert_heap_thresh = 0;
|
||||||
|
config_save_i32("alert_temp", 0);
|
||||||
|
config_save_i32("alert_heap", 0);
|
||||||
|
snprintf(reply, reply_size, "OK ALERT all disabled");
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
snprintf(reply, reply_size, "ERR ALERT TEMP <c>|HEAP <bytes>|OFF");
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
|
||||||
/* HELP */
|
/* HELP */
|
||||||
if (strcmp(cmd, "HELP") == 0) {
|
if (strcmp(cmd, "HELP") == 0) {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
@@ -2207,7 +2316,7 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
"CALIBRATE [STATUS|CLEAR|N] PRESENCE [ON|OFF|THRESHOLD]\n"
|
"CALIBRATE [STATUS|CLEAR|N] PRESENCE [ON|OFF|THRESHOLD]\n"
|
||||||
"CHANSCAN [ON|OFF|NOW|INTERVAL] LED [QUIET|AUTO]\n"
|
"CHANSCAN [ON|OFF|NOW|INTERVAL] LED [QUIET|AUTO]\n"
|
||||||
"POWERSAVE [ON|OFF] AUTH [secret|OFF] FLOODTHRESH <n> [win]\n"
|
"POWERSAVE [ON|OFF] AUTH [secret|OFF] FLOODTHRESH <n> [win]\n"
|
||||||
"LOG <NONE|ERROR|WARN|INFO|DEBUG|VERBOSE> RSSI RESET\n"
|
"ALERT [TEMP <c>|HEAP <bytes>|OFF] LOG <level> RSSI RESET\n"
|
||||||
"OTA <url> POWERTEST [dwell] IDENTIFY REBOOT FACTORY");
|
"OTA <url> POWERTEST [dwell] IDENTIFY REBOOT FACTORY");
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
@@ -2254,6 +2363,8 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
"powersave=%s\n"
|
"powersave=%s\n"
|
||||||
"auth=%s\n"
|
"auth=%s\n"
|
||||||
"flood_thresh=%d/%ds\n"
|
"flood_thresh=%d/%ds\n"
|
||||||
|
"alert_temp=%.1f\n"
|
||||||
|
"alert_heap=%lu\n"
|
||||||
"boots=%lu",
|
"boots=%lu",
|
||||||
s_hostname,
|
s_hostname,
|
||||||
s_send_frequency,
|
s_send_frequency,
|
||||||
@@ -2276,6 +2387,7 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
s_powersave ? "on" : "off",
|
s_powersave ? "on" : "off",
|
||||||
s_auth_secret[0] ? "on" : "off",
|
s_auth_secret[0] ? "on" : "off",
|
||||||
s_flood_thresh, s_flood_window_s,
|
s_flood_thresh, s_flood_window_s,
|
||||||
|
s_alert_temp_thresh, (unsigned long)s_alert_heap_thresh,
|
||||||
(unsigned long)s_boot_count);
|
(unsigned long)s_boot_count);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user