Compare commits

..

13 Commits

Author SHA1 Message Date
8f60e9c38c publication temp int + etat online 2026-03-14 11:51:44 +01:00
67e6d1cfaa webserver (crashes & dumps) 2026-03-13 18:57:27 +01:00
77e8116d54 bg
(cherry picked from commit a84b935d41b012d8f4349df37f5e37f4e2de47be)
2026-03-06 07:24:23 +01:00
0de95c5386 dump to flash + poisoning 2026-03-06 07:22:00 +01:00
f871b76ca2 fix compat linux 2026-03-01 21:09:32 +01:00
9d31148d21 Merge branch 'OTA' 2026-03-01 20:33:14 +01:00
9293b5fdc6 Merge branch 'chgt_style_dateheure' 2026-03-01 16:56:00 +01:00
bce9f03eb4 format date 2026-03-01 16:50:11 +01:00
9217397d7d OTA: 1ere version 2026-03-01 15:58:43 +01:00
b7552e15ce notif sonore et fin (1ere version) 2026-02-28 21:06:58 +01:00
434ede2480 amélioration ihm (boutons) 2026-02-26 23:56:19 +01:00
8e868da692 linux compat 2026-02-26 23:55:50 +01:00
e1640f4ad9 machine : affichage durée depuis départ 2026-02-26 23:20:05 +01:00
27 changed files with 717 additions and 203 deletions

View File

@ -2,7 +2,7 @@
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${config:idf.toolsPath}/tools/riscv32-esp-elf/esp-14.2.0_20251107/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"compilerPath": "/home/marc/.espressif/tools/riscv32-esp-elf/esp-14.2.0_20251107/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",

2
.vscode/launch.json vendored
View File

@ -37,7 +37,7 @@
"type": "cppdbg",
"request": "launch",
"preLaunchTask": "Build - Build IHM",
"program": "${workspaceFolder}/components/domotic_display/test_host/build/nvs_host_test.elf",
"program": "${workspaceFolder}/components/domotic_display/test_host/build/host_test.elf",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,

View File

@ -1,3 +1,54 @@
# --------------------------------------------------
# 1. Version de base (version.txt)
# --------------------------------------------------
file(READ "${CMAKE_SOURCE_DIR}/version.txt" FW_VERSION)
string(STRIP "${FW_VERSION}" FW_VERSION)
# --------------------------------------------------
# 2. Détection git + branche
# --------------------------------------------------
find_package(Git)
set(GIT_BRANCH "")
set(IS_GIT_REPO OFF)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
set(IS_GIT_REPO ON)
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
endif()
# --------------------------------------------------
# 3. Mode dev ou release
# --------------------------------------------------
# Convention simple :
# - branche main / master => release
# - le reste => dev
set(IS_DEV_BUILD OFF)
if(IS_GIT_REPO AND NOT GIT_BRANCH STREQUAL "main" AND NOT GIT_BRANCH STREQUAL "master")
set(IS_DEV_BUILD ON)
endif()
# --------------------------------------------------
# 4. Nettoyage nom de branche
# --------------------------------------------------
if(IS_DEV_BUILD)
# remplace / par -
string(REPLACE "/" "-" GIT_BRANCH_CLEAN "${GIT_BRANCH}")
set(FW_VERSION "${FW_VERSION}-${GIT_BRANCH_CLEAN}")
endif()
message(STATUS "Firmware version: ${FW_VERSION}")
# --------------------------------------------------
cmake_minimum_required(VERSION 3.16)
option(SIMULATION_QEMU "Build for QEMU simulation" OFF)
message(STATUS "ROOT:: SIMULATION_QEMU = ${SIMULATION_QEMU}")
@ -33,7 +84,7 @@ endif()
#list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/esp_timer")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(rgb_lcd)
project(domotic)
# -------------------------------------------------
@ -61,3 +112,44 @@ else()
add_link_options(-fsanitize=address)
endif()
# --- Paramètres OTA ---
set(OTA_DIR "${CMAKE_SOURCE_DIR}/../ota/fw")
set(DEVICE "esp32p4")
# --- Nom final du binaire ---
set(OTA_BIN_NAME "${PROJECT_NAME}-v${FW_VERSION}.bin")
set(BUILD_BIN "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin")
set(BUILD_ELF "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf")
message(STATUS "Firmware version: ${FW_VERSION}")
# --- Target OTA ---
add_custom_target(ota_push ALL
COMMAND ${CMAKE_COMMAND} -E make_directory ${OTA_DIR}
COMMAND ${CMAKE_COMMAND} -E copy
${BUILD_BIN}
${OTA_DIR}/${OTA_BIN_NAME}
COMMAND ${CMAKE_COMMAND} -E copy
${BUILD_ELF}
${OTA_DIR}/latest.elf
COMMENT "📦 Copy firmware to OTA server directory"
DEPENDS app
)
add_custom_target(ota_latest
COMMAND ${CMAKE_COMMAND} -E echo
"{ \"version\":\"${FW_VERSION}\", \
\"bin\":\"${OTA_BIN_NAME}\" }"
> ${OTA_DIR}/latest.json
DEPENDS ota_push
)
add_custom_target(ota_to_mqtt
COMMAND podman run --rm eclipse-mosquitto:alpine
mosquitto_pub
-h 192.168.0.10
-t devices/esp32p4_01/ota/update
-m '{"version":"${FW_VERSION}","url":"https://192.168.0.9:8443/${OTA_BIN_NAME}","sha256":"","force":true}'
)

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);
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

@ -191,6 +191,13 @@ static void create_ui(void*)
#include <unistd.h> // usleep
#include "platform_detect.h"
bool suspended;
void suspendIHM(){
suspended=true;
#if CONFIG_IDF_TARGET_ESP32P4
lvgl_port_stop();
#endif
}
void drawIhm(void *param) {
// Init display + LVGL
init_display_ihm();
@ -205,10 +212,14 @@ void drawIhm(void *param) {
// Loop unifiée
while (1) {
ihm_gateway_process_queue();
#if CONFIG_IDF_TARGET_LINUX
uint32_t idle_time = lv_timer_handler();
#endif
if(!suspended){
ihm_gateway_process_queue();
#if CONFIG_IDF_TARGET_LINUX
uint32_t idle_time = lv_timer_handler();
#endif
}else{
vTaskDelay(500 / portTICK_PERIOD_MS);
}
// ESP32 : task FreeRTOS
//vTaskDelay(500 / portTICK_PERIOD_MS);
@ -248,6 +259,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 +304,6 @@ void init_display_ihm(){
mainState.display_init=true;
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
@ -433,7 +445,8 @@ void app_main_display()
// lv_label_bind_text(mqtt, &mqttStatus, "Mqtt %d");
lv_obj_t *scr = lv_scr_act();
lv_obj_add_style(scr, &style_gradient, 0);
//lv_obj_add_style(scr, &style_gradient, 0);
lv_obj_set_style_bg_color(scr,lv_color_darken(lv_color_make(0x9B, 0x18, 0x42), LV_OPA_40),0);
/* Your LVGL objects code here .... */
@ -1306,73 +1319,34 @@ 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 *day = lv_obj_find_by_name(parent, "day");
lv_obj_t *month = lv_obj_find_by_name(parent, "month");
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);
lv_obj_t *hour = lv_obj_find_by_name(parent, "hour");
lv_obj_t *minute = lv_obj_find_by_name(parent, "minute");
char buf[4];
bool refresh_date = false;
bool refresh_time = false;
int d = timeStruct->tm_mday;
int monthVal = timeStruct->tm_mon;
int h = timeStruct->tm_hour;
int m = timeStruct->tm_min;
// ---- 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;
}
char buf[12];
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;
}
snprintf(buf, sizeof(buf), "%02d", d);
lv_label_set_text(day, buf);
if (true || refresh_date) {
lv_spangroup_refresh(ctx->date_group);
}
snprintf(buf, sizeof(buf), "%02d", monthVal+1);
lv_label_set_text(month, buf);
// ---- 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);
}
/* ------------------------------------------------------------ */
@ -1544,6 +1518,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_t *btnUp = lv_button_create(cont_colVolets);
lv_obj_set_id(btnUp, (void *)"CV_BT_UP");
lv_obj_set_size(btnUp,60,60);
lv_obj_add_style(btnUp, &style_btn, 0);
lv_obj_add_event_cb(btnUp, event_handler, LV_EVENT_ALL, upEvent);
// lv_obj_align(btnUp, LV_ALIGN_CENTER, 0, -40);
@ -1558,6 +1533,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_name(btnDwn, "btnDown_#");
lv_obj_add_style(btnDwn, &style_btn, 0);
lv_obj_set_size(btnDwn,60,60);
lv_obj_add_event_cb(btnDwn, event_handler, LV_EVENT_ALL, downEvent);
// lv_obj_align(btnDwn, LV_ALIGN_CENTER, 0, -40);
lv_obj_remove_flag(btnDwn, LV_OBJ_FLAG_PRESS_LOCK);
@ -1585,10 +1561,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)
{
@ -1615,15 +1622,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_size(meteoContainer, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(meteoContainer, LV_FLEX_FLOW_COLUMN);
lv_obj_t *back = lv_label_create(meteoContainer);
lv_obj_add_flag(back, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_id(back, "backBtn");
lv_label_set_text(back, LV_SYMBOL_HOME);
//lv_obj_set_style_border_color(back, lv_color_make(255, 0, 0), 0);
//lv_obj_set_style_border_width(back, 1, 0);
lv_obj_set_style_text_font(back, lv_theme_get_font_large(back), 0);
//lv_obj_set_style_text_color(back, lv_color_white(), 0);
lv_obj_add_event_cb(back, backCb, LV_EVENT_CLICKED, NULL);
createBackBtn(meteoContainer);
lv_obj_t *title = lv_label_create(meteoContainer);
lv_obj_set_id(title, "titleMeteoLbl");
@ -1643,15 +1642,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_size(meteoContainer, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(meteoContainer, LV_FLEX_FLOW_COLUMN);
lv_obj_t *back = lv_label_create(meteoContainer);
lv_obj_add_flag(back, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_id(back, "backBtn");
lv_label_set_text(back, LV_SYMBOL_HOME);
//lv_obj_set_style_border_color(back, lv_color_make(255, 0, 0), 0);
//lv_obj_set_style_border_width(back, 1, 0);
lv_obj_set_style_text_font(back, lv_theme_get_font_large(back), 0);
lv_obj_set_style_text_color(back, lv_color_white(), 0);
lv_obj_add_event_cb(back, backCb, LV_EVENT_CLICKED, NULL);
createBackBtn(meteoContainer);
lv_obj_t *title = lv_label_create(meteoContainer);
lv_obj_set_id(title, "titleMeteoLbl");
@ -1673,15 +1664,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_size(meteoContainer, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(meteoContainer, LV_FLEX_FLOW_COLUMN);
lv_obj_t *back = lv_label_create(meteoContainer);
lv_obj_add_flag(back, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_id(back, "backBtn");
lv_label_set_text(back, LV_SYMBOL_HOME);
//lv_obj_set_style_border_color(back, lv_color_make(255, 0, 0), 0);
//lv_obj_set_style_border_width(back, 1, 0);
lv_obj_set_style_text_font(back, lv_theme_get_font_large(back), 0);
lv_obj_set_style_text_color(back, lv_color_white(), 0);
lv_obj_add_event_cb(back, backCb, LV_EVENT_CLICKED, NULL);
createBackBtn(meteoContainer);
lv_obj_t *title = lv_label_create(meteoContainer);
lv_obj_set_id(title, "titleMeteoLbl");
@ -1704,15 +1687,7 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_set_size(meteoContainer, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(meteoContainer, LV_FLEX_FLOW_COLUMN);
lv_obj_t *back = lv_label_create(meteoContainer);
lv_obj_add_flag(back, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_id(back, "backBtn");
lv_label_set_text(back, LV_SYMBOL_HOME);
//lv_obj_set_style_border_color(back, lv_color_make(255, 0, 0), 0);
//lv_obj_set_style_border_width(back, 1, 0);
lv_obj_set_style_text_font(back, lv_theme_get_font_large(back), 0);
lv_obj_set_style_text_color(back, lv_color_white(), 0);
lv_obj_add_event_cb(back, backCb, LV_EVENT_CLICKED, NULL);
createBackBtn(meteoContainer);
lv_obj_t *title = lv_label_create(meteoContainer);
lv_obj_set_id(title, "titleMeteoLbl");
@ -1722,7 +1697,24 @@ void messageCardContent(lv_obj_t *cont_messages)
lv_obj_add_event_cb(title, backCb, LV_EVENT_CLICKED, NULL);
draw_tabMeteo(meteoContainer);
}
void createBackBtn(lv_obj_t *meteoContainer)
{
lv_obj_t *backBtn = lv_button_create(meteoContainer);
lv_obj_add_flag(backBtn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_id(backBtn, "backBtn");
lv_obj_set_style_border_color(backBtn, lv_color_make(0, 0, 0), 0);
lv_obj_set_style_border_width(backBtn, 1, 0);
lv_obj_set_style_bg_opa(backBtn, 50, 0);
lv_obj_set_size(backBtn, 80, 80);
lv_obj_add_event_cb(backBtn, backCb, LV_EVENT_CLICKED, NULL);
lv_obj_t *backBtnLbl = lv_label_create(backBtn);
lv_label_set_text(backBtnLbl, LV_SYMBOL_HOME);
lv_obj_set_align(backBtnLbl, LV_TEXT_ALIGN_CENTER);
lv_obj_set_style_text_font(backBtnLbl, lv_theme_get_font_large(backBtnLbl), 0);
lv_obj_set_style_text_color(backBtnLbl, lv_color_white(), 0);
lv_obj_center(backBtnLbl);
}
void messagerieCb(lv_obj_t *base_obj)
@ -1731,6 +1723,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)
@ -1818,38 +1811,82 @@ void messageCardContent(lv_obj_t *cont_messages)
//lv_obj_remove_style_all(date_and_time);
lv_obj_set_size(date_and_time, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_t *date = lv_spangroup_create(date_and_time);
lv_obj_t *date = lv_obj_create(date_and_time);
lv_obj_set_name(date, "date");
lv_obj_set_id(date, "date");
lv_obj_set_size(date, 250, LV_SIZE_CONTENT);
lv_obj_clear_flag(date, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_opa(date, LV_OPA_TRANSP, 0);
lv_obj_set_style_border_width(date, 0, 0);
// Layout flex horizontal
lv_obj_set_flex_flow(date, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(
date,
LV_FLEX_ALIGN_SPACE_BETWEEN, // répartit les enfants
LV_FLEX_ALIGN_CENTER, // align vertical
LV_FLEX_ALIGN_CENTER
);
// lv_obj_add_style(date, &c->fonts[FONT_HEADING_MD], 0);
// lv_obj_add_style(date, &c->styles[STYLE_COLOR_BASE][STYLE_TYPE_TEXT], 0);
lv_span_t *day = lv_spangroup_add_span(date);
lv_span_set_text(day, "--");
lv_style_set_text_font(lv_span_get_style(day), &super_malibu_80);
lv_style_set_text_color(lv_span_get_style(day), base);
lv_style_set_line_color(lv_span_get_style(day), lv_color_black());
lv_style_set_line_width(lv_span_get_style(day), 2);
lv_span_t *month = lv_spangroup_add_span(date);
lv_span_set_text(month, "--");
lv_style_set_text_font(lv_span_get_style(month), &super_malibu_80);
lv_obj_t *day = lv_label_create(date);
lv_obj_set_name(day, "day");
lv_label_set_text(day, "--");
lv_obj_set_style_text_font(day, &super_malibu_80, 0);
lv_obj_set_style_text_color(day, lv_color_white(), 0);
lv_obj_set_style_line_color(day, lv_color_black(), 0);
lv_obj_set_style_line_width(day, 2, 0);
// --- / ---
lv_obj_t *slash_lbl = lv_label_create(date);
lv_obj_set_name(slash_lbl, "slash");
lv_obj_set_style_text_font(slash_lbl, &super_malibu_80, 0);
lv_obj_set_style_text_color(slash_lbl, base, 0);
lv_label_set_text(slash_lbl, "/");
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_obj_t *month = lv_label_create(date);
lv_obj_set_name(month, "month");
lv_label_set_text(month, "--");
lv_obj_set_style_text_font(month, &super_malibu_80,0);
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, date_and_time, NULL);
lv_obj_t * subContent = lv_obj_create(info_area);
lv_obj_set_flex_flow(subContent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(subContent, LV_PCT(70), LV_PCT(100));

View File

@ -35,10 +35,11 @@ void ihm_gateway_post_event(xIHMEvent_t *evt) {
extern lv_subject_t wifiStatus;
static xIHMEvent_t *evt = NULL;
void ihm_gateway_process_queue(void) {
if (!xIHMEventQueue) return;
xIHMEvent_t *evt = NULL;
#if CONFIG_IDF_TARGET_LINUX
while (xQueueReceive(xIHMEventQueue, &evt, pdMS_TO_TICKS(0)) == pdTRUE)
@ -103,6 +104,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

@ -19,12 +19,14 @@ void showMeteoIcon(const char *icon, lv_obj_t *desc_icon, int childNr);
void draw_tabVolets();
void meteoCb(lv_obj_t *base_obj);
void createBackBtn(lv_obj_t *ontainer);
void initHome();
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);
@ -43,4 +45,6 @@ void draw_tabCuve(lv_obj_t * parent);
void draw_tabHome(lv_obj_t * parent);
void draw_tabSettings(lv_obj_t * parent);
void suspendIHM();
void drawIhm(void *pvParameter);

View File

@ -10,6 +10,7 @@ list(APPEND EXTRA_COMPONENT_DIRS
../../stateManagement
../../eventsManager
../../RemindMe
../../washingMachineState
)
idf_build_set_property(COMPILE_DEFINITIONS "NO_DEBUG_STORAGE" APPEND)

View File

@ -2,7 +2,7 @@ dependencies:
idf:
source:
type: idf
version: 5.5.2
version: 5.5.0
lvgl/lvgl:
component_hash: 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f
dependencies: []

View File

@ -20,7 +20,7 @@ idf_component_register(SRCS
"../../include"
"../mock"
WHOLE_ARCHIVE
REQUIRES lvgl meteofrance RemindMe)
REQUIRES lvgl meteofrance RemindMe washingMachineState)
message("Including SDL2 support")
find_package(PkgConfig REQUIRED)

View File

@ -1,3 +1,9 @@
if(${IDF_TARGET} STREQUAL "esp32p4")
idf_component_register(SRCS "obtain_time.c" "eventsManager.c" obtain_time.c
INCLUDE_DIRS "include"
REQUIRES mqtt esp_netif meteofrance audio)
else()
idf_component_register(SRCS "obtain_time.c" "eventsManager.c" obtain_time.c
INCLUDE_DIRS "include"
REQUIRES mqtt esp_netif meteofrance)
endif()

View File

@ -6,7 +6,9 @@
#include "obtain_time.h"
#include <time.h>
#include "meteofrance.h"
#include "audio.h"
#if CONFIG_IDF_TARGET_ESP32P4
#include "audio.h"
#endif
EventGroupHandle_t domotic_event_group;
QueueHandle_t ihm_queue;
@ -30,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";
}
}
@ -173,6 +176,10 @@ void send_event(domo_events evt, void* pDatas) {
ihmEvt->eEventType = IHM_EVT_HUMID_TEMP;
ihmEvt->pvData = msg_copy4;
ihmEvt->bNeedToFreeData = true;
//On envoie les données via mqtt
esp_mqtt_client_publish(client, topicTempInt, pDatas, 0, 0, 0);
break;
case EVT_HAUTEUR_CUVE: {
@ -207,7 +214,19 @@ void send_event(domo_events evt, void* pDatas) {
}
case EVT_FIN_MACHINE:{
#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,8 +36,18 @@ 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();
void send_event(domo_events evt, void *pDatas);
#define topicTempExt "house/temp/282A802600008059"
#define topicHauteurCuve "house/cuve/hauteur"
#define topicTempInt "house/temp/287DCF1E00008020"
#define topicHauteurCuveEvol "house/cuve/hauteurEvol"
#define topicConsoElec "energy/puissance_5mn"
#define topicEtatMachine "energy/machine_en_route"
#define topicdomoticCommand "domotic/cmd"
#define topicTest "test"

View File

@ -73,8 +73,6 @@ void updateTime(void *pvParameter)
while (1)
{
time(&now);
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
tzset();
struct tm timeinfo = {0};
localtime_r(&now, &timeinfo);
sprintf(strftime_buf, "%s %d %s %02d:%02d (%lli mn depuis reboot)", days[timeinfo.tm_wday], timeinfo.tm_mday, months[timeinfo.tm_mon], timeinfo.tm_hour, timeinfo.tm_min, (esp_timer_get_time() / 1000 / 1000 / 60));

View File

@ -54,6 +54,29 @@ WashingMachineState getEtatMachine(){
return wms;
}
char *duree_depuis_timestamp_formatee(time_t timestamp)
{
time_t maintenant = time(NULL);
long diff_minutes = (maintenant - timestamp) / 60;
if (diff_minutes < 0)
diff_minutes = 0; // sécurité si timestamp futur
char *resultat = malloc(32);
if (!resultat)
return NULL;
if (diff_minutes < 60) {
snprintf(resultat, 32, "%ldmn", diff_minutes);
} else {
long heures = diff_minutes / 60;
long minutes = diff_minutes % 60;
snprintf(resultat, 32, "%ldh%02ld", heures, minutes);
}
return resultat;
}
void getEtatMachineStr(WashingMachineState wms, char* etat, size_t etatSize)
{
char* etatStr;
@ -73,10 +96,22 @@ void getEtatMachineStr(WashingMachineState wms, char* etat, size_t etatSize)
etatStr="Etat inconnu";
break;
}
char dateStr[30];
timestampToDate(wms.depuis,dateStr,30);
snprintf(etat,etatSize,"%s depuis %s", etatStr, dateStr);
//Si la machine est en route on ajoute la durée en heures/minutes depuis le départ
if(wms.etat==LAVEUSE_LAVAGE){
char *duree = duree_depuis_timestamp_formatee(wms.depuis);
if (duree) {
printf("Durée : %s\n", duree);
snprintf(etat,etatSize,"%s depuis %s\n (%s)", etatStr, duree, dateStr);
free(duree);
}
}else{
snprintf(etat,etatSize,"%s depuis %s", etatStr, dateStr);
}
ESP_LOGE(TAG,"%s",etat);
}

View File

@ -129,7 +129,7 @@ dependencies:
type: service
version: 1.0.4
espressif/esp_hosted:
component_hash: 5e1f8af8b51b47714eafb30c59bac952d6abc03125c01cb6a970f368d1e1cb83
component_hash: 9eea9ce83444a412b4a02311f787f3639a5d29fe67e6df83863010dd68ab250b
dependencies:
- name: idf
require: private
@ -137,7 +137,7 @@ dependencies:
source:
registry_url: https://components.espressif.com
type: service
version: 2.11.7
version: 2.12.0
espressif/esp_ipa:
component_hash: 31003e536f0d26158e10d7fcf2448b6377ce1148518287d0f34ed3b6c942c6d8
dependencies:
@ -217,7 +217,7 @@ dependencies:
type: service
version: 1.2.0~1
espressif/esp_lvgl_port:
component_hash: 88bbda87376ae20fabfbb5d794ed78d23b3b4a186ecfafecf2edbf06223fd6c9
component_hash: b6360960f47b6776462e7092861b3ea66477ffb762a01baa0aecbb3d74cd50f4
dependencies:
- name: idf
require: private
@ -229,7 +229,7 @@ dependencies:
source:
registry_url: https://components.espressif.com/
type: service
version: 2.7.1
version: 2.7.2
espressif/esp_sccb_intf:
component_hash: c071b189e49f40940722aea01a5489f873385cc39cd5b14012341135b85d1a9d
dependencies:
@ -351,12 +351,12 @@ dependencies:
type: service
version: 1.20.4
lvgl/lvgl:
component_hash: 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f
component_hash: 184e532558c1c45fefed631f3e235423d22582aafb4630f3e8885c35281a49ae
dependencies: []
source:
registry_url: https://components.espressif.com
type: service
version: 9.4.0
version: 9.5.0
suda-morris/am2302_rmt:
component_hash: 890df8ebfec652eb9f8e1d612959f00a951dbe9241335e5e335fc7fb1468ea32
dependencies:

View File

@ -9,7 +9,7 @@ if(${IDF_TARGET} STREQUAL "esp32p4")
idf_component_register(SRCS main.c communication.c
INCLUDE_DIRS "./include"
REQUIRES ${comps}
EMBED_TXTFILES ${PROJECT_DIR}/main/ca_cert.pem
EMBED_TXTFILES ${PROJECT_DIR}/main/ca_cert.pem ${PROJECT_DIR}/../ota/certs/server.crt
EMBED_FILES "index.html")
littlefs_create_partition_image(littlefs medias FLASH_IN_PROJECT)
elseif(${IDF_TARGET} STREQUAL "linux")

View File

@ -2,6 +2,7 @@
#include "esp_log.h"
#include "mqtt_client.h"
#include "stateManagement.h"
#include "eventsManager.h"
#if CONFIG_IDF_TARGET_ESP32P4
#include "esp_wifi.h"
#endif
@ -183,8 +184,8 @@ void splitIt(char *payload, unsigned int length, float *datas)
mqtt_callback mqttcb;
void mqtt_publish(const char *topic, const char *datas){
esp_mqtt_client_publish(client, topic, datas, 0, 1, 0);
void mqtt_publish(const char *topic, const char *datas, bool retain){
esp_mqtt_client_publish(client, topic, datas, 0, 1, retain);
}
/*
@ -238,6 +239,10 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
msg_id = esp_mqtt_client_subscribe(client, topicConsoElec, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "devices/esp32p4_01/ota/update", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
//msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
//ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
@ -285,7 +290,13 @@ void mqtt_app_start(mqtt_callback callback, EventGroupHandle_t domotic_event_gro
mqttcb=callback;
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://192.168.0.10",
.network.timeout_ms = 1000};
.network.timeout_ms = 1000,
.session.keepalive=30,
.session.last_will.topic="esp32p4_domotic/binary_sensor/online/state",
.session.last_will.msg = "offline",
.session.last_will.qos = 1,
.session.last_will.retain = 1
};
#if CONFIG_BROKER_URL_FROM_STDIN
char line[128];

View File

@ -16,14 +16,6 @@ typedef void (*wifi_callback)(wifi_evt evt);
typedef void (*mqtt_callback)(mqtt_evt evt, esp_mqtt_event_handle_t evt_data);
void wifi_init_sta(wifi_callback cb);
void mqtt_app_start(mqtt_callback cb, EventGroupHandle_t evtGroup);
void mqtt_publish(const char *topic, const char *datas);
void mqtt_publish(const char *topic, const char *datas, bool retain);
#define topicTempExt "house/temp/282A802600008059"
#define topicHauteurCuve "house/cuve/hauteur"
#define topicTempInt "house/temp/287DCF1E00008020"
#define topicHauteurCuveEvol "house/cuve/hauteurEvol"
#define topicConsoElec "energy/puissance_5mn"
#define topicEtatMachine "energy/machine_en_route"
#define topicdomoticCommand "domotic/cmd"
#define topicTest "test"

View File

@ -73,8 +73,8 @@
<h1>Developed by Mark</h1>
<h2>How to save and Download crash dump</h2>
</br></br>
<button class="btn success"><a href="http://192.168.4.1/download">Download</a></button></br></br>
<button class="btn success"><a href="http://192.168.4.1/crash">Crash</a></button>
<button class="btn success"><a href="./download">Download</a></button></br></br>
<button class="btn success"><a href="./crash">Crash</a></button>
</center>
</body>
</html>

View File

@ -20,10 +20,10 @@
#include "ihm_gateway.h"
// OTA
/*#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_ota_ops.h"
#include "esp_https_ota.h"
*/
#include "esp_http_client.h"
// Includes personnels
//#include "wifi_logger.h"
#include "obtain_time.h"
@ -39,6 +39,7 @@
#if CONFIG_IDF_TARGET_ESP32P4
#include "am2302_rmt.h"
#include "esp_wifi.h"
#endif
// GPIO assignment
#define AM2302_GPIO 4
@ -106,8 +107,128 @@ void bh1750_init(void)
bh1750 = bh1750_create(I2C_MASTER_NUM, BH1750_I2C_ADDRESS_DEFAULT, bus_handle);
*/
}
TaskHandle_t ihm_task;
typedef struct {
char version[32];
char url[256];
char sha256[65];
bool force;
} ota_msg_t;
static QueueHandle_t ota_queue;
static const char *OTA_TOPIC = "devices/esp32p4_01/ota/update";
#include <cJSON.h>
bool parse_ota_json(const char *json, ota_msg_t *msg)
{
ESP_LOGE("OTA","On demande à parser : %s", json);
cJSON *root = cJSON_Parse(json);
if (!root) return false;
snprintf(msg->version, sizeof(msg->version),
"%s", cJSON_GetStringValue(cJSON_GetObjectItem(root, "version")));
snprintf(msg->url, sizeof(msg->url),
"%s", cJSON_GetStringValue(cJSON_GetObjectItem(root, "url")));
snprintf(msg->sha256, sizeof(msg->sha256),
"%s", cJSON_GetStringValue(cJSON_GetObjectItem(root, "sha256")));
msg->force = cJSON_IsTrue(cJSON_GetObjectItem(root, "force"));
cJSON_Delete(root);
return true;
}
extern const uint8_t server_cert_pem_start[] asm("_binary_server_crt_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_server_crt_end");
void ota_task(void *arg)
{
ota_msg_t msg;
while (1) {
if (xQueueReceive(ota_queue, &msg, portMAX_DELAY)) {
ESP_LOGI("OTA", "Starting OTA to %s", msg.version);
esp_http_client_config_t http_cfg = {
.url = msg.url,
.cert_pem = (const char *)server_cert_pem_start,
.timeout_ms = 15000,
.skip_cert_common_name_check = true,
};
esp_https_ota_config_t ota_cfg = {
.http_config = &http_cfg,
};
lv_obj_t *ota = lv_msgbox_create(lv_scr_act());
lv_msgbox_add_text(ota,"Mise à jour OTA en cours");
suspendIHM();
esp_err_t ret = esp_https_ota(&ota_cfg);
if (ret == ESP_OK) {
ESP_LOGI("OTA", "OTA successful, rebooting");
esp_restart();
} else {
ESP_LOGE("OTA", "OTA failed (%s)", esp_err_to_name(ret));
}
}
}
}
static bool parse_semver(const char *ver, int *maj, int *min, int *pat)
{
return sscanf(ver, "%d.%d.%d", maj, min, pat) == 3;
}
static bool is_dev_build(void)
{
const esp_app_desc_t *desc = esp_app_get_description();
return strchr(desc->version, '-') != NULL;
}
static const char *get_suffix(const char *ver)
{
const char *dash = strchr(ver, '-');
return dash ? dash + 1 : NULL;
}
bool is_new_version(const char *incoming)
{
int cur_maj, cur_min, cur_pat;
int new_maj, new_min, new_pat;
const esp_app_desc_t *desc = esp_app_get_description();
const char *current = desc->version;
if (!parse_semver(current, &cur_maj, &cur_min, &cur_pat) ||
!parse_semver(incoming, &new_maj, &new_min, &new_pat)) {
ESP_LOGW("OTA", "Invalid version format");
return true; // fail-open
}
// 1⃣ Upgrade semver classique
if (new_maj != cur_maj) return new_maj > cur_maj;
if (new_min != cur_min) return new_min > cur_min;
if (new_pat != cur_pat) return new_pat > cur_pat;
// 2⃣ Même X.Y.Z → comportement dépend du mode
if (is_dev_build()) {
const char *cur_suffix = get_suffix(current);
const char *new_suffix = get_suffix(incoming);
// si une des deux n'a pas de suffixe → contexte différent
if (!cur_suffix || !new_suffix) {
ESP_LOGI("OTA", "Dev mode: context change (suffix missing)");
return true;
}
// suffix différent → OTA
if (strcmp(cur_suffix, new_suffix) != 0) {
ESP_LOGI("OTA", "Dev mode: branch change (%s → %s)",
cur_suffix, new_suffix);
return true;
}
}
ESP_LOGI("OTA", "No OTA needed");
return false;
}
void mqtt_cb(mqtt_evt evt, esp_mqtt_event_handle_t event){
switch (evt)
{
@ -128,11 +249,31 @@ void mqtt_cb(mqtt_evt evt, esp_mqtt_event_handle_t event){
break;
case MQTT_DATA_RECEIVED:
//lv_subject_set_int(&mqttStatus,2);
ESP_LOGD(TAG, "\nMQTT_EVENT_DATA");
ESP_LOGD(TAG, "TOPIC=%.*s\n", event->topic_len, event->topic);
ESP_LOGD(TAG, "DATA=%.*s\n", event->data_len, event->data);
ESP_LOGE(TAG, "MQTT_EVENT_DATA");
ESP_LOGE(TAG, "TOPIC=%.*s\n", event->topic_len, event->topic);
ESP_LOGE(TAG, "DATA=%.*s\n", event->data_len, event->data);
char *topic = strndup(event->topic, event->topic_len);
if (strcmp(topic, topicTempExt) == 0)
if (strncmp(topic, OTA_TOPIC, event->topic_len ) == 0) {
ota_msg_t msg = {0};
// ⚠️ copie safe (topic/data pas null-terminated)
char payload[512] = {0};
memcpy(payload, event->data, event->data_len);
if (!parse_ota_json(payload, &msg)) {
ESP_LOGE("OTA", "Invalid OTA JSON");
return;
}
if (!is_new_version(msg.version) && !msg.force) {
ESP_LOGI("OTA", "Version already installed");
return;
}
ESP_LOGI("OTA", "OTA requested: %s", msg.version);
xQueueSend(ota_queue, &msg, 0); }
else if (strcmp(topic, topicTempExt) == 0)
{
//if(lvgl_port_lock(50)){
float temp = strtof(event->data, NULL);
@ -297,7 +438,7 @@ extern char *days[7];
extern char *months[12];
/* esp_err_t _ota_http_event_handler(esp_http_client_event_t *evt)
esp_err_t _ota_http_event_handler(esp_http_client_event_t *evt)
{
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
@ -327,11 +468,10 @@ extern char *months[12];
}
return ESP_OK;
}
*/
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
/* static esp_err_t validate_image_header(esp_app_desc_t *new_app_info)
static esp_err_t validate_image_header(esp_app_desc_t *new_app_info)
{
if (new_app_info == NULL) {
return ESP_ERR_INVALID_ARG;
@ -352,12 +492,11 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
return ESP_OK;
}
*/
void simple_ota_example_task(void *pvParameter)
{
/* ESP_LOGE(TAG,"En attente connexion wifi");
ESP_LOGE(TAG,"En attente connexion wifi");
// Waiting until either the connection is established (WIFI_CONNECTED_BIT).
EventBits_t bits = xEventGroupWaitBits(domotic_event_group,
BIT0,
@ -381,8 +520,8 @@ void simple_ota_example_task(void *pvParameter)
esp_http_client_config_t config = {
.url = "https://192.168.0.28:8070/rgb_lcd.bin",
.timeout_ms = 30000,
.buffer_size = 20000,
.buffer_size_tx = 20000, //TX Buffer, Main Buffer
.buffer_size = 6144,
.buffer_size_tx = 6144, //TX Buffer, Main Buffer
.event_handler = _ota_http_event_handler,
.keep_alive_enable = true,
.cert_pem = (char *)server_cert_pem_start,
@ -483,7 +622,7 @@ ota_end:
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed");
vTaskDelete(NULL);
}
*/}
}
#if CONFIG_IDF_TARGET_ESP32P4
am2302_handle_t sensor = NULL;
@ -872,9 +1011,29 @@ void lightSensorTask(void *pvParameter){
*/
}
static void heartbeat_task(void *arg)
{
const char *topic = "esp32p4_domotic/binary_sensor/online/state";
while (1) {
mqtt_publish(
topic,
"online",
true);
vTaskDelay(pdMS_TO_TICKS(30000)); // 30 s
}
}
#define BUILD_TIME __DATE__ " " __TIME__
//#include "audio.h"
void app_main(void)
{
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
tzset();
ESP_ERROR_CHECK(esp_event_loop_create_default());
ihm_gateway_init();
@ -888,14 +1047,24 @@ void app_main(void)
#else
littlefs_mount();
xTaskCreate(&drawIhm,"ihm_task",10000,getIHMQueueHandle(),10,NULL);
xTaskCreate(&drawIhm,"ihm_task",10000,getIHMQueueHandle(),3,&ihm_task);
//et sinon on se connecte
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta(wifi_cb);
//start_wifi_logger();
/* Ensure to disable any WiFi power save mode, this allows best throughput
* and hence timings for overall OTA operation.
*/
esp_wifi_set_ps(WIFI_PS_NONE);
//xTaskCreate(&simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
#endif
boucleMeteo();
ota_queue = xQueueCreate(1, sizeof(ota_msg_t));
xTaskCreate(ota_task,"ota_task",8192,NULL,5,NULL);
mqtt_app_start(mqtt_cb, domotic_event_group);
TaskHandle_t xHandle = NULL;
@ -921,8 +1090,15 @@ void app_main(void)
xTaskCreate(&readTempHumid, "read_temp_task", 8192, NULL, 5, NULL);
#endif
esp_ota_mark_app_valid_cancel_rollback();
esp_app_desc_t *appDesc = esp_app_get_description();
char buff[300];
snprintf(buff,300,"%s %s %s", BUILD_TIME, appDesc->version, appDesc->idf_ver);
mqtt_publish("esp32p4_domotic/sensor/firmware/state",buff, true);
xTaskCreate(heartbeat_task, "heartbeat_task", 4096, NULL, 5, NULL);
start_webserver();
}
void boucleMeteo()

View File

@ -16,11 +16,13 @@ CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=8192
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_CACHE_L2_CACHE_LINE_128B=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
CONFIG_FATFS_LFN_HEAP=y
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM=n
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_VFS_MAX_COUNT=15
CONFIG_BSP_LCD_DPI_BUFFER_NUMS=2
CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR=y

View File

@ -1 +1 @@
0.3
0.0.5