#include "user_app.h" #include "dashboard_ui.h" #include "ws_client.h" #include "esp_wifi_bsp.h" #include "wifi_config.h" #include "i2c_bsp.h" #include "i2c_equipment.h" #include "adc_bsp.h" #include "button_bsp.h" #include "codec_bsp.h" #include "alert.h" #include "lvgl_bsp.h" #include #include #include #include #include static const char *TAG = "user_app"; /* Hardware objects */ static I2cMasterBus *s_i2c = nullptr; static Shtc3Port *s_shtc3 = nullptr; static CodecPort *s_codec = nullptr; /* Forward declarations */ static void sensor_task(void *arg); static void button_task(void *arg); static void ws_data_cb(const pi_stats_t *stats); static void ws_state_cb(ws_state_t state); void UserApp_AppInit(void) { ESP_LOGI(TAG, "Initializing hardware..."); /* I2C bus (SCL=14, SDA=13, port 0) */ s_i2c = new I2cMasterBus(14, 13, 0); /* SHTC3 temperature/humidity sensor */ s_shtc3 = new Shtc3Port(*s_i2c); ESP_LOGI(TAG, "SHTC3 ID: 0x%04X", s_shtc3->Shtc3_GetShtc3Id()); /* RTC */ Rtc_Setup(s_i2c, 0x51); /* ADC for battery */ Adc_PortInit(); /* Buttons */ Custom_ButtonInit(); /* Codec */ s_codec = new CodecPort(*s_i2c, "S3_RLCD_4_2"); /* Alert system */ alert_init(); alert_set_codec(s_codec); /* WebSocket client init (not started yet) */ ws_client_init(WS_SERVER_URI); ws_client_set_data_callback(ws_data_cb); ws_client_set_state_callback(ws_state_cb); ESP_LOGI(TAG, "Hardware init complete"); } void UserApp_UiInit(void) { dashboard_ui_create(); ESP_LOGI(TAG, "Dashboard UI created"); } void UserApp_TaskInit(void) { ESP_LOGI(TAG, "Starting background tasks..."); /* Start WebSocket client */ ws_client_start(); /* Sensor polling task - Core 1, 4KB stack */ xTaskCreatePinnedToCore(sensor_task, "sensor", 4 * 1024, NULL, 3, NULL, 1); /* Button handling task - Core 1 */ xTaskCreatePinnedToCore(button_task, "button", 2 * 1024, NULL, 2, NULL, 1); ESP_LOGI(TAG, "All tasks started"); } /* ---------- WebSocket callbacks ---------- */ static void ws_data_cb(const pi_stats_t *stats) { /* Check alert conditions */ if (stats->cpu_temp > 80.0f) { alert_trigger(ALERT_HIGH_TEMP); } for (int i = 0; i < stats->service_count; i++) { if (!stats->services[i].running) { alert_trigger(ALERT_SERVICE_DOWN); break; } } /* Update UI under LVGL lock */ if (Lvgl_lock(100)) { dashboard_ui_update_stats(stats); char ip_buf[20]; wifi_sta_get_ip_str(ip_buf, sizeof(ip_buf)); dashboard_ui_update_connection( ws_client_get_state(), wifi_sta_is_connected(), ip_buf ); Lvgl_unlock(); } } static void ws_state_cb(ws_state_t state) { ESP_LOGI(TAG, "WS state changed: %d", state); if (state == WS_STATE_DISCONNECTED || state == WS_STATE_ERROR) { alert_trigger(ALERT_WS_DISCONNECT); } else if (state == WS_STATE_CONNECTED) { alert_trigger(ALERT_CONNECT_OK); } /* Update connection indicator */ if (Lvgl_lock(100)) { char ip_buf[20]; wifi_sta_get_ip_str(ip_buf, sizeof(ip_buf)); dashboard_ui_update_connection(state, wifi_sta_is_connected(), ip_buf); Lvgl_unlock(); } } /* ---------- Sensor task ---------- */ static void sensor_task(void *arg) { float temp = 0, humidity = 0; rtcTimeStruct_t rtc_time = {}; for (;;) { /* Read SHTC3 */ s_shtc3->Shtc3_Wakeup(); vTaskDelay(pdMS_TO_TICKS(20)); s_shtc3->Shtc3_ReadTempHumi(&temp, &humidity); s_shtc3->Shtc3_Sleep(); /* Read RTC */ Rtc_GetTime(&rtc_time); /* Read battery */ uint8_t batt = Adc_GetBatteryLevel(); /* Update UI under LVGL lock */ if (Lvgl_lock(100)) { dashboard_ui_update_local(temp, humidity, batt); dashboard_ui_update_time(rtc_time.hour, rtc_time.minute, rtc_time.second); Lvgl_unlock(); } vTaskDelay(pdMS_TO_TICKS(5000)); } } /* ---------- Button task ---------- */ static void button_task(void *arg) { for (;;) { /* Wait for GP18 button event (single click = bit 0) */ EventBits_t bits = xEventGroupWaitBits( GP18ButtonGroups, set_bit_button(0), /* single press bit */ pdTRUE, /* clear on exit */ pdFALSE, /* any bit */ pdMS_TO_TICKS(500) ); if (bits & set_bit_button(0)) { bool muted = !alert_is_muted(); alert_mute(muted); ESP_LOGI(TAG, "GP18 pressed: alerts %s", muted ? "muted" : "unmuted"); } } }