#pragma once // Pure state machine: no ESP-IDF dependencies, can be compiled and tested on a PC. #include #include typedef enum { DET_IDLE = 0, DET_TRIGGERED, DET_COOLDOWN } det_state_t; typedef struct { uint32_t win_n; // window length in frames (clamped to 1..32) uint32_t win_m; // matches required (clamped to 1..win_n) uint32_t cooldown_ms; } detector_cfg_t; typedef struct { det_state_t state; uint32_t hist; // bit 0 = newest frame, 1 = matched uint32_t cooldown_start_ms; uint32_t triggers; } detector_t; void detector_init(detector_t *d); void detector_reset(detector_t *d); // back to IDLE with empty history // Loudness gate AND score >= threshold. bool detector_is_match(float rms, float score, float min_rms, float threshold); // Feed one frame. Returns true exactly once per trigger (IDLE -> TRIGGERED). // TRIGGERED lasts one frame, then COOLDOWN until cooldown_ms elapsed, then IDLE // with a cleared history (the buzzer's tail cannot re-trigger). bool detector_update(detector_t *d, bool match, uint32_t now_ms, const detector_cfg_t *cfg); const char *detector_state_name(det_state_t s);