#include #include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "lvgl.h" #include "battery_monitor.h" #include "metrics_ble.h" #include "metrics_serial.h" #include "st7305_rlcd.h" #include "temp_humidity_monitor.h" #include "ui_screen.h" static const char *TAG = "rlcd_app"; static void lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p) { uint16_t *buffer = (uint16_t *)color_p; for (int y = area->y1; y <= area->y2; y++) { for (int x = area->x1; x <= area->x2; x++) { uint8_t color = (*buffer < 0x7FFF) ? ST7305_COLOR_BLACK : ST7305_COLOR_WHITE; st7305_rlcd_set_pixel(x, y, color); buffer++; } } st7305_rlcd_flush(); lv_display_flush_ready(disp); } static void lvgl_tick_task(void *arg) { (void)arg; while (1) { vTaskDelay(pdMS_TO_TICKS(5)); lv_lock(); lv_tick_inc(5); lv_unlock(); } } static esp_err_t init_lvgl(void) { lv_init(); lv_display_t *disp = lv_display_create(st7305_rlcd_get_width(), st7305_rlcd_get_height()); lv_display_set_flush_cb(disp, lvgl_flush_cb); size_t buffer_size = LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565) * st7305_rlcd_get_width() * st7305_rlcd_get_height(); uint8_t *buffer = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM); if (buffer == NULL) { ESP_LOGE(TAG, "Failed to allocate LVGL buffer."); return ESP_ERR_NO_MEM; } lv_display_set_buffers(disp, buffer, NULL, buffer_size, LV_DISPLAY_RENDER_MODE_PARTIAL); xTaskCreate(lvgl_tick_task, "lv_tick", 4096, NULL, 1, NULL); return ESP_OK; } void app_main(void) { ESP_LOGI(TAG, "Starting ESP32-S3-RLCD workbench firmware."); ESP_ERROR_CHECK(st7305_rlcd_init()); ESP_ERROR_CHECK(init_lvgl()); lv_lock(); ui_screen_create(); lv_unlock(); metrics_serial_start(); metrics_ble_start(); battery_monitor_start(); temp_humidity_monitor_start(); int frame_counter = 0; uint32_t ble_ui_update_count = 0; TickType_t last_status_notify_tick = 0; int latest_battery_percent = -1; int latest_battery_mv = -1; int latest_temperature_c10 = -999; int latest_humidity_percent = -1; while (1) { vTaskDelay(pdMS_TO_TICKS(30)); ui_metrics_t metrics; if (metrics_ble_take_latest(&metrics)) { ui_screen_update_metrics_from_source(&metrics, "BLE"); ble_ui_update_count++; if ((ble_ui_update_count % 10) == 0) { ESP_LOGI(TAG, "BLE UI updated: %lu", (unsigned long)ble_ui_update_count); } } if (metrics_serial_take_latest(&metrics)) { ui_screen_update_metrics_from_source(&metrics, "USB"); } int battery_percent = 0; int battery_mv = 0; if (battery_monitor_take_latest(&battery_percent, &battery_mv)) { latest_battery_percent = battery_percent; latest_battery_mv = battery_mv; ui_screen_update_battery(battery_percent, battery_mv); } int temperature_c10 = 0; int humidity_percent = 0; if (temp_humidity_monitor_take_latest(&temperature_c10, &humidity_percent)) { latest_temperature_c10 = temperature_c10; latest_humidity_percent = humidity_percent; ui_screen_update_environment(temperature_c10, humidity_percent); } TickType_t now_tick = xTaskGetTickCount(); if ((now_tick - last_status_notify_tick) >= pdMS_TO_TICKS(5000)) { last_status_notify_tick = now_tick; uint32_t uptime_seconds = (uint32_t)(now_tick / configTICK_RATE_HZ); uint32_t internal_free_kb = (uint32_t)(heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024); uint32_t psram_free_kb = (uint32_t)(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024); metrics_ble_notify_status(latest_battery_percent, latest_battery_mv, latest_temperature_c10, latest_humidity_percent, uptime_seconds, internal_free_kb, psram_free_kb); } if (metrics_ble_take_ota_restart_request()) { ESP_LOGW(TAG, "BLE OTA complete, restarting."); vTaskDelay(pdMS_TO_TICKS(2000)); esp_restart(); } lv_lock(); lv_timer_handler(); frame_counter++; if (frame_counter >= 33) { frame_counter = 0; ui_screen_tick(); } lv_unlock(); } }