vibe code the initial impl
This commit is contained in:
+59
-120
@@ -1,139 +1,78 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Door buzzer detector: spectral-template matching on an I2S MEMS mic, ntfy push alerts.
|
||||
#include <stdio.h>
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "led_strip.h"
|
||||
#include "freertos/task.h"
|
||||
#include "audio_in.h"
|
||||
#include "board_config.h"
|
||||
#include "calibration.h"
|
||||
#include "config_store.h"
|
||||
#include "console.h"
|
||||
#include "led.h"
|
||||
#include "matcher.h"
|
||||
#include "notifier.h"
|
||||
#include "processing.h"
|
||||
|
||||
#define I2S_BCLK_GPIO GPIO_NUM_3
|
||||
#define I2S_WS_GPIO GPIO_NUM_4
|
||||
#define I2S_DATA_GPIO GPIO_NUM_5
|
||||
#define RGB_LED_GPIO GPIO_NUM_48
|
||||
static const char *TAG = "main";
|
||||
|
||||
#define SAMPLE_RATE_HZ 16000
|
||||
#define SAMPLES_PER_READ 256
|
||||
#define GREEN_NOISE_RMS 100000.0f
|
||||
#define RED_NOISE_RMS 1000000.0f
|
||||
#define LED_BRIGHTNESS 64
|
||||
|
||||
static const char *TAG = "buzz_detector";
|
||||
|
||||
static led_strip_handle_t init_rgb_led(void)
|
||||
// BOOT button (GPIO0, active low). A short press (30 ms .. 1 s) starts `learn`.
|
||||
static void button_task(void *arg)
|
||||
{
|
||||
const led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = RGB_LED_GPIO,
|
||||
.max_leds = 1,
|
||||
.led_model = LED_MODEL_WS2812,
|
||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
|
||||
.flags = {
|
||||
.invert_out = false,
|
||||
},
|
||||
gpio_config_t io = {
|
||||
.pin_bit_mask = 1ULL << BOOT_BTN_GPIO,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
};
|
||||
const led_strip_rmt_config_t rmt_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 10 * 1000 * 1000,
|
||||
.mem_block_symbols = 64,
|
||||
.flags = {
|
||||
.with_dma = false,
|
||||
},
|
||||
};
|
||||
|
||||
led_strip_handle_t strip = NULL;
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &strip));
|
||||
ESP_ERROR_CHECK(led_strip_clear(strip));
|
||||
return strip;
|
||||
}
|
||||
|
||||
static void update_rgb_led(led_strip_handle_t strip, float rms)
|
||||
{
|
||||
float red = 0.0f;
|
||||
float green = 0.0f;
|
||||
|
||||
if (rms < GREEN_NOISE_RMS) {
|
||||
green = LED_BRIGHTNESS * rms / GREEN_NOISE_RMS;
|
||||
} else {
|
||||
float red_fraction = (rms - GREEN_NOISE_RMS) /
|
||||
(RED_NOISE_RMS - GREEN_NOISE_RMS);
|
||||
if (red_fraction > 1.0f) {
|
||||
red_fraction = 1.0f;
|
||||
if (gpio_config(&io) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "button gpio_config failed");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
int64_t down_at = 0;
|
||||
for (;;) {
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
bool down = gpio_get_level(BOOT_BTN_GPIO) == 0;
|
||||
if (down && down_at == 0) {
|
||||
down_at = esp_timer_get_time();
|
||||
} else if (!down && down_at != 0) {
|
||||
int64_t held_ms = (esp_timer_get_time() - down_at) / 1000;
|
||||
down_at = 0;
|
||||
if (held_ms >= 30 && held_ms <= 1000) {
|
||||
printf("\n[BOOT button] starting learn\n");
|
||||
calibration_run_learn(false, 0.0f); // blocks this task only
|
||||
}
|
||||
}
|
||||
red = LED_BRIGHTNESS * red_fraction;
|
||||
green = LED_BRIGHTNESS * (1.0f - red_fraction);
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(led_strip_set_pixel(strip, 0, (uint8_t)red,
|
||||
(uint8_t)green, 0));
|
||||
ESP_ERROR_CHECK(led_strip_refresh(strip));
|
||||
}
|
||||
|
||||
static i2s_chan_handle_t init_microphone(void)
|
||||
static void check(esp_err_t err, const char *what)
|
||||
{
|
||||
i2s_chan_handle_t rx_channel = NULL;
|
||||
const i2s_chan_config_t channel_config =
|
||||
I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, NULL, &rx_channel));
|
||||
|
||||
i2s_std_config_t standard_config = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE_HZ),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(
|
||||
I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = I2S_BCLK_GPIO,
|
||||
.ws = I2S_WS_GPIO,
|
||||
.dout = I2S_GPIO_UNUSED,
|
||||
.din = I2S_DATA_GPIO,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
standard_config.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_channel, &standard_config));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_channel));
|
||||
return rx_channel;
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s failed: %s", what, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
led_strip_handle_t rgb_led = init_rgb_led();
|
||||
i2s_chan_handle_t microphone = init_microphone();
|
||||
int32_t samples[SAMPLES_PER_READ];
|
||||
float smoothed_rms = 0.0f;
|
||||
uint32_t log_counter = 0;
|
||||
ESP_ERROR_CHECK(config_init()); // also initialises NVS
|
||||
check(led_init(), "led_init");
|
||||
ESP_ERROR_CHECK(calibration_init());
|
||||
ESP_ERROR_CHECK(matcher_init());
|
||||
ESP_ERROR_CHECK(audio_in_init());
|
||||
check(notifier_init(), "notifier_init");
|
||||
ESP_ERROR_CHECK(processing_start());
|
||||
ESP_ERROR_CHECK(audio_in_start());
|
||||
|
||||
ESP_LOGI(TAG, "INMP441 ready on BCLK=%d, WS=%d, SD=%d; RGB LED on GPIO %d",
|
||||
I2S_BCLK_GPIO, I2S_WS_GPIO, I2S_DATA_GPIO, RGB_LED_GPIO);
|
||||
|
||||
while (true) {
|
||||
size_t bytes_read = 0;
|
||||
ESP_ERROR_CHECK(i2s_channel_read(microphone, samples, sizeof(samples),
|
||||
&bytes_read, portMAX_DELAY));
|
||||
const size_t sample_count = bytes_read / sizeof(samples[0]);
|
||||
if (sample_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t sum_squares = 0;
|
||||
for (size_t i = 0; i < sample_count; ++i) {
|
||||
// INMP441 provides 24-bit signed samples in the upper bits of each 32-bit slot.
|
||||
const int32_t sample = samples[i] >> 8;
|
||||
sum_squares += (int64_t)sample * sample;
|
||||
}
|
||||
|
||||
const float rms = sqrtf((float)sum_squares / sample_count);
|
||||
smoothed_rms += 0.2f * (rms - smoothed_rms);
|
||||
update_rgb_led(rgb_led, smoothed_rms);
|
||||
|
||||
if (++log_counter == 16) {
|
||||
ESP_LOGI(TAG, "sound RMS: %.0f", smoothed_rms);
|
||||
log_counter = 0;
|
||||
}
|
||||
if (xTaskCreate(button_task, "button", 4096, NULL, 3, NULL) != pdPASS) {
|
||||
ESP_LOGE(TAG, "button task not created");
|
||||
}
|
||||
|
||||
printf("\nDoor buzzer detector ready. Type `help`.\n");
|
||||
if (!matcher_has_template()) {
|
||||
printf("NEEDS TEMPLATE. Either:\n"
|
||||
" 1) python tools/make_template.py --buzz your_recording.wav then rebuild/flash, or\n"
|
||||
" 2) type `learn` (or short-press BOOT) and press the buzzer during the 60 s session.\n");
|
||||
}
|
||||
check(console_start(), "console_start");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user