diff --git a/components/audio/audio.c b/components/audio/audio.c index bcfbda6..12c2296 100644 --- a/components/audio/audio.c +++ b/components/audio/audio.c @@ -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); - 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); + } +} diff --git a/components/audio/include/audio.h b/components/audio/include/audio.h index 898b21b..e6e120f 100644 --- a/components/audio/include/audio.h +++ b/components/audio/include/audio.h @@ -1,2 +1,4 @@ void playSound(void); + +void stopSound(void); diff --git a/components/audio/sounds/imperial_march.wav b/components/audio/sounds/imperial_march.wav deleted file mode 100644 index 995e86e..0000000 Binary files a/components/audio/sounds/imperial_march.wav and /dev/null differ diff --git a/components/domotic_display/ihm.c b/components/domotic_display/ihm.c index 46efc55..5828d8f 100644 --- a/components/domotic_display/ihm.c +++ b/components/domotic_display/ihm.c @@ -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,10 +1537,41 @@ void messageCardContent(lv_obj_t *cont_messages) lv_obj_set_width(txtMinuteur, LV_PCT(100)); } - void draw_minuteur(char *txt){ - lv_label_set_text(txtMinuteur, txt); + + 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); diff --git a/components/domotic_display/ihm_gateway.c b/components/domotic_display/ihm_gateway.c index b8fea5e..5c53d8b 100644 --- a/components/domotic_display/ihm_gateway.c +++ b/components/domotic_display/ihm_gateway.c @@ -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; diff --git a/components/domotic_display/include/ihm.h b/components/domotic_display/include/ihm.h index fc885b4..a5544d4 100644 --- a/components/domotic_display/include/ihm.h +++ b/components/domotic_display/include/ihm.h @@ -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); diff --git a/components/eventsManager/eventsManager.c b/components/eventsManager/eventsManager.c index 2c5b4bc..95bf097 100644 --- a/components/eventsManager/eventsManager.c +++ b/components/eventsManager/eventsManager.c @@ -32,16 +32,17 @@ void startEvtManager(){ const char * domo_event_to_str(domo_events evt) { switch (evt) { - case EVT_WIFI_CONNECTED: return "WIFI_CONNECTED"; - case EVT_TIME_SETTED: return "TIME_SETTED"; - case EVT_BTN_VOLET: return "BTN_VOLET"; - case EVT_PUISSANCE_RECUE: return "PUISSANCE_RECUE"; - case EVT_ETAT_MACHINE: return "ETAT_MACHINE"; - case EVT_HAUTEUR_CUVE: return "HAUTEUR_CUVE"; - case EVT_METEO_RECUE: return "METEO_RECUE"; - case EVT_TEMP_EXT: return "TEMP_EXT"; - case EVT_TEMP_INT: return "TEMP_INT"; - case EVT_FIN_MACHINE: return "FIN_MACHINE"; + case EVT_WIFI_CONNECTED: return "WIFI_CONNECTED"; + case EVT_TIME_SETTED: return "TIME_SETTED"; + case EVT_BTN_VOLET: return "BTN_VOLET"; + case EVT_PUISSANCE_RECUE: return "PUISSANCE_RECUE"; + case EVT_ETAT_MACHINE: return "ETAT_MACHINE"; + case EVT_HAUTEUR_CUVE: return "HAUTEUR_CUVE"; + case EVT_METEO_RECUE: return "METEO_RECUE"; + 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; diff --git a/components/eventsManager/include/eventsManager.h b/components/eventsManager/include/eventsManager.h index 9b71186..f66c2b1 100644 --- a/components/eventsManager/include/eventsManager.h +++ b/components/eventsManager/include/eventsManager.h @@ -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(); diff --git a/main/medias/sounds/imperial_march.wav b/main/medias/sounds/imperial_march.wav deleted file mode 100644 index 995e86e..0000000 Binary files a/main/medias/sounds/imperial_march.wav and /dev/null differ diff --git a/main/medias/sounds/mixkit-clear-announce-tones-2861.wav b/main/medias/sounds/mixkit-clear-announce-tones-2861.wav new file mode 100644 index 0000000..d2a1ee2 Binary files /dev/null and b/main/medias/sounds/mixkit-clear-announce-tones-2861.wav differ