Files
StackChan/remote/code/main/ui/ui_running_screen.c
T
2026-04-20 16:27:57 +08:00

188 lines
7.4 KiB
C

#include "ui_running_screen.h"
#include "../lvgl_port.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
lv_obj_t* running_screen = NULL;
lv_obj_t* joystick_dot = NULL;
lv_obj_t* joystick_area = NULL;
lv_obj_t* battery_label = NULL;
lv_obj_t* channel_info_label = NULL;
lv_obj_t* id_info_label = NULL;
/**
* @brief Create the running screen UI with joystick visualization and status information
* @note This function creates a standalone screen with multiple UI elements:
* - Title label at the top
* - Joystick visualization area with crosshair
* - Red dot representing joystick position
* - Battery level display
* - Channel information display
* - Device ID information display
* @details The function creates a 115x115 pixel joystick area with crosshair lines
* and a red circular dot that represents the current joystick position
* @warning This function should only be called once per application run to avoid memory leaks
*/
void create_running_screen()
{
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
lv_disp_t* disp = lv_disp_get_default();
if (disp == NULL) {
ESP_LOGE("UI", "No default display found!");
lvgl_port_unlock();
return;
}
if (running_screen == NULL) {
running_screen = lv_obj_create(NULL);
}
if (running_screen == NULL) {
ESP_LOGE("UI", "Failed to create running screen!");
return;
}
lv_obj_clear_flag(running_screen, LV_OBJ_FLAG_SCROLLABLE);
// Create title
lv_obj_t* label = lv_label_create(running_screen);
lv_label_set_text(label, "StackChan :)");
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_set_style_text_font(label, &lv_font_montserrat_14, 0);
// Create joystick area
joystick_area = lv_obj_create(running_screen);
lv_obj_set_size(joystick_area, 115, 115); // Reduced size
lv_obj_align(joystick_area, LV_ALIGN_TOP_MID, 0, 35);
// Set joystick area style
lv_obj_set_style_border_width(joystick_area, 2, LV_PART_MAIN);
lv_obj_set_style_border_color(joystick_area, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_color(joystick_area, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_pad_all(joystick_area, 0, LV_PART_MAIN);
lv_obj_clear_flag(joystick_area, LV_OBJ_FLAG_SCROLLABLE);
// Add horizontal crosshair line
lv_obj_t* cross_line_h = lv_line_create(joystick_area);
static lv_point_t points_h[2] = {{0, 57}, {115, 57}}; // Horizontal center line
lv_line_set_points(cross_line_h, points_h, 2);
lv_obj_set_style_line_color(cross_line_h, lv_color_make(64, 64, 64), 0);
lv_obj_set_style_line_width(cross_line_h, 1, 0);
// Add vertical crosshair line
lv_obj_t* cross_line_v = lv_line_create(joystick_area);
static lv_point_t points_v[2] = {{56, 0}, {56, 115}}; // Vertical center line
lv_line_set_points(cross_line_v, points_v, 2);
lv_obj_set_style_line_color(cross_line_v, lv_color_make(64, 64, 64), 0);
lv_obj_set_style_line_width(cross_line_v, 1, 0);
// Create joystick dot
joystick_dot = lv_obj_create(joystick_area);
lv_obj_set_size(joystick_dot, 10, 10); // Dot size
lv_obj_set_style_radius(joystick_dot, LV_RADIUS_CIRCLE, LV_PART_MAIN); // Set to circle
lv_obj_set_style_bg_color(joystick_dot, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN);
lv_obj_set_style_border_width(joystick_dot, 0, LV_PART_MAIN);
lv_obj_align(joystick_dot, LV_ALIGN_CENTER, 0, 0);
// Create battery level display
battery_label = lv_label_create(running_screen);
lv_label_set_text(battery_label, "Bat: 100%%");
lv_obj_align(battery_label, LV_ALIGN_TOP_LEFT, 10, 160);
lv_obj_set_style_text_font(battery_label, &lv_font_montserrat_14, 0);
// Create Channel information display
channel_info_label = lv_label_create(running_screen);
lv_label_set_text(channel_info_label, "Channel: 1");
lv_obj_align(channel_info_label, LV_ALIGN_TOP_LEFT, 10, 180);
lv_obj_set_style_text_font(channel_info_label, &lv_font_montserrat_14, 0);
// Create ID information display
id_info_label = lv_label_create(running_screen);
lv_label_set_text(id_info_label, "Receiver ID: \n0(broadcast)");
lv_obj_align(id_info_label, LV_ALIGN_TOP_LEFT, 10, 200);
lv_obj_set_style_text_font(id_info_label, &lv_font_montserrat_14, 0);
lvgl_port_unlock();
}
/**
* @brief Update the running screen UI with current joystick values and status information
* @param joyX X-axis value from joystick (raw value to be mapped to screen coordinates)
* @param joyY Y-axis value from joystick (raw value to be mapped to screen coordinates)
* @param channel Current WiFi channel being used
* @param id Device ID for communication
* @param bat Battery level percentage (0-100)
* @note This function maps joystick values to the 115x115 pixel joystick area
* and applies deadzone correction to center the dot when joystick is near center position
* @details
* 1. Maps raw joystick values to screen coordinates within the joystick area
* 2. Applies deadzone correction to keep dot centered when joystick is in neutral position
* 3. Clamps values to prevent the dot from going outside the joystick area
* 4. Updates the position of the joystick dot
* 5. Updates battery level, channel and ID information labels
*/
void update_running_screen(int16_t joyX, int16_t joyY, uint8_t channel, uint8_t id, uint8_t bat)
{
// Map joystick values to 115x115 area (using your mapping approach)
int16_t x_pos = map(joyX, X_MIN, X_MAX, 5, 110); // Leave 5px margin
int16_t y_pos = map(joyY, Y_MIN, Y_MAX, 110, 5); // Y-axis inverted
// Apply deadzone
int16_t x_center = map(X_CENTER, X_MIN, X_MAX, 5, 110);
int16_t y_center = map(Y_CENTER, Y_MIN, Y_MAX, 110, 5);
if (abs(joyX - X_CENTER) < DEAD_ZONE) {
x_pos = x_center;
}
if (abs(joyY - Y_CENTER) < DEAD_ZONE) {
y_pos = y_center;
}
// Limit range
x_pos = fmax(5, fmin(x_pos, 110));
y_pos = fmax(5, fmin(y_pos, 110));
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
// Update joystick dot position (relative to joystick_area center)
lv_obj_align(joystick_dot, LV_ALIGN_TOP_LEFT, x_pos - 5, y_pos - 5);
// Update battery level
lv_label_set_text_fmt(battery_label, "Bat: %d%%", bat);
// Update Channel and ID display
lv_label_set_text_fmt(channel_info_label, "Channel: %u", channel);
if (id == 0) {
lv_label_set_text(id_info_label, "Receiver ID:\n 0(broadcast)");
} else {
lv_label_set_text_fmt(id_info_label, "Receiver ID: %u", id);
}
lvgl_port_unlock();
}
/**
* @brief Reset all UI object pointers to NULL to prepare for screen destruction
* @note This function does not actually destroy the UI objects, but resets the pointers
* that reference them, allowing the UI to be recreated or switched
* @warning The actual UI objects should be destroyed separately using LVGL's object destruction functions
*/
void ui_running_screen_destory()
{
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
if (running_screen != NULL) {
lv_obj_del(running_screen);
running_screen = NULL;
}
lvgl_port_unlock();
joystick_dot = NULL;
joystick_area = NULL;
battery_label = NULL;
channel_info_label = NULL;
id_info_label = NULL;
}