notif sonore et fin (1ere version)

This commit is contained in:
Marc Pasteur 2026-02-28 21:06:58 +01:00
parent 434ede2480
commit b7552e15ce
10 changed files with 255 additions and 107 deletions

View File

@ -10,12 +10,10 @@
#include "bsp/esp-bsp.h"
/* Buffer for reading/writing to I2S driver. Same length as SPIFFS buffer and I2S buffer, for optimal read/write performance.
Recording audio data path:
I2S peripheral -> I2S buffer (DMA) -> App buffer (RAM) -> SPIFFS buffer -> External SPI Flash.
Vice versa for playback. */
External SPI Flash -> SPIFFS buffer -> App buffer (RAM) -> I2S buffer (DMA) -> I2S peripheral.
*/
#define BUFFER_SIZE (1024)
#define SAMPLE_RATE (16000) // For recording
#define DEFAULT_VOLUME (25)
#define DEFAULT_VOLUME (50)
/* Globals */
static const char *TAG = "audio";
@ -35,22 +33,130 @@ typedef struct __attribute__((packed))
uint8_t data[];
} dumb_wav_header_t;
typedef struct {
FILE *f;
uint32_t data_offset;
uint32_t data_size;
} wav_ctx_t;
#define AUDIO_CHUNK_SIZE 4096
bool wav_open(wav_ctx_t *ctx, const char *path)
{
ctx->f = fopen(path, "rb");
if (!ctx->f) return false;
/* Skip RIFF header */
fseek(ctx->f, 12, SEEK_SET);
uint32_t chunk_id, chunk_size;
while (fread(&chunk_id, 4, 1, ctx->f))
{
fread(&chunk_size, 4, 1, ctx->f);
if (chunk_id == 0x61746164) { // "data"
ctx->data_offset = ftell(ctx->f);
ctx->data_size = chunk_size;
return true;
}
fseek(ctx->f, chunk_size, SEEK_CUR);
}
fclose(ctx->f);
return false;
}
void rewind_wav(wav_ctx_t *ctx)
{
fseek(ctx->f, ctx->data_offset, SEEK_SET);
}
#define AUDIO_CHUNK_SIZE 4096
static void audio_task(void *arg)
{
ESP_LOGI(TAG,"Audio task");
esp_codec_dev_handle_t spk_codec_dev = bsp_audio_codec_speaker_init();
assert(spk_codec_dev);
esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
esp_codec_dev_set_out_mute(spk_codec_dev, false); // 🔑 CRITIQUE
const char *play_filename = "/littlefs/sounds/mixkit-clear-announce-tones-2861.wav";
uint8_t *wav_bytes = malloc(BUFFER_SIZE);
assert(wav_bytes != NULL);
for (;;)
{
/* ⏸️ Attend PLAY */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
bool stop_requested = false;
while (!stop_requested) {
ESP_LOGI(TAG,"On joue !");
FILE *play_file = fopen(play_filename, "rb");
if (!play_file) {
ESP_LOGE(TAG,"File not found");
break;
}
dumb_wav_header_t wav_header;
if (fread(&wav_header, 1, sizeof(wav_header), play_file) != sizeof(wav_header)) {
ESP_LOGE(TAG,"Header read failed");
fclose(play_file);
break;
}
esp_codec_dev_sample_info_t fs = {
.sample_rate = wav_header.sample_rate,
.channel = wav_header.num_channels,
.bits_per_sample = wav_header.bits_per_sample,
};
ESP_ERROR_CHECK(esp_codec_dev_open(spk_codec_dev, &fs));
uint32_t bytes_sent = 0;
while (bytes_sent < wav_header.data_size) {
size_t bytes_read = fread(wav_bytes, 1, BUFFER_SIZE, play_file);
if (bytes_read == 0) break;
esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read);
bytes_sent += bytes_read;
vTaskDelay(1); // watchdog + scheduler
}
esp_codec_dev_close(spk_codec_dev);
fclose(play_file);
ESP_LOGI(TAG,"Fin du fichier");
if (ulTaskNotifyTake(pdTRUE, 0) > 0) {
stop_requested = true;
}
}
}
free(wav_bytes);
}
static void audio_task__(void *arg)
{
ESP_LOGE(TAG,"Audio task");
esp_codec_dev_handle_t spk_codec_dev = bsp_audio_codec_speaker_init();
assert(spk_codec_dev);
esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
/* Pointer to a file that is going to be played */
const char music_filename[] = "/littlefs/sounds/imperial_march.wav";
const char music_filename[] = "/littlefs/sounds/mixkit-clear-announce-tones-2861.wav";
const char *play_filename = music_filename;
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
int16_t *wav_bytes = malloc(BUFFER_SIZE);
assert(wav_bytes != NULL);
bool stop_requested = false;
while (!stop_requested){
ESP_LOGE(TAG,"On joue !");
/* Open WAV file */
ESP_LOGI(TAG, "Playing file %s", play_filename);
FILE *play_file = fopen(play_filename, "rb");
@ -65,10 +171,10 @@ static void audio_task(void *arg)
ESP_LOGW(TAG, "Error in reading file");
break;
}
ESP_LOGI(TAG, "Number of channels: %" PRIu16 "", wav_header.num_channels);
ESP_LOGI(TAG, "Bits per sample: %" PRIu16 "", wav_header.bits_per_sample);
ESP_LOGI(TAG, "Sample rate: %" PRIu32 "", wav_header.sample_rate);
ESP_LOGI(TAG, "Data size: %" PRIu32 "", wav_header.data_size);
//ESP_LOGI(TAG, "Number of channels: %" PRIu16 "", wav_header.num_channels);
//ESP_LOGI(TAG, "Bits per sample: %" PRIu16 "", wav_header.bits_per_sample);
//ESP_LOGI(TAG, "Sample rate: %" PRIu32 "", wav_header.sample_rate);
//ESP_LOGI(TAG, "Data size: %" PRIu32 "", wav_header.data_size);
esp_codec_dev_sample_info_t fs = {
.sample_rate = wav_header.sample_rate,
@ -83,11 +189,22 @@ static void audio_task(void *arg)
size_t bytes_read_from_spiffs = fread(wav_bytes, 1, BUFFER_SIZE, play_file);
esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs);
bytes_send_to_i2s += bytes_read_from_spiffs;
/* ⭐ LIGNE CRITIQUE ⭐ */
vTaskDelay(1); // laisse respirer IDLE + watchdog
}
ESP_LOGE(TAG,"On a fini le fichier");
fclose(play_file);
free(wav_bytes);
esp_codec_dev_close(spk_codec_dev);
if(ulTaskNotifyTake(pdTRUE, 0)>0){
stop_requested=true;
break;
};
}
free(wav_bytes);
vTaskSuspend(NULL);
}
void playSound(void)
@ -98,5 +215,10 @@ void playSound(void)
}
xTaskNotifyGive(audio_task_handle);
}
void stopSound(void){
if (audio_task_handle) {
xTaskNotifyGive(audio_task_handle);
}
}

View File

@ -1,2 +1,4 @@
void playSound(void);
void stopSound(void);

View File

@ -248,6 +248,8 @@ static void event_handler(lv_event_t *e)
static lv_indev_t *keyboard;
#endif
struct tm timeinfo;
void init_display_ihm(){
#if CONFIG_IDF_TARGET_LINUX
@ -291,7 +293,6 @@ void init_display_ihm(){
mainState.display_init=true;
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
@ -1306,73 +1307,22 @@ static void time_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
LV_UNUSED(observer);
//ESP_LOGE(TAG,"################### On passe dans le cb time");
lv_obj_t *parent = observer->target;
struct tm *timeStruct=lv_subject_get_pointer(subject);
lv_obj_t *dateTimeObj = lv_observer_get_target_obj(observer);
datetime_ctx_t *ctx = lv_obj_get_user_data(dateTimeObj);
if (!ctx){
ESP_LOGE(TAG,"################### Contexte non positionné");
return;
}
lv_obj_t *hour = lv_obj_get_child_by_name(parent, "hour");
lv_obj_t *minute = lv_obj_get_child_by_name(parent, "minute");
struct tm *t = (struct tm *)lv_subject_get_pointer(subject);
if (!t){
ESP_LOGE(TAG,"################### Pas de struct tm");
return;
}
//ESP_LOGE(TAG,"################################# %d %d %d %02d:%02d", t->tm_wday, t->tm_mday, t->tm_mon, t->tm_hour, t->tm_min);
int h = timeStruct->tm_hour;
int m = timeStruct->tm_min;
char buf[4];
bool refresh_date = false;
bool refresh_time = false;
char buf[3];
// ---- DATE ----
if (t->tm_mday != ctx->last_day) {
ctx->last_day = t->tm_mday;
u8_to_2digits(buf, t->tm_mday);
buf[2] = '/';
buf[3] = '\0';
//ESP_LOGE(TAG,"Jour %s",buf);
lv_span_set_text(ctx->date_day, buf);
refresh_date = true;
}
if ((t->tm_mon + 1) != ctx->last_month) {
ctx->last_month = t->tm_mon + 1;
u8_to_2digits(buf, ctx->last_month);
//ESP_LOGE(TAG,"Mois %s",buf);
lv_span_set_text(ctx->date_month, buf);
refresh_date = true;
}
if (true || refresh_date) {
lv_spangroup_refresh(ctx->date_group);
}
// ---- TIME ----
if (t->tm_hour != ctx->last_hour) {
ctx->last_hour = t->tm_hour;
u8_to_2digits(buf, t->tm_hour);
//ESP_LOGE(TAG,"Heure %s",buf);
lv_span_set_text(ctx->time_hour, buf);
refresh_time = true;
}
if (t->tm_min != ctx->last_min) {
ctx->last_min = t->tm_min;
buf[0] = ':';
buf[1] = '0' + (t->tm_min / 10);
buf[2] = '0' + (t->tm_min % 10);
buf[3] = '\0';
//ESP_LOGE(TAG,"Minutes %s",buf);
lv_span_set_text(ctx->time_min, buf);
refresh_time = true;
}
if (refresh_time) {
lv_spangroup_refresh(ctx->time_group);
}
snprintf(buf, sizeof(buf), "%02d", h);
lv_label_set_text(hour, buf);
snprintf(buf, sizeof(buf), "%02d", m);
lv_label_set_text(minute, buf);
}
/* ------------------------------------------------------------ */
@ -1587,9 +1537,40 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_width(txtMinuteur, LV_PCT(100));
}
lv_obj_t *btnStopNotif;
static void minuteur_event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
// lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(e);
char *evtData = (char *)lv_event_get_user_data(e);
switch (code)
{
case LV_EVENT_CLICKED:
send_event(EVT_FIN_MACHINE_STOP_NOTIF,NULL);
lv_obj_delete(btnStopNotif);
break;
default:
break;
}
}
void draw_minuteur(char *txt){
lv_label_set_text(txtMinuteur, txt);
}
}
void draw_minuteurStop(char *txt){
lv_obj_t *ctr = lv_obj_get_parent(txtMinuteur);
btnStopNotif = lv_btn_create(ctr);
lv_obj_add_event_cb(btnStopNotif, minuteur_event_handler, LV_EVENT_ALL, NULL);
lv_obj_t * label=lv_label_create(btnStopNotif);
lv_label_set_text(label, "Stop !");
lv_obj_center(label);
}
static void backCb(lv_event_t * e)
@ -1718,6 +1699,7 @@ void messageCardContent(lv_obj_t *cont_messages)
}
void minuteurCb(lv_obj_t *base_obj)
{
send_event(EVT_FIN_MACHINE_STOP_NOTIF,NULL);
drawMinuteur(base_obj);
}
void voletsCb(lv_obj_t *base_obj)
@ -1820,22 +1802,45 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_span_set_text(month, "--");
lv_style_set_text_font(lv_span_get_style(month), &super_malibu_80);
lv_obj_t *time = lv_spangroup_create(date_and_time);
lv_obj_set_name(time, "time");
lv_obj_set_id(time, "time");
// lv_obj_add_style(time, &txtstyle, 0);
lv_span_t *hourSpan = lv_spangroup_add_span(time);
lv_span_set_text(hourSpan, "--");
lv_style_set_text_font(lv_span_get_style(hourSpan), &super_malibu_80);
lv_span_t *minute = lv_spangroup_add_span(time);
lv_span_set_text(minute, ":--");
lv_style_set_text_color(lv_span_get_style(minute), base);
lv_style_set_line_color(lv_span_get_style(minute), accent);
lv_style_set_text_font(lv_span_get_style(minute), &super_malibu_80);
//lv_subject_add_observer_obj(&c->th, theme_observer_accent_span_cb, time, minute);
datetime_ctx_init(date_and_time);
lv_subject_add_observer_obj(&timeSubj, time_observer_cb, date_and_time, NULL);
lv_obj_t *objTime = lv_obj_create(date_and_time);
lv_obj_set_size(objTime, 250, LV_SIZE_CONTENT);
lv_obj_clear_flag(objTime, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_opa(objTime, LV_OPA_TRANSP, 0);
lv_obj_set_style_border_width(objTime, 0, 0);
// Layout flex horizontal
lv_obj_set_flex_flow(objTime, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(
objTime,
LV_FLEX_ALIGN_SPACE_BETWEEN, // répartit les enfants
LV_FLEX_ALIGN_CENTER, // align vertical
LV_FLEX_ALIGN_CENTER
);
// --- HEURES ---
lv_obj_t *hour_lbl = lv_label_create(objTime);
lv_obj_set_name(hour_lbl, "hour");
lv_obj_set_style_text_font(hour_lbl, &super_malibu_80, 0);
lv_obj_set_style_text_color(hour_lbl, lv_color_white(), 0);
lv_label_set_text(hour_lbl, "--");
// --- DEUX-POINTS ---
lv_obj_t *colon_lbl = lv_label_create(objTime);
lv_obj_set_name(colon_lbl, "colon");
lv_obj_set_style_text_font(colon_lbl, &super_malibu_80, 0);
lv_obj_set_style_text_color(colon_lbl, base, 0);
lv_label_set_text(colon_lbl, ":");
// --- MINUTES ---
lv_obj_t *minute_lbl = lv_label_create(objTime);
lv_obj_set_name(minute_lbl, "minute");
lv_obj_set_style_text_font(minute_lbl, &super_malibu_80, 0);
lv_obj_set_style_text_color(minute_lbl, lv_color_white(), 0);
lv_label_set_text(minute_lbl, "--");
// Init datetime
//datetime_ctx_init(date_and_time);
lv_subject_add_observer_obj(&timeSubj, time_observer_cb, objTime, NULL);
lv_obj_t * subContent = lv_obj_create(info_area);
lv_obj_set_flex_flow(subContent, LV_FLEX_FLOW_COLUMN);

View File

@ -103,6 +103,11 @@ void traiteEvt(void *arg)
break;
}
case IHM_EVT_MACHINE_TERMINEE:
{
draw_minuteurStop();
}
case IHM_EVT_HAUTEUR_CUVE:
{
float hauteur = *(float *)evt->pvData;

View File

@ -26,6 +26,7 @@ void drawHome();
void draw_time(struct tm *dateHeure);
void draw_tempExt(char *tempHumid);
void draw_minuteur(char *txt);
void draw_minuteurStop();
void draw_temp(char *tempHumid);
void draw_meteo(meteo_event_payload_t *meteo);

View File

@ -42,6 +42,7 @@ const char * domo_event_to_str(domo_events evt)
case EVT_TEMP_EXT: return "TEMP_EXT";
case EVT_TEMP_INT: return "TEMP_INT";
case EVT_FIN_MACHINE: return "FIN_MACHINE";
case EVT_FIN_MACHINE_STOP_NOTIF: return "FIN_MACHINE_STOP_NOTIF";
default: return "EVT_UNKNOWN";
}
}
@ -212,6 +213,16 @@ void send_event(domo_events evt, void* pDatas) {
#if CONFIG_IDF_TARGET_ESP32P4
playSound();
#endif
ihmEvt->eEventType = IHM_EVT_MACHINE_TERMINEE;
ihmEvt->pvData = NULL;
ihmEvt->bNeedToFreeData = false;
break;
}
case EVT_FIN_MACHINE_STOP_NOTIF:{
#if CONFIG_IDF_TARGET_ESP32P4
stopSound();
#endif
free(ihmEvt); // rien à envoyer à l'IHM
ihmEvt = NULL;
break;

View File

@ -15,7 +15,8 @@ typedef enum eIHMEvent_t{
IHM_EVT_ETAT_MACHINE,
IHM_EVT_HAUTEUR_CUVE,
IHM_EVT_METEO_RECUE,
IHM_EVT_TEMP_RECUE
IHM_EVT_TEMP_RECUE,
IHM_EVT_MACHINE_TERMINEE
} eIHMEvent_t;
typedef struct IHM_EVENT
@ -35,7 +36,8 @@ typedef enum domo_events{
EVT_METEO_RECUE,
EVT_TEMP_EXT,
EVT_TEMP_INT,
EVT_FIN_MACHINE
EVT_FIN_MACHINE,
EVT_FIN_MACHINE_STOP_NOTIF
} domo_events;
void startEvtManager();
QueueHandle_t getIHMQueueHandle();