Files
buzz_detector/main/console.c
T

229 lines
9.1 KiB
C

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_console.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "sdkconfig.h"
#include "audio_in.h"
#include "calibration.h"
#include "config_store.h"
#include "console.h"
#include "fingerprint.h"
#include "matcher.h"
#include "notifier.h"
#include "processing.h"
#include "test_vector.h"
static int cmd_selftest(int argc, char **argv)
{
#if !TEST_VECTOR_VALID
printf("no test vector compiled in. Run tools/make_template.py and rebuild.\n");
return 0;
#else
static features_ctx_t ctx; // own scratch: never shares with the processing task
float fp[NUM_BANDS], rms = 0;
bool ok = features_compute(&ctx, TEST_VECTOR_PCM, fp, &rms);
float maxerr = 0;
for (int b = 0; b < NUM_BANDS; b++) {
float e = fabsf(fp[b] - TEST_VECTOR_FP[b]);
if (!(e <= maxerr)) maxerr = e; // NaN-safe
}
float rms_rel = fabsf(rms - TEST_VECTOR_RMS) / fmaxf(TEST_VECTOR_RMS, 1.0f);
printf("fingerprint max abs error: %.3e (tol 1e-3)\n", maxerr);
printf("rms: firmware %.3f, expected %.3f (rel err %.2e)\n", rms, TEST_VECTOR_RMS, rms_rel);
bool pass = ok && maxerr < 1e-3f && rms_rel < 1e-3f;
printf("selftest: %s\n", pass ? "PASS" : "FAIL");
if (!pass) printf("Parity broken: feature_params.h changed since make_template.py ran? Re-run it and rebuild.\n");
return 0;
#endif
}
static int cmd_selfcheck(int argc, char **argv) { calibration_run_test(true); return 0; }
static int cmd_test(int argc, char **argv) { calibration_run_test(false); return 0; }
static int cmd_background(int argc, char **argv) { calibration_run_background(); return 0; }
static int cmd_autothresh(int argc, char **argv) { calibration_autothresh(); return 0; }
static int cmd_learn(int argc, char **argv)
{
if (argc >= 2 && strcmp(argv[1], "reset") == 0) {
esp_err_t e = matcher_reset();
printf("learn reset: %s. Active template: %s\n", e == ESP_OK ? "NVS template cleared" : esp_err_to_name(e),
matcher_source_name());
return 0;
}
calibration_run_learn(false, 0.0f);
return 0;
}
static int cmd_adapt(int argc, char **argv)
{
app_config_t cfg;
config_get(&cfg);
float w = cfg.adapt_weight;
if (argc >= 2) {
char *end;
w = strtof(argv[1], &end);
if (*end != '\0' || !(w >= 0.0f && w <= 1.0f)) {
printf("usage: adapt [weight 0..1] (weight of the NEW template)\n");
return 0;
}
}
calibration_run_learn(true, w);
return 0;
}
static int cmd_monitor(int argc, char **argv)
{
if (argc < 2 || (strcmp(argv[1], "on") && strcmp(argv[1], "off"))) {
printf("usage: monitor on|off (currently %s)\n", processing_get_monitor() ? "on" : "off");
return 0;
}
processing_set_monitor(strcmp(argv[1], "on") == 0);
printf("monitor %s\n", argv[1]);
return 0;
}
static int cmd_set(int argc, char **argv)
{
if (argc != 3) {
printf("usage: set <param> <value> (see `get`)\n");
return 0;
}
char err[96];
if (config_set_param(argv[1], argv[2], err, sizeof(err)) == ESP_OK) {
printf("ok, saved\n");
} else {
printf("error: %s\n", err);
}
return 0;
}
static int cmd_get(int argc, char **argv)
{
char line[200];
for (int i = 0; i < config_param_count(); i++) {
if (argc >= 2 && strcmp(argv[1], config_param_name(i)) != 0) continue;
config_param_format(i, line, sizeof(line));
printf("%s\n", line);
}
return 0;
}
static int cmd_status(int argc, char **argv)
{
proc_stats_t ps;
processing_get_stats(&ps);
notifier_stats_t ns;
notifier_get_stats(&ns);
app_config_t cfg;
config_get(&cfg);
float sug;
printf("uptime: %llu s\n", (unsigned long long)(esp_timer_get_time() / 1000000));
printf("mode / state: %s / %s\n", calibration_mode_name(calibration_get_mode()), detector_state_name(ps.state));
printf("template: %s, %lu frames%s\n", matcher_source_name(), (unsigned long)matcher_frame_count(),
matcher_has_template() ? "" : " <-- run tools/make_template.py + rebuild, or `learn`");
if (matcher_suggested_threshold(&sug)) printf(" template.h suggests threshold %.3f\n", sug);
printf("detector: threshold %.3f, min_rms %.0f, window %lu/%lu, buzz quiet tail %lu ms\n", cfg.threshold, cfg.min_rms,
(unsigned long)cfg.win_m, (unsigned long)cfg.win_n, (unsigned long)cfg.cooldown_ms);
printf("last frame: rms %.1f, score %+.3f, match %d\n", ps.last_rms, ps.last_score, ps.last_match);
printf("triggers: %lu (last at uptime %lu s)\n", (unsigned long)ps.triggers, (unsigned long)ps.last_trigger_s);
printf("last buzz: %lu ms (final quiet tail excluded)\n", (unsigned long)ps.last_buzz_ms);
printf("frames: processed %lu, captured %lu, dropped %lu\n", (unsigned long)ps.frames,
(unsigned long)audio_in_frames_captured(), (unsigned long)audio_in_frames_dropped());
printf("proc time: avg %lu us, max %lu us (budget %d us per frame)\n", (unsigned long)ps.proc_avg_us,
(unsigned long)ps.proc_max_us, HOP_LEN * 1000000 / FS_HZ);
printf("wifi: %s%s%s\n", ns.wifi_configured ? (ns.wifi_connected ? "connected " : "DISCONNECTED") : "not configured",
ns.wifi_connected ? ns.ip : "", "");
printf("event POST: %s, ok %lu, failed %lu, dropped %lu\n",
ns.endpoint_configured ? "endpoint configured" : "endpoint not configured",
(unsigned long)ns.sent_ok, (unsigned long)ns.sent_failed, (unsigned long)ns.dropped);
printf("heap free: %lu bytes\n", (unsigned long)esp_get_free_heap_size());
return 0;
}
static int cmd_fingerprint(int argc, char **argv)
{
float live[NUM_BANDS], tpl[NUM_BANDS];
processing_get_last_fp(live);
matcher_get_template(tpl);
const int *e = features_band_edges();
printf("band bins live template (%s)\n", matcher_source_name());
for (int b = 0; b < NUM_BANDS; b++) {
printf("%3d %3d-%-3d %+.4f %+.4f\n", b, e[b], e[b + 1] - 1, live[b], tpl[b]);
}
return 0;
}
static int cmd_alert(int argc, char **argv)
{
printf(notifier_send(42) ? "queued test event (delay_ms=42)\n" : "queue full\n");
return 0;
}
static int cmd_reboot(int argc, char **argv)
{
printf("rebooting...\n");
fflush(stdout);
esp_restart();
return 0;
}
static void reg(const char *name, const char *help, const char *hint, esp_console_cmd_func_t fn)
{
const esp_console_cmd_t c = { .command = name, .help = help, .hint = hint, .func = fn };
esp_err_t err = esp_console_cmd_register(&c);
if (err != ESP_OK) ESP_LOGE("console", "register %s: %s", name, esp_err_to_name(err));
}
esp_err_t console_start(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t rc = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
rc.prompt = "buzz> ";
rc.max_cmdline_length = 128;
rc.task_stack_size = 8192;
esp_err_t err;
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
esp_console_dev_uart_config_t hw = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
err = esp_console_new_repl_uart(&hw, &rc, &repl);
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
esp_console_dev_usb_serial_jtag_config_t hw = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
err = esp_console_new_repl_usb_serial_jtag(&hw, &rc, &repl);
#elif CONFIG_ESP_CONSOLE_USB_CDC
esp_console_dev_usb_cdc_config_t hw = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
err = esp_console_new_repl_usb_cdc(&hw, &rc, &repl);
#else
#error "Unsupported console transport"
#endif
if (err != ESP_OK) {
ESP_LOGE("console", "REPL create: %s", esp_err_to_name(err));
return err;
}
esp_console_register_help_command();
reg("selftest", "Run firmware feature pipeline on the built-in test vector (C/Python parity)", NULL, cmd_selftest);
reg("selfcheck", "10 s: press the real buzzer; score vs ACTIVE template + verdict", NULL, cmd_selfcheck);
reg("learn", "60 s: learn template from the real mic (`learn reset` = clear NVS template)", "[reset]", cmd_learn);
reg("adapt", "60 s: blend newly heard buzzer into the active template", "[weight 0..1]", cmd_adapt);
reg("background", "60 s ambient score measurement vs active template", NULL, cmd_background);
reg("test", "10 s: press the buzzer; score distribution of loud frames", NULL, cmd_test);
reg("autothresh", "set threshold between background p99 and buzzer p5", NULL, cmd_autothresh);
reg("monitor", "per-frame live line on/off", "on|off", cmd_monitor);
reg("set", "set a parameter (persisted)", "<param> <value>", cmd_set);
reg("get", "show parameters", "[param]", cmd_get);
reg("status", "template source, detector state, timing, Wi-Fi", NULL, cmd_status);
reg("fingerprint", "print live fingerprint and active template", NULL, cmd_fingerprint);
reg("alert", "send a test event POST", NULL, cmd_alert);
reg("reboot", "restart", NULL, cmd_reboot);
err = esp_console_start_repl(repl);
if (err != ESP_OK) ESP_LOGE("console", "start_repl: %s", esp_err_to_name(err));
return err;
}