Files
pi-dashboard/components/user_app/user_app.cpp

239 lines
6.5 KiB
C++
Raw Normal View History

2026-02-15 04:15:30 +09:00
#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"
2026-02-15 21:11:33 +09:00
#include "audio_client.h"
2026-02-15 04:15:30 +09:00
#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);
2026-02-15 21:11:33 +09:00
/* Audio streaming client */
audio_client_init(AUDIO_SERVER_URI, s_codec);
2026-02-15 04:15:30 +09:00
/* 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();
2026-02-15 21:11:33 +09:00
/* Start audio streaming client */
audio_client_start();
2026-02-15 04:15:30 +09:00
/* 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 ---------- */
2026-02-15 12:57:05 +09:00
static void rtc_sync_if_needed(const pi_stats_t *stats)
{
if (!stats->time_valid) return;
rtcTimeStruct_t rtc = {};
Rtc_GetTime(&rtc);
/* Convert both to seconds-since-midnight for comparison */
int pi_secs = stats->time_hour * 3600 + stats->time_minute * 60 + stats->time_second;
int rtc_secs = rtc.hour * 3600 + rtc.minute * 60 + rtc.second;
int delta = pi_secs - rtc_secs;
if (delta < 0) delta = -delta;
/* Also check date mismatch as an immediate trigger */
bool date_mismatch = (rtc.year != stats->time_year ||
rtc.month != stats->time_month ||
rtc.day != stats->time_day);
if (date_mismatch || delta > 60) {
Rtc_SetTime(stats->time_year, stats->time_month, stats->time_day,
stats->time_hour, stats->time_minute, stats->time_second);
ESP_LOGI(TAG, "RTC synced from Pi: %04d-%02d-%02d %02d:%02d:%02d (drift: %ds)",
stats->time_year, stats->time_month, stats->time_day,
stats->time_hour, stats->time_minute, stats->time_second, delta);
}
}
2026-02-15 04:15:30 +09:00
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;
}
}
2026-02-15 12:57:05 +09:00
/* Sync RTC if Pi time drifts from board clock */
rtc_sync_if_needed(stats);
2026-02-15 04:15:30 +09:00
/* 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 = {};
2026-02-15 18:13:53 +09:00
int sensor_divider = 0;
2026-02-15 04:15:30 +09:00
for (;;) {
2026-02-15 18:13:53 +09:00
/* Read RTC every second */
2026-02-15 04:15:30 +09:00
Rtc_GetTime(&rtc_time);
2026-02-15 18:13:53 +09:00
/* Read SHTC3 + battery every 5 seconds */
if (sensor_divider == 0) {
s_shtc3->Shtc3_Wakeup();
vTaskDelay(pdMS_TO_TICKS(20));
s_shtc3->Shtc3_ReadTempHumi(&temp, &humidity);
s_shtc3->Shtc3_Sleep();
uint8_t batt = Adc_GetBatteryLevel();
if (Lvgl_lock(100)) {
dashboard_ui_update_local(temp, humidity, batt);
Lvgl_unlock();
}
}
sensor_divider = (sensor_divider + 1) % 5;
2026-02-15 04:15:30 +09:00
2026-02-15 18:13:53 +09:00
/* Update clock every second */
2026-02-15 04:15:30 +09:00
if (Lvgl_lock(100)) {
2026-02-15 18:13:53 +09:00
dashboard_ui_update_time(rtc_time.hour, rtc_time.minute, rtc_time.second,
rtc_time.year, rtc_time.month, rtc_time.day,
rtc_time.week);
2026-02-15 04:15:30 +09:00
Lvgl_unlock();
}
2026-02-15 18:13:53 +09:00
vTaskDelay(pdMS_TO_TICKS(1000));
2026-02-15 04:15:30 +09:00
}
}
/* ---------- 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");
}
}
}