193 lines
4.7 KiB
C++
193 lines
4.7 KiB
C++
|
|
#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 <esp_log.h>
|
||
|
|
#include <esp_timer.h>
|
||
|
|
#include <freertos/FreeRTOS.h>
|
||
|
|
#include <freertos/task.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
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");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|