mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-21 18:19:43 +00:00
Update mesh protocol handling and settings
This commit is contained in:
+274
-189
@@ -1,20 +1,21 @@
|
||||
#include "board/TLoRaPagerBoard.h"
|
||||
#include "ui/LV_Helper.h"
|
||||
#include <cmath>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "ui/LV_Helper.h"
|
||||
#include <Preferences.h>
|
||||
#include <cmath>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#include "app/app_context.h"
|
||||
#include "display/DisplayConfig.h"
|
||||
#include "ui/assets/images.h"
|
||||
#include "ui/ui_common.h"
|
||||
#include "app/app_context.h"
|
||||
#include "ui/widgets/system_notification.h"
|
||||
|
||||
// Custom app icons generated as C images (RGB565A8)
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
extern const lv_image_dsc_t gps_icon;
|
||||
extern const lv_image_dsc_t Chat;
|
||||
extern const lv_image_dsc_t Setting;
|
||||
@@ -28,12 +29,12 @@ extern "C" {
|
||||
#define MAIN_TIMING_DEBUG 0
|
||||
|
||||
// Forward declarations for app entry functions (implemented in ui_*.cpp)
|
||||
void ui_gps_enter(lv_obj_t *parent);
|
||||
void ui_chat_enter(lv_obj_t *parent);
|
||||
void ui_contacts_enter(lv_obj_t *parent);
|
||||
void ui_setting_enter(lv_obj_t *parent);
|
||||
void ui_gps_enter(lv_obj_t* parent);
|
||||
void ui_chat_enter(lv_obj_t* parent);
|
||||
void ui_contacts_enter(lv_obj_t* parent);
|
||||
void ui_setting_enter(lv_obj_t* parent);
|
||||
#ifdef ARDUINO_USB_MODE
|
||||
void ui_usb_enter(lv_obj_t *parent);
|
||||
void ui_usb_enter(lv_obj_t* parent);
|
||||
#endif
|
||||
|
||||
// GPS data access - now provided by GpsService
|
||||
@@ -59,23 +60,25 @@ bool isScreenSleepDisabled();
|
||||
// Use gps::GpsService::getInstance().getCollectionInterval()/setCollectionInterval()
|
||||
|
||||
// Factory-style menu structure (global for ui_*.cpp access)
|
||||
lv_obj_t *main_screen = nullptr;
|
||||
lv_obj_t *menu_panel = nullptr;
|
||||
lv_group_t *menu_g = nullptr;
|
||||
lv_group_t *app_g = nullptr;
|
||||
lv_obj_t *desc_label = nullptr;
|
||||
lv_obj_t *time_label = nullptr; // Time display label at top left of menu
|
||||
lv_obj_t *battery_label = nullptr; // Battery display label at top right of menu
|
||||
lv_obj_t* main_screen = nullptr;
|
||||
lv_obj_t* menu_panel = nullptr;
|
||||
lv_group_t* menu_g = nullptr;
|
||||
lv_group_t* app_g = nullptr;
|
||||
lv_obj_t* desc_label = nullptr;
|
||||
lv_obj_t* time_label = nullptr; // Time display label at top left of menu
|
||||
lv_obj_t* battery_label = nullptr; // Battery display label at top right of menu
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
// App function types (like factory example)
|
||||
typedef void (*app_func_t)(lv_obj_t *parent);
|
||||
typedef void (*app_func_t)(lv_obj_t* parent);
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
app_func_t setup_func_cb;
|
||||
app_func_t exit_func_cb;
|
||||
void *user_data;
|
||||
void* user_data;
|
||||
} app_t;
|
||||
|
||||
// App entry functions (implemented in ui_*.cpp files, global scope)
|
||||
@@ -105,7 +108,7 @@ app_t ui_setting_main = {
|
||||
};
|
||||
|
||||
// Shutdown app - directly triggers system shutdown
|
||||
static void ui_shutdown_enter(lv_obj_t *parent)
|
||||
static void ui_shutdown_enter(lv_obj_t* parent)
|
||||
{
|
||||
// Directly trigger software shutdown without confirmation dialog
|
||||
// The main menu access already implies user intent
|
||||
@@ -127,14 +130,14 @@ app_t ui_usb_main = {
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_USB_MODE
|
||||
const char *kAppNames[6] = {"GPS", "Chat", "Contacts", "USB Mass Storage", "Setting", "Shutdown"};
|
||||
const lv_image_dsc_t *kAppImages[6] = {&gps_icon, &Chat, &contact, &img_usb, &Setting, &shutdown};
|
||||
app_t *kAppFuncs[6] = {&ui_gps_main, &ui_chat_main, &ui_contacts_main, &ui_usb_main, &ui_setting_main, &ui_shutdown_main};
|
||||
const char* kAppNames[6] = {"GPS", "Chat", "Contacts", "USB Mass Storage", "Setting", "Shutdown"};
|
||||
const lv_image_dsc_t* kAppImages[6] = {&gps_icon, &Chat, &contact, &img_usb, &Setting, &shutdown};
|
||||
app_t* kAppFuncs[6] = {&ui_gps_main, &ui_chat_main, &ui_contacts_main, &ui_usb_main, &ui_setting_main, &ui_shutdown_main};
|
||||
#define NUM_APPS 6
|
||||
#else
|
||||
const char *kAppNames[5] = {"GPS", "Chat", "Contacts", "Setting", "Shutdown"};
|
||||
const lv_image_dsc_t *kAppImages[5] = {&gps_icon, &Chat, &contact, &Setting, &shutdown};
|
||||
app_t *kAppFuncs[5] = {&ui_gps_main, &ui_chat_main, &ui_contacts_main, &ui_setting_main, &ui_shutdown_main};
|
||||
const char* kAppNames[5] = {"GPS", "Chat", "Contacts", "Setting", "Shutdown"};
|
||||
const lv_image_dsc_t* kAppImages[5] = {&gps_icon, &Chat, &contact, &Setting, &shutdown};
|
||||
app_t* kAppFuncs[5] = {&ui_gps_main, &ui_chat_main, &ui_contacts_main, &ui_setting_main, &ui_shutdown_main};
|
||||
#define NUM_APPS 5
|
||||
#endif
|
||||
|
||||
@@ -149,22 +152,23 @@ void menu_hidden()
|
||||
lv_tileview_set_tile_by_index(main_screen, 0, 1, LV_ANIM_ON);
|
||||
}
|
||||
|
||||
static void btn_event_cb(lv_event_t *e)
|
||||
static void btn_event_cb(lv_event_t* e)
|
||||
{
|
||||
lv_event_code_t c = lv_event_get_code(e);
|
||||
const char *text = (const char *)lv_event_get_user_data(e);
|
||||
if (c == LV_EVENT_FOCUSED) {
|
||||
const char* text = (const char*)lv_event_get_user_data(e);
|
||||
if (c == LV_EVENT_FOCUSED)
|
||||
{
|
||||
#if LVGL_VERSION_MAJOR == 9
|
||||
lv_obj_send_event(desc_label, (lv_event_code_t)name_change_id, (void *)text);
|
||||
lv_obj_send_event(desc_label, (lv_event_code_t)name_change_id, (void*)text);
|
||||
#else
|
||||
// For LVGL v8, would use lv_msg_send
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void create_app(lv_obj_t *parent, const char *name, const lv_image_dsc_t *img, app_t *app_fun)
|
||||
static void create_app(lv_obj_t* parent, const char* name, const lv_image_dsc_t* img, app_t* app_fun)
|
||||
{
|
||||
lv_obj_t *btn = lv_btn_create(parent);
|
||||
lv_obj_t* btn = lv_btn_create(parent);
|
||||
lv_coord_t w = 150;
|
||||
lv_coord_t h = LV_PCT(100);
|
||||
|
||||
@@ -174,21 +178,25 @@ static void create_app(lv_obj_t *parent, const char *name, const lv_image_dsc_t
|
||||
lv_obj_set_style_shadow_width(btn, 30, LV_PART_MAIN);
|
||||
lv_obj_set_style_shadow_color(btn, lv_color_black(), LV_PART_MAIN);
|
||||
uint32_t phy_hor_res = lv_display_get_physical_horizontal_resolution(NULL);
|
||||
if (phy_hor_res < 320) {
|
||||
if (phy_hor_res < 320)
|
||||
{
|
||||
lv_obj_set_style_radius(btn, LV_RADIUS_CIRCLE, 0);
|
||||
}
|
||||
lv_obj_set_user_data(btn, (void *)name);
|
||||
lv_obj_set_user_data(btn, (void*)name);
|
||||
|
||||
if (img != NULL) {
|
||||
lv_obj_t *icon = lv_image_create(btn);
|
||||
if (img != NULL)
|
||||
{
|
||||
lv_obj_t* icon = lv_image_create(btn);
|
||||
lv_image_set_src(icon, img);
|
||||
lv_obj_center(icon);
|
||||
}
|
||||
/* Text change event callback */
|
||||
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_FOCUSED, (void *)name);
|
||||
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_FOCUSED, (void*)name);
|
||||
|
||||
/* Click to select event callback */
|
||||
lv_obj_add_event_cb(btn, [](lv_event_t *e) {
|
||||
lv_obj_add_event_cb(
|
||||
btn, [](lv_event_t* e)
|
||||
{
|
||||
lv_event_code_t c = lv_event_get_code(e);
|
||||
app_t *func_cb = (app_t *)lv_event_get_user_data(e);
|
||||
lv_obj_t *parent = lv_obj_get_child(main_screen, 1);
|
||||
@@ -201,16 +209,16 @@ static void create_app(lv_obj_t *parent, const char *name, const lv_image_dsc_t
|
||||
(*func_cb->setup_func_cb)(parent);
|
||||
}
|
||||
menu_hidden();
|
||||
}
|
||||
},
|
||||
LV_EVENT_CLICKED, app_fun);
|
||||
} },
|
||||
LV_EVENT_CLICKED, app_fun);
|
||||
}
|
||||
|
||||
void menu_name_label_event_cb(lv_event_t *e)
|
||||
void menu_name_label_event_cb(lv_event_t* e)
|
||||
{
|
||||
#if LVGL_VERSION_MAJOR == 9
|
||||
const char *v = (const char *)lv_event_get_param(e);
|
||||
if (v) {
|
||||
const char* v = (const char*)lv_event_get_param(e);
|
||||
if (v)
|
||||
{
|
||||
lv_label_set_text(lv_event_get_target_obj(e), v);
|
||||
}
|
||||
#else
|
||||
@@ -226,14 +234,14 @@ void menu_name_label_event_cb(lv_event_t *e)
|
||||
// GPS data collection task is now in TLoRaPagerBoard class
|
||||
|
||||
// Screen sleep management
|
||||
static uint32_t last_user_activity_time = 0; // Timestamp of last user activity
|
||||
static uint32_t last_user_activity_time = 0; // Timestamp of last user activity
|
||||
static SemaphoreHandle_t activity_mutex = NULL;
|
||||
static TaskHandle_t screen_sleep_task_handle = NULL;
|
||||
static bool screen_sleeping = false;
|
||||
static bool screen_sleep_disabled = false; // Flag to disable screen sleep (e.g., during USB mode)
|
||||
static bool screen_sleep_disabled = false; // Flag to disable screen sleep (e.g., during USB mode)
|
||||
static uint8_t saved_keyboard_brightness = 127; // Save keyboard brightness before sleep (default 127)
|
||||
static uint32_t screen_sleep_timeout_ms = 30000; // Default 30 seconds, can be configured
|
||||
static Preferences preferences; // For saving/loading settings
|
||||
static uint32_t screen_sleep_timeout_ms = 30000; // Default 30 seconds, can be configured
|
||||
static Preferences preferences; // For saving/loading settings
|
||||
|
||||
// Power management wrapper function for accessing preferences
|
||||
Preferences& getPreferencesInstance() { return preferences; }
|
||||
@@ -251,8 +259,10 @@ TaskHandle_t getScreenSleepTaskHandle() { return screen_sleep_task_handle; }
|
||||
bool isScreenSleeping()
|
||||
{
|
||||
bool sleeping = false;
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
sleeping = screen_sleeping;
|
||||
xSemaphoreGive(activity_mutex);
|
||||
}
|
||||
@@ -267,26 +277,29 @@ bool isScreenSleeping()
|
||||
*/
|
||||
uint32_t getScreenSleepTimeout()
|
||||
{
|
||||
uint32_t timeout = 30000; // Default 30 seconds
|
||||
|
||||
uint32_t timeout = 30000; // Default 30 seconds
|
||||
|
||||
// Read from persistent storage to ensure we have the latest value
|
||||
preferences.begin("settings", true); // Read-only mode
|
||||
timeout = preferences.getUInt("sleep_timeout", 30000); // Default 30 seconds
|
||||
preferences.begin("settings", true); // Read-only mode
|
||||
timeout = preferences.getUInt("sleep_timeout", 30000); // Default 30 seconds
|
||||
preferences.end();
|
||||
|
||||
|
||||
// Ensure minimum timeout
|
||||
if (timeout < 10000) {
|
||||
if (timeout < 10000)
|
||||
{
|
||||
timeout = 30000;
|
||||
}
|
||||
|
||||
|
||||
// Also update memory variable for faster access
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
screen_sleep_timeout_ms = timeout;
|
||||
xSemaphoreGive(activity_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -299,9 +312,11 @@ void setScreenSleepTimeout(uint32_t timeout_ms)
|
||||
// Minimum 10 seconds, maximum 300 seconds (5 minutes)
|
||||
if (timeout_ms < 10000) timeout_ms = 10000;
|
||||
if (timeout_ms > 300000) timeout_ms = 300000;
|
||||
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE) {
|
||||
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
screen_sleep_timeout_ms = timeout_ms;
|
||||
// Save to preferences
|
||||
preferences.begin("settings", false);
|
||||
@@ -321,15 +336,19 @@ void setScreenSleepTimeout(uint32_t timeout_ms)
|
||||
*/
|
||||
void disableScreenSleep()
|
||||
{
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
screen_sleep_disabled = true;
|
||||
// Wake up screen if it's sleeping
|
||||
if (screen_sleeping) {
|
||||
if (screen_sleeping)
|
||||
{
|
||||
screen_sleeping = false;
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
// Restore keyboard brightness
|
||||
if (instance.hasKeyboard()) {
|
||||
if (instance.hasKeyboard())
|
||||
{
|
||||
instance.kb.setBrightness(saved_keyboard_brightness);
|
||||
}
|
||||
}
|
||||
@@ -343,8 +362,10 @@ void disableScreenSleep()
|
||||
*/
|
||||
void enableScreenSleep()
|
||||
{
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
screen_sleep_disabled = false;
|
||||
// Reset activity time to current time to prevent immediate sleep
|
||||
last_user_activity_time = millis();
|
||||
@@ -360,8 +381,10 @@ void enableScreenSleep()
|
||||
bool isScreenSleepDisabled()
|
||||
{
|
||||
bool disabled = false;
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
disabled = screen_sleep_disabled;
|
||||
xSemaphoreGive(activity_mutex);
|
||||
}
|
||||
@@ -375,15 +398,19 @@ bool isScreenSleepDisabled()
|
||||
*/
|
||||
void updateUserActivity()
|
||||
{
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
last_user_activity_time = millis();
|
||||
// If screen is sleeping, wake it up
|
||||
if (screen_sleeping) {
|
||||
if (screen_sleeping)
|
||||
{
|
||||
screen_sleeping = false;
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
// Restore keyboard brightness
|
||||
if (instance.hasKeyboard()) {
|
||||
if (instance.hasKeyboard())
|
||||
{
|
||||
instance.kb.setBrightness(saved_keyboard_brightness);
|
||||
}
|
||||
}
|
||||
@@ -397,76 +424,90 @@ void updateUserActivity()
|
||||
* Monitors user activity and puts screen to sleep after 30 seconds of inactivity
|
||||
* Wakes up screen when user input is detected
|
||||
*/
|
||||
static void screenSleepTask(void *pvParameters)
|
||||
static void screenSleepTask(void* pvParameters)
|
||||
{
|
||||
(void)pvParameters;
|
||||
TickType_t last_wake_time = xTaskGetTickCount();
|
||||
const TickType_t check_interval = pdMS_TO_TICKS(1000); // Check every 1 second
|
||||
|
||||
while (true) {
|
||||
const TickType_t check_interval = pdMS_TO_TICKS(1000); // Check every 1 second
|
||||
|
||||
while (true)
|
||||
{
|
||||
// User activity detection is now handled by LVGL input device callbacks:
|
||||
// - lv_encoder_read() calls updateUserActivity() for rotary encoder
|
||||
// - keypad_read() calls updateUserActivity() for keyboard
|
||||
// No need to poll here, as it would consume input events before LVGL can process them
|
||||
|
||||
|
||||
// Check if screen should sleep or wake
|
||||
if (activity_mutex != NULL) {
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
if (activity_mutex != NULL)
|
||||
{
|
||||
if (xSemaphoreTake(activity_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
uint32_t current_time = millis();
|
||||
uint32_t time_since_activity = current_time - last_user_activity_time;
|
||||
|
||||
|
||||
// Get current timeout from persistent storage (may have been changed in settings)
|
||||
// Read directly from preferences to ensure we have the latest value
|
||||
preferences.begin("settings", true); // Read-only mode
|
||||
preferences.begin("settings", true); // Read-only mode
|
||||
uint32_t current_timeout = preferences.getUInt("sleep_timeout", 30000);
|
||||
preferences.end();
|
||||
|
||||
|
||||
// Ensure minimum timeout
|
||||
if (current_timeout < 10000) {
|
||||
if (current_timeout < 10000)
|
||||
{
|
||||
current_timeout = 30000;
|
||||
}
|
||||
|
||||
|
||||
// Update memory variable
|
||||
screen_sleep_timeout_ms = current_timeout;
|
||||
|
||||
|
||||
// Skip sleep if screen sleep is disabled (e.g., during USB mode)
|
||||
if (screen_sleep_disabled) {
|
||||
if (screen_sleep_disabled)
|
||||
{
|
||||
// If screen is sleeping but sleep is now disabled, wake it up
|
||||
if (screen_sleeping) {
|
||||
if (screen_sleeping)
|
||||
{
|
||||
screen_sleeping = false;
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
// Restore keyboard brightness
|
||||
if (instance.hasKeyboard()) {
|
||||
if (instance.hasKeyboard())
|
||||
{
|
||||
instance.kb.setBrightness(saved_keyboard_brightness);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(activity_mutex);
|
||||
} else {
|
||||
// Normal sleep logic
|
||||
if (!screen_sleeping && time_since_activity >= current_timeout) {
|
||||
// Put screen to sleep
|
||||
screen_sleeping = true;
|
||||
// Save current keyboard brightness before turning off
|
||||
if (instance.hasKeyboard()) {
|
||||
saved_keyboard_brightness = instance.kb.getBrightness();
|
||||
instance.kb.setBrightness(0); // Turn off keyboard backlight
|
||||
}
|
||||
instance.setBrightness(0); // Turn off display backlight
|
||||
} else if (screen_sleeping && time_since_activity < current_timeout) {
|
||||
// Wake up screen (shouldn't happen here, but just in case)
|
||||
screen_sleeping = false;
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
// Restore keyboard brightness
|
||||
if (instance.hasKeyboard()) {
|
||||
instance.kb.setBrightness(saved_keyboard_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(activity_mutex);
|
||||
else
|
||||
{
|
||||
// Normal sleep logic
|
||||
if (!screen_sleeping && time_since_activity >= current_timeout)
|
||||
{
|
||||
// Put screen to sleep
|
||||
screen_sleeping = true;
|
||||
// Save current keyboard brightness before turning off
|
||||
if (instance.hasKeyboard())
|
||||
{
|
||||
saved_keyboard_brightness = instance.kb.getBrightness();
|
||||
instance.kb.setBrightness(0); // Turn off keyboard backlight
|
||||
}
|
||||
instance.setBrightness(0); // Turn off display backlight
|
||||
}
|
||||
else if (screen_sleeping && time_since_activity < current_timeout)
|
||||
{
|
||||
// Wake up screen (shouldn't happen here, but just in case)
|
||||
screen_sleeping = false;
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
// Restore keyboard brightness
|
||||
if (instance.hasKeyboard())
|
||||
{
|
||||
instance.kb.setBrightness(saved_keyboard_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(activity_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wait for next check
|
||||
vTaskDelayUntil(&last_wake_time, check_interval);
|
||||
}
|
||||
@@ -475,7 +516,7 @@ static void screenSleepTask(void *pvParameters)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(100); // Give Serial time to stabilize before printing logs
|
||||
delay(100); // Give Serial time to stabilize before printing logs
|
||||
Serial.printf("\n\n[Setup] ===== SYSTEM STARTUP =====\n");
|
||||
Serial.printf("[Setup] Serial initialized at 115200 baud\n");
|
||||
|
||||
@@ -483,27 +524,32 @@ void setup()
|
||||
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
|
||||
bool waking_from_sleep = (wakeup_reason != ESP_SLEEP_WAKEUP_UNDEFINED);
|
||||
|
||||
if (waking_from_sleep) {
|
||||
if (waking_from_sleep)
|
||||
{
|
||||
Serial.printf("[Setup] Wakeup cause: %d\n", wakeup_reason);
|
||||
}
|
||||
|
||||
instance.begin();
|
||||
|
||||
// If waking from deep sleep, perform wake up initialization
|
||||
if (waking_from_sleep) {
|
||||
if (waking_from_sleep)
|
||||
{
|
||||
instance.wakeUp();
|
||||
}
|
||||
beginLvglHelper(instance);
|
||||
|
||||
|
||||
// Initialize system notification component
|
||||
ui::SystemNotification::init();
|
||||
|
||||
|
||||
// Initialize chat application context
|
||||
app::AppContext& app_ctx = app::AppContext::getInstance();
|
||||
bool use_mock = false; // Enable real LoRa adapter for logging and radio tests
|
||||
if (app_ctx.init(instance, use_mock)) {
|
||||
if (app_ctx.init(instance, use_mock))
|
||||
{
|
||||
Serial.printf("[Setup] Chat application context initialized\n");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("[Setup] WARNING: Failed to initialize chat context\n");
|
||||
}
|
||||
|
||||
@@ -539,63 +585,72 @@ void setup()
|
||||
/* Create time label at top left of menu */
|
||||
time_label = lv_label_create(menu_panel);
|
||||
lv_obj_set_width(time_label, LV_SIZE_CONTENT);
|
||||
lv_obj_align(time_label, LV_ALIGN_TOP_LEFT, 5, 0); // Top left, 5px from left edge
|
||||
lv_obj_align(time_label, LV_ALIGN_TOP_LEFT, 5, 0); // Top left, 5px from left edge
|
||||
lv_obj_set_style_text_align(time_label, LV_TEXT_ALIGN_LEFT, 0);
|
||||
lv_obj_set_style_text_color(time_label, lv_color_black(), 0); // Black text for better contrast
|
||||
lv_obj_set_style_text_color(time_label, lv_color_black(), 0); // Black text for better contrast
|
||||
// Add light background to make black text visible
|
||||
lv_obj_set_style_bg_color(time_label, lv_color_white(), 0);
|
||||
lv_obj_set_style_bg_opa(time_label, LV_OPA_80, 0); // Semi-transparent white background
|
||||
lv_obj_set_style_bg_opa(time_label, LV_OPA_80, 0); // Semi-transparent white background
|
||||
lv_obj_set_style_pad_all(time_label, 4, 0);
|
||||
// Make sure time label is on top
|
||||
lv_obj_move_foreground(time_label);
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320) {
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320)
|
||||
{
|
||||
lv_obj_set_style_text_font(time_label, &lv_font_montserrat_14, 0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_obj_set_style_text_font(time_label, &lv_font_montserrat_18, 0);
|
||||
}
|
||||
lv_label_set_text(time_label, "--:--");
|
||||
|
||||
|
||||
/* Create battery label at top right of menu */
|
||||
battery_label = lv_label_create(menu_panel);
|
||||
lv_obj_set_width(battery_label, LV_SIZE_CONTENT);
|
||||
lv_obj_align(battery_label, LV_ALIGN_TOP_RIGHT, -5, 0); // Top right, 5px from right edge
|
||||
lv_obj_align(battery_label, LV_ALIGN_TOP_RIGHT, -5, 0); // Top right, 5px from right edge
|
||||
lv_obj_set_style_text_align(battery_label, LV_TEXT_ALIGN_RIGHT, 0);
|
||||
lv_obj_set_style_text_color(battery_label, lv_color_black(), 0);
|
||||
// Add light background to make black text visible
|
||||
lv_obj_set_style_bg_color(battery_label, lv_color_white(), 0);
|
||||
lv_obj_set_style_bg_opa(battery_label, LV_OPA_80, 0); // Semi-transparent white background
|
||||
lv_obj_set_style_bg_opa(battery_label, LV_OPA_80, 0); // Semi-transparent white background
|
||||
lv_obj_set_style_pad_all(battery_label, 4, 0);
|
||||
// Make sure battery label is on top
|
||||
lv_obj_move_foreground(battery_label);
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320) {
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320)
|
||||
{
|
||||
lv_obj_set_style_text_font(battery_label, &lv_font_montserrat_14, 0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_obj_set_style_text_font(battery_label, &lv_font_montserrat_18, 0);
|
||||
}
|
||||
lv_label_set_text(battery_label, "?%");
|
||||
|
||||
|
||||
/* Initialize the menu view - moved down to make room for time */
|
||||
lv_obj_t *panel = lv_obj_create(menu_panel);
|
||||
lv_obj_t* panel = lv_obj_create(menu_panel);
|
||||
lv_obj_set_scrollbar_mode(panel, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_set_size(panel, LV_PCT(100), LV_PCT(70));
|
||||
lv_obj_set_scroll_snap_x(panel, LV_SCROLL_SNAP_CENTER);
|
||||
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
|
||||
// Move panel down to make room for time label (adjust offset based on screen size)
|
||||
int panel_offset = 30; // Offset for time label space
|
||||
if (lv_display_get_physical_vertical_resolution(NULL) > 320) {
|
||||
panel_offset = 35; // Slightly more space on larger screens
|
||||
int panel_offset = 30; // Offset for time label space
|
||||
if (lv_display_get_physical_vertical_resolution(NULL) > 320)
|
||||
{
|
||||
panel_offset = 35; // Slightly more space on larger screens
|
||||
}
|
||||
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, panel_offset);
|
||||
lv_obj_add_style(panel, &style_frameless, 0);
|
||||
|
||||
/* Add applications */
|
||||
for (int i = 0; i < NUM_APPS; ++i) {
|
||||
for (int i = 0; i < NUM_APPS; ++i)
|
||||
{
|
||||
create_app(panel, kAppNames[i], kAppImages[i], kAppFuncs[i]);
|
||||
lv_group_add_obj(menu_g, lv_obj_get_child(panel, i));
|
||||
}
|
||||
|
||||
int offset = -10;
|
||||
if (lv_display_get_physical_vertical_resolution(NULL) > 320) {
|
||||
if (lv_display_get_physical_vertical_resolution(NULL) > 320)
|
||||
{
|
||||
offset = -45;
|
||||
}
|
||||
/* Initialize the label */
|
||||
@@ -604,10 +659,13 @@ void setup()
|
||||
lv_obj_align(desc_label, LV_ALIGN_BOTTOM_MID, 0, offset);
|
||||
lv_obj_set_style_text_align(desc_label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320) {
|
||||
if (lv_display_get_physical_horizontal_resolution(NULL) < 320)
|
||||
{
|
||||
lv_obj_set_style_text_font(desc_label, &lv_font_montserrat_16, 0);
|
||||
lv_obj_align(desc_label, LV_ALIGN_BOTTOM_MID, 0, -25);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_obj_set_style_text_font(desc_label, &lv_font_montserrat_20, 0);
|
||||
}
|
||||
lv_label_set_long_mode(desc_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
@@ -625,9 +683,10 @@ void setup()
|
||||
// Create timer to update time display (minimum resource usage)
|
||||
// Update every 60 seconds, display format: HH:MM (no seconds)
|
||||
// This minimizes I2C communication and UI updates
|
||||
const uint32_t time_update_interval_ms = 60000; // 60 seconds = minimum update frequency
|
||||
|
||||
lv_timer_t *time_timer = lv_timer_create([](lv_timer_t *timer) {
|
||||
const uint32_t time_update_interval_ms = 60000; // 60 seconds = minimum update frequency
|
||||
|
||||
lv_timer_t* time_timer = lv_timer_create([](lv_timer_t* timer)
|
||||
{
|
||||
if (time_label == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -651,15 +710,16 @@ void setup()
|
||||
} else {
|
||||
// If RTC read fails, show error indicator
|
||||
lv_label_set_text(time_label, "??:??");
|
||||
}
|
||||
}, time_update_interval_ms, NULL);
|
||||
lv_timer_set_repeat_count(time_timer, -1); // Repeat indefinitely
|
||||
|
||||
} },
|
||||
time_update_interval_ms, NULL);
|
||||
lv_timer_set_repeat_count(time_timer, -1); // Repeat indefinitely
|
||||
|
||||
// Create timer to update battery display (minimum resource usage)
|
||||
// Update every 60 seconds (1 minute) - battery changes slowly, minimize I2C communication
|
||||
const uint32_t battery_update_interval_ms = 60000; // 60 seconds = minimum update frequency
|
||||
|
||||
lv_timer_t *battery_timer = lv_timer_create([](lv_timer_t *timer) {
|
||||
const uint32_t battery_update_interval_ms = 60000; // 60 seconds = minimum update frequency
|
||||
|
||||
lv_timer_t* battery_timer = lv_timer_create([](lv_timer_t* timer)
|
||||
{
|
||||
if (battery_label == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -683,36 +743,50 @@ void setup()
|
||||
lv_label_set_text(battery_label, battery_str);
|
||||
strncpy(last_battery_str, battery_str, sizeof(last_battery_str) - 1);
|
||||
last_battery_str[sizeof(last_battery_str) - 1] = '\0';
|
||||
}
|
||||
}, battery_update_interval_ms, NULL);
|
||||
lv_timer_set_repeat_count(battery_timer, -1); // Repeat indefinitely
|
||||
|
||||
} },
|
||||
battery_update_interval_ms, NULL);
|
||||
lv_timer_set_repeat_count(battery_timer, -1); // Repeat indefinitely
|
||||
|
||||
// Update time immediately (don't wait for first timer tick)
|
||||
if (instance.isRTCReady()) {
|
||||
if (instance.isRTCReady())
|
||||
{
|
||||
char time_str[16];
|
||||
if (instance.getRTCTimeString(time_str, sizeof(time_str), false)) {
|
||||
if (instance.getRTCTimeString(time_str, sizeof(time_str), false))
|
||||
{
|
||||
lv_label_set_text(time_label, time_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update battery immediately (don't wait for first timer tick)
|
||||
char battery_str[32];
|
||||
bool charging = instance.isCharging();
|
||||
int level = instance.getBatteryLevel();
|
||||
if (level >= 0) {
|
||||
if (level >= 0)
|
||||
{
|
||||
// Select appropriate battery symbol based on level
|
||||
const char* battery_symbol;
|
||||
if (charging) {
|
||||
if (charging)
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_CHARGE;
|
||||
} else if (level >= 90) {
|
||||
}
|
||||
else if (level >= 90)
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_BATTERY_FULL;
|
||||
} else if (level >= 60) {
|
||||
}
|
||||
else if (level >= 60)
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_BATTERY_3;
|
||||
} else if (level >= 30) {
|
||||
}
|
||||
else if (level >= 30)
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_BATTERY_2;
|
||||
} else if (level >= 10) {
|
||||
}
|
||||
else if (level >= 10)
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_BATTERY_1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
battery_symbol = LV_SYMBOL_BATTERY_EMPTY;
|
||||
}
|
||||
snprintf(battery_str, sizeof(battery_str), "%s %d%%", battery_symbol, level);
|
||||
@@ -720,45 +794,52 @@ void setup()
|
||||
}
|
||||
|
||||
instance.setBrightness(DEVICE_MAX_BRIGHTNESS_LEVEL);
|
||||
|
||||
|
||||
// GPS data collection task is now created in TLoRaPagerBoard::begin()
|
||||
|
||||
|
||||
// Create activity mutex for screen sleep management
|
||||
activity_mutex = xSemaphoreCreateMutex();
|
||||
if (activity_mutex == NULL) {
|
||||
if (activity_mutex == NULL)
|
||||
{
|
||||
log_e("Failed to create activity mutex");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize activity time
|
||||
last_user_activity_time = millis();
|
||||
|
||||
|
||||
// Load screen sleep timeout from preferences
|
||||
preferences.begin("settings", true);
|
||||
screen_sleep_timeout_ms = preferences.getUInt("sleep_timeout", 30000); // Default 30 seconds
|
||||
screen_sleep_timeout_ms = preferences.getUInt("sleep_timeout", 30000); // Default 30 seconds
|
||||
preferences.end();
|
||||
|
||||
|
||||
// Ensure minimum timeout
|
||||
if (screen_sleep_timeout_ms < 10000) {
|
||||
if (screen_sleep_timeout_ms < 10000)
|
||||
{
|
||||
screen_sleep_timeout_ms = 30000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create screen sleep management task
|
||||
BaseType_t sleep_task_result = xTaskCreate(
|
||||
screenSleepTask,
|
||||
"screen_sleep",
|
||||
2 * 1024, // Stack size
|
||||
2 * 1024, // Stack size
|
||||
NULL,
|
||||
3, // Priority (lower than GPS task)
|
||||
&screen_sleep_task_handle
|
||||
);
|
||||
if (sleep_task_result != pdPASS) {
|
||||
3, // Priority (lower than GPS task)
|
||||
&screen_sleep_task_handle);
|
||||
if (sleep_task_result != pdPASS)
|
||||
{
|
||||
log_e("Failed to create screen sleep task");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
log_d("Screen sleep management task created successfully");
|
||||
}
|
||||
|
||||
// If waking from deep sleep, update user activity to prevent immediate screen sleep
|
||||
if (waking_from_sleep) {
|
||||
if (waking_from_sleep)
|
||||
{
|
||||
updateUserActivity();
|
||||
log_d("Updated user activity after waking from sleep");
|
||||
}
|
||||
@@ -774,7 +855,8 @@ void loop()
|
||||
#ifdef ARDUINO_USB_MODE
|
||||
// If USB mode is active, run USB loop (like Launcher's loop() function)
|
||||
// This ensures USB tasks get CPU time and prevents other tasks from interfering
|
||||
if (ui_usb_is_active()) {
|
||||
if (ui_usb_is_active())
|
||||
{
|
||||
// Process LVGL for USB mode
|
||||
lv_timer_handler();
|
||||
|
||||
@@ -800,17 +882,19 @@ void loop()
|
||||
static uint32_t last_loop_ms = 0;
|
||||
static uint32_t loop_count = 0;
|
||||
uint32_t now_ms = millis();
|
||||
|
||||
|
||||
// Record loop() call interval
|
||||
if (last_loop_ms > 0) {
|
||||
if (last_loop_ms > 0)
|
||||
{
|
||||
uint32_t interval = now_ms - last_loop_ms;
|
||||
if (interval > 50) { // Only log intervals > 50ms (indicating delay)
|
||||
if (interval > 50)
|
||||
{ // Only log intervals > 50ms (indicating delay)
|
||||
Serial.printf("[MAIN] loop() interval: %lu ms (count=%lu)\n", interval, loop_count);
|
||||
}
|
||||
}
|
||||
last_loop_ms = now_ms;
|
||||
loop_count++;
|
||||
|
||||
|
||||
uint32_t t_before = millis();
|
||||
#endif
|
||||
|
||||
@@ -820,12 +904,13 @@ void loop()
|
||||
#if MAIN_TIMING_DEBUG
|
||||
uint32_t t_after = millis();
|
||||
uint32_t handler_duration = t_after - t_before;
|
||||
|
||||
|
||||
// Log lv_timer_handler() execution time
|
||||
if (handler_duration > 10) {
|
||||
if (handler_duration > 10)
|
||||
{
|
||||
Serial.printf("[MAIN] lv_timer_handler() took %lu ms\n", handler_duration);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
delay(2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user