137 lines
3.9 KiB
C++
137 lines
3.9 KiB
C++
#include "alert.h"
|
|
#include "audio_client.h"
|
|
#include "codec_bsp.h"
|
|
#include "i2c_bsp.h"
|
|
#include <esp_log.h>
|
|
#include <esp_timer.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/semphr.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
static const char *TAG = "alert";
|
|
|
|
/* Audio params: 24kHz, 16-bit, stereo */
|
|
#define SAMPLE_RATE 24000
|
|
#define CHANNELS 2
|
|
#define BITS 16
|
|
#define TONE_FREQ 762
|
|
#define TONE_DURATION_MS 200
|
|
|
|
/* Tone buffer: 200ms at 24kHz * 2ch * 2bytes = 19200 bytes */
|
|
#define TONE_SAMPLES (SAMPLE_RATE * TONE_DURATION_MS / 1000)
|
|
#define TONE_BUF_SIZE (TONE_SAMPLES * CHANNELS * (BITS / 8))
|
|
|
|
static int16_t *s_tone_buf = NULL;
|
|
static bool s_muted = false;
|
|
static CodecPort *s_codec = NULL;
|
|
static SemaphoreHandle_t s_alert_mutex = NULL;
|
|
|
|
/* Per-type cooldown tracking (microseconds) */
|
|
static int64_t s_last_trigger[ALERT_TYPE_COUNT] = {};
|
|
|
|
/* Cooldown periods in microseconds */
|
|
static const int64_t s_cooldown_us[ALERT_TYPE_COUNT] = {
|
|
60 * 1000000LL, /* SERVICE_DOWN: 60s */
|
|
60 * 1000000LL, /* HIGH_TEMP: 60s */
|
|
30 * 1000000LL, /* WS_DISCONNECT: 30s */
|
|
0, /* CONNECT_OK: no cooldown */
|
|
};
|
|
|
|
/* Beep patterns: number of beeps per alert type */
|
|
static const int s_beep_count[ALERT_TYPE_COUNT] = {
|
|
0, /* SERVICE_DOWN: triple beep */ /* DISABLED FOR TESTING */
|
|
2, /* HIGH_TEMP: double beep */
|
|
0, /* WS_DISCONNECT: single beep */ /* DISABLED FOR TESTING */
|
|
0, /* CONNECT_OK: single short beep */ /* DISABLED FOR TESTING */
|
|
};
|
|
|
|
static void generate_tone_buffer(void)
|
|
{
|
|
/* Allocate in PSRAM */
|
|
s_tone_buf = (int16_t *)heap_caps_malloc(TONE_BUF_SIZE, MALLOC_CAP_SPIRAM);
|
|
if (!s_tone_buf) {
|
|
ESP_LOGE(TAG, "Failed to allocate tone buffer");
|
|
return;
|
|
}
|
|
|
|
/* Generate 1kHz square wave */
|
|
int half_period = SAMPLE_RATE / (TONE_FREQ * 2);
|
|
int16_t amplitude = 16000; /* ~50% of max to avoid clipping */
|
|
|
|
for (int i = 0; i < TONE_SAMPLES; i++) {
|
|
int16_t val = ((i / half_period) % 2 == 0) ? amplitude : -amplitude;
|
|
/* Stereo: write same value to L and R */
|
|
s_tone_buf[i * 2] = val;
|
|
s_tone_buf[i * 2 + 1] = val;
|
|
}
|
|
}
|
|
|
|
void alert_init(void)
|
|
{
|
|
s_alert_mutex = xSemaphoreCreateMutex();
|
|
generate_tone_buffer();
|
|
memset(s_last_trigger, 0, sizeof(s_last_trigger));
|
|
ESP_LOGI(TAG, "Alert system initialized");
|
|
}
|
|
|
|
void alert_set_codec(void *codec)
|
|
{
|
|
s_codec = (CodecPort *)codec;
|
|
}
|
|
|
|
void alert_trigger(alert_type_t type)
|
|
{
|
|
if (type >= ALERT_TYPE_COUNT) return;
|
|
if (s_muted) return;
|
|
if (!s_tone_buf || !s_codec) return;
|
|
if (audio_client_get_state() == AUDIO_PLAYING) return; /* don't fight over codec */
|
|
|
|
if (xSemaphoreTake(s_alert_mutex, pdMS_TO_TICKS(50)) != pdTRUE) return;
|
|
|
|
/* Check cooldown */
|
|
int64_t now = esp_timer_get_time();
|
|
if (s_cooldown_us[type] > 0 && (now - s_last_trigger[type]) < s_cooldown_us[type]) {
|
|
xSemaphoreGive(s_alert_mutex);
|
|
return;
|
|
}
|
|
s_last_trigger[type] = now;
|
|
|
|
ESP_LOGI(TAG, "Alert triggered: type=%d", type);
|
|
|
|
/* Open codec for playback */
|
|
s_codec->CodecPort_SetInfo("es8311", 1, SAMPLE_RATE, CHANNELS, BITS);
|
|
s_codec->CodecPort_SetSpeakerVol(70);
|
|
|
|
/* Play beep pattern */
|
|
int beeps = s_beep_count[type];
|
|
for (int b = 0; b < beeps; b++) {
|
|
/* Play tone in chunks */
|
|
uint8_t *ptr = (uint8_t *)s_tone_buf;
|
|
int remaining = TONE_BUF_SIZE;
|
|
while (remaining > 0) {
|
|
int chunk = (remaining > 512) ? 512 : remaining;
|
|
s_codec->CodecPort_PlayWrite(ptr, chunk);
|
|
ptr += chunk;
|
|
remaining -= chunk;
|
|
}
|
|
/* Gap between beeps */
|
|
if (b < beeps - 1) {
|
|
vTaskDelay(pdMS_TO_TICKS(150));
|
|
}
|
|
}
|
|
|
|
xSemaphoreGive(s_alert_mutex);
|
|
}
|
|
|
|
void alert_mute(bool muted)
|
|
{
|
|
s_muted = muted;
|
|
ESP_LOGI(TAG, "Alerts %s", muted ? "MUTED" : "UNMUTED");
|
|
}
|
|
|
|
bool alert_is_muted(void)
|
|
{
|
|
return s_muted;
|
|
}
|