2845 lines
108 KiB
C
2845 lines
108 KiB
C
/* AUTO-GENERATED by GUI Editor — do not edit manually */
|
||
#include "gui_generated.h"
|
||
#include "lvgl.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <math.h>
|
||
|
||
/* Fonts */
|
||
LV_FONT_DECLARE(montserrat_16_ru_en)
|
||
|
||
/* Images */
|
||
LV_IMG_DECLARE(img_valve)
|
||
LV_IMG_DECLARE(img_fan)
|
||
LV_IMG_DECLARE(img_channel)
|
||
LV_IMG_DECLARE(img_heater)
|
||
LV_IMG_DECLARE(img_cooler)
|
||
LV_IMG_DECLARE(img_pipe)
|
||
LV_IMG_DECLARE(img_electric_heater)
|
||
LV_IMG_DECLARE(img_filter_g4)
|
||
LV_IMG_DECLARE(img_filter_h13)
|
||
|
||
/* Color scheme - minimalistic dark theme */
|
||
#define COLOR_BG_DARK 0x0F1419 /* Очень темный фон */
|
||
#define COLOR_BG_PANEL 0x1A1F26 /* Фон панелей */
|
||
#define COLOR_BG_MODULE 0x252B33 /* Фон модулей */
|
||
#define COLOR_BG_BTN 0x2D333B /* Фон кнопок */
|
||
#define COLOR_BORDER 0x3D444D /* Границы */
|
||
#define COLOR_TEXT 0xD0D4D8 /* Светлый текст */
|
||
#define COLOR_TEXT_DIM 0x6E7681 /* Приглушенный текст */
|
||
#define COLOR_TEXT_ACCENT 0x58A6FF /* Акцентный текст */
|
||
#define COLOR_ACCENT 0x3FB950 /* Зеленый для активных */
|
||
#define COLOR_WARNING 0xD29922 /* Желтый для предупреждений */
|
||
#define COLOR_DANGER 0xF85149 /* Красный для аварий */
|
||
|
||
lv_obj_t *scr_main = NULL;
|
||
lv_obj_t *scr_screen1 = NULL; /* Меню */
|
||
lv_obj_t *scr_screen2 = NULL; /* Журнал */
|
||
lv_obj_t *scr_screen3 = NULL; /* Уставка параметров */
|
||
lv_obj_t *scr_screen4 = NULL; /* Настройки (после пароля) */
|
||
|
||
/* System state structure (like ModBus data) */
|
||
typedef struct {
|
||
int16_t temp_room; /* Температура комнаты x10 */
|
||
int16_t hum_room; /* Влажность комнаты x10 */
|
||
int16_t temp_channel; /* Температура канала x10 */
|
||
int16_t hum_channel; /* Влажность канала x10 */
|
||
uint16_t room_num; /* Номер комнаты */
|
||
uint8_t is_master; /* 1=Мастер, 0=Слейв */
|
||
uint8_t has_signal; /* Есть ли сигнал при слейве */
|
||
uint8_t season; /* 0=Зима, 1=Лето, 2=Переход */
|
||
uint8_t mode; /* 0=Остановка, 1=Дежурный, 2=Рабочий */
|
||
uint8_t valve_inlet; /* Входной клапан 0-100% */
|
||
uint8_t valve_recirc; /* Клапан рециркуляции 0-100% */
|
||
uint8_t filter_g4_clean; /* 0=Грязный, 1=Чистый */
|
||
uint8_t kws_valve; /* KWS клапан 0-100% */
|
||
uint8_t kws_mode; /* 0=Охлаждение, 1=Нагрев */
|
||
uint8_t pww_valve; /* PWW клапан 0-100% */
|
||
uint8_t pww_mode; /* 0=Охлаждение, 1=Нагрев */
|
||
uint8_t heater_on; /* Электронагреватель 0=Выкл, 1=Вкл */
|
||
uint8_t fan_on; /* Вентилятор 0=Выкл, 1=Вкл */
|
||
uint8_t filter_h13_clean; /* 0=Грязный, 1=Чистый */
|
||
uint16_t year;
|
||
uint8_t month;
|
||
uint8_t day;
|
||
uint8_t hour;
|
||
uint8_t minute;
|
||
} SysState_t;
|
||
|
||
static SysState_t state = {
|
||
.temp_room = 220,
|
||
.hum_room = 450,
|
||
.temp_channel = 200,
|
||
.hum_channel = 500,
|
||
.room_num = 101,
|
||
.is_master = 1,
|
||
.has_signal = 1,
|
||
.season = 0,
|
||
.mode = 1,
|
||
.valve_inlet = 75,
|
||
.valve_recirc = 25,
|
||
.filter_g4_clean = 0, /* Фильтр загрязнен для демонстрации */
|
||
.kws_valve = 50,
|
||
.kws_mode = 1,
|
||
.pww_valve = 30,
|
||
.pww_mode = 0, /* PWW в режиме охлаждения для демонстрации */
|
||
.heater_on = 0,
|
||
.fan_on = 1,
|
||
.filter_h13_clean = 1,
|
||
.year = 2026,
|
||
.month = 2,
|
||
.day = 26,
|
||
.hour = 12,
|
||
.minute = 30
|
||
};
|
||
|
||
/* Module configuration for drag&drop */
|
||
typedef enum {
|
||
MODULE_VALVE_INLET = 0,
|
||
MODULE_VALVE_RECIRC,
|
||
MODULE_FILTER_G4,
|
||
MODULE_KWS,
|
||
MODULE_PWW,
|
||
MODULE_HEATER,
|
||
MODULE_FAN,
|
||
MODULE_FILTER_H13,
|
||
MODULE_CHANNEL,
|
||
MODULE_COUNT
|
||
} module_type_t;
|
||
|
||
typedef struct {
|
||
module_type_t type;
|
||
int enabled; /* 1=активен (на линии), 0=неактивен (внизу) */
|
||
int position; /* позиция в ряду (0-7) */
|
||
lv_obj_t *obj; /* указатель на объект */
|
||
lv_obj_t *img; /* указатель на изображение */
|
||
lv_obj_t *label_name; /* название модуля */
|
||
lv_obj_t *label_value; /* указатель на значение */
|
||
lv_obj_t *pipe_placeholder; /* труба-заглушка когда модуль отключен */
|
||
} module_config_t;
|
||
|
||
static module_config_t modules[MODULE_COUNT] = {
|
||
{MODULE_VALVE_INLET, 1, 0, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_VALVE_RECIRC, 1, 1, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_FILTER_G4, 1, 2, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_KWS, 1, 3, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_PWW, 1, 4, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_HEATER, 1, 5, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_FAN, 1, 6, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_FILTER_H13, 1, 7, NULL, NULL, NULL, NULL, NULL},
|
||
{MODULE_CHANNEL, 1, 8, NULL, NULL, NULL, NULL, NULL},
|
||
};
|
||
|
||
/* Module dimensions and positions */
|
||
#define MODULE_WIDTH 70
|
||
#define MODULE_HEIGHT 95
|
||
#define MODULE_SPACING 3
|
||
#define MODULES_ACTIVE_Y 20
|
||
#define MODULES_INACTIVE_Y 175
|
||
#define MODULE_SCALE 140 /* Scale factor: 256=100%, 140~55% for 64x64 images */
|
||
|
||
/* Panel for schema (needed globally for drag) */
|
||
static lv_obj_t *schema_panel = NULL;
|
||
|
||
/* Pipe placeholders for inactive modules in active line */
|
||
static lv_obj_t *pipe_placeholders[MODULE_COUNT] = {NULL};
|
||
|
||
/* Drag state for modules */
|
||
static lv_obj_t *dragged_module = NULL;
|
||
static int dragged_module_idx = -1;
|
||
static lv_point_t drag_start_pos;
|
||
|
||
/* Alarm log structure */
|
||
#define MAX_ALARMS 20
|
||
typedef struct {
|
||
char message[80];
|
||
char timestamp[20];
|
||
int active;
|
||
} alarm_entry_t;
|
||
|
||
static alarm_entry_t alarm_log[MAX_ALARMS];
|
||
static int alarm_count = 0;
|
||
|
||
/* Settings/Setpoints for Work and Standby modes */
|
||
typedef struct {
|
||
float temp; /* Температура */
|
||
float hum; /* Влажность */
|
||
float valve_inlet; /* Клапан притока */
|
||
float valve_recirc; /* Клапан рециркуляции */
|
||
uint8_t heater; /* 0=ВЫКЛ, 1=ВКЛ, 2=АВТО */
|
||
uint8_t fan; /* 0=ВЫКЛ, 1=ВКЛ */
|
||
uint8_t kws_auto; /* 1=АВТО, 0=ручной режим */
|
||
float kws_manual; /* Значение для ручного режима */
|
||
uint8_t pww_auto; /* 1=АВТО, 0=ручной режим */
|
||
float pww_manual; /* Значение для ручного режима */
|
||
} setpoint_mode_t;
|
||
|
||
static setpoint_mode_t setpoint_work = {
|
||
.temp = 22.0f, .hum = 50.0f, .valve_inlet = 75.0f, .valve_recirc = 25.0f,
|
||
.heater = 2, .fan = 1, .kws_auto = 1, .kws_manual = 0, .pww_auto = 1, .pww_manual = 0
|
||
};
|
||
|
||
static setpoint_mode_t setpoint_standby = {
|
||
.temp = 18.0f, .hum = 45.0f, .valve_inlet = 35.0f, .valve_recirc = 40.0f,
|
||
.heater = 0, .fan = 1, .kws_auto = 1, .kws_manual = 0, .pww_auto = 1, .pww_manual = 0
|
||
};
|
||
|
||
/* Configuration parameters (Настройки - с паролем) */
|
||
typedef struct {
|
||
float temp_room_offset;
|
||
float hum_room_offset;
|
||
float temp_channel_offset;
|
||
float hum_channel_offset;
|
||
float valve_inlet_offset;
|
||
float valve_recirc_offset;
|
||
uint8_t has_recirc_valve;
|
||
uint8_t has_heater;
|
||
float pid_pww_kp;
|
||
float pid_pww_ti;
|
||
float pid_pww_d;
|
||
float pid_kws_kp;
|
||
float pid_kws_ti;
|
||
float pid_kws_d;
|
||
uint8_t force_mode_enabled;
|
||
uint8_t force_mode_value;
|
||
uint8_t key_swap_enabled;
|
||
} config_t;
|
||
|
||
static config_t config = {
|
||
.temp_room_offset = 0.0f,
|
||
.hum_room_offset = 0.0f,
|
||
.temp_channel_offset = 0.0f,
|
||
.hum_channel_offset = 0.0f,
|
||
.valve_inlet_offset = 0.0f,
|
||
.valve_recirc_offset = 0.0f,
|
||
.has_recirc_valve = 1,
|
||
.has_heater = 1,
|
||
.pid_pww_kp = 1.0f,
|
||
.pid_pww_ti = 10.0f,
|
||
.pid_pww_d = 0.0f,
|
||
.pid_kws_kp = 1.0f,
|
||
.pid_kws_ti = 10.0f,
|
||
.pid_kws_d = 0.0f,
|
||
.force_mode_enabled = 0,
|
||
.force_mode_value = 1,
|
||
.key_swap_enabled = 1
|
||
};
|
||
|
||
/* Filter replacement dates */
|
||
static char filter_g4_date[12] = "01.01.2026";
|
||
static char filter_h13_date[12] = "01.01.2026";
|
||
|
||
/* Password and settings */
|
||
#define PASSWORD "1234"
|
||
static char password_input[10] = "";
|
||
static lv_obj_t *popup_password = NULL;
|
||
static lv_obj_t *lbl_password_display = NULL;
|
||
static int password_target = 0; /* 0=настройки, 1=очистка журнала фильтров */
|
||
|
||
/* Parameter editor */
|
||
static lv_obj_t *popup_param_editor = NULL;
|
||
static lv_obj_t *lbl_param_value = NULL;
|
||
static char param_value_str[20] = "";
|
||
static float *param_being_edited = NULL;
|
||
static char param_name[50] = "";
|
||
static int param_is_percentage = 0;
|
||
|
||
/* Configuration parameters */
|
||
/* (moved to config_t structure above) */
|
||
|
||
/* UI element references for updates */
|
||
static lv_obj_t *lbl_temp_room = NULL;
|
||
static lv_obj_t *lbl_hum_room = NULL;
|
||
static lv_obj_t *lbl_temp_channel = NULL;
|
||
static lv_obj_t *lbl_hum_channel = NULL;
|
||
static lv_obj_t *lbl_room_num = NULL;
|
||
static lv_obj_t *lbl_master_slave = NULL;
|
||
static lv_obj_t *lbl_season = NULL;
|
||
static lv_obj_t *lbl_mode = NULL;
|
||
static lv_obj_t *lbl_datetime = NULL;
|
||
|
||
/* Equipment UI references */
|
||
static lv_obj_t *panel_schema = NULL;
|
||
|
||
/* Popup windows */
|
||
static lv_obj_t *popup_readings = NULL;
|
||
static lv_obj_t *popup_alarm = NULL;
|
||
|
||
/* Setpoints screen UI */
|
||
static lv_obj_t *lbl_work_temp = NULL;
|
||
static lv_obj_t *lbl_work_hum = NULL;
|
||
static lv_obj_t *lbl_standby_temp = NULL;
|
||
static lv_obj_t *lbl_standby_hum = NULL;
|
||
|
||
/* Forward declarations */
|
||
static void create_popup_readings(void);
|
||
static void create_popup_alarm(const char *message);
|
||
static void add_alarm_to_log(const char *message, int active);
|
||
static void btn_log_cb(lv_event_t *e);
|
||
static void btn_settings_cb(lv_event_t *e);
|
||
static void btn_config_cb(lv_event_t *e);
|
||
static void create_popup_password(int target);
|
||
static void password_keypad_cb(lv_event_t *e);
|
||
static void create_popup_param_editor(const char *name, float *param_ptr, int is_percentage);
|
||
static void param_keypad_cb(lv_event_t *e);
|
||
static void close_popup_cb(lv_event_t *e);
|
||
static void btn_back_to_main_cb(lv_event_t *e);
|
||
static void create_screen_config(void);
|
||
static void create_screen_screen2(void);
|
||
static void module_drag_cb(lv_event_t *e);
|
||
static void update_module_display(int module_idx);
|
||
static void refresh_setpoints_display(void);
|
||
static void btn_reset_alarm_cb(lv_event_t *e);
|
||
static void btn_clear_log_cb(lv_event_t *e);
|
||
static lv_obj_t* create_module_widget(lv_obj_t *parent, int module_idx, int x, int y);
|
||
static void update_module_positions(void);
|
||
|
||
/* Get image for module type */
|
||
static const lv_image_dsc_t* get_module_image(int module_idx) {
|
||
switch (module_idx) {
|
||
case MODULE_VALVE_INLET:
|
||
case MODULE_VALVE_RECIRC:
|
||
return &img_valve;
|
||
case MODULE_FILTER_G4:
|
||
return &img_filter_g4;
|
||
case MODULE_KWS:
|
||
return state.kws_mode ? &img_heater : &img_cooler;
|
||
case MODULE_PWW:
|
||
return state.pww_mode ? &img_heater : &img_cooler;
|
||
case MODULE_HEATER:
|
||
return &img_electric_heater;
|
||
case MODULE_FAN:
|
||
return &img_fan;
|
||
case MODULE_FILTER_H13:
|
||
return &img_filter_h13;
|
||
case MODULE_CHANNEL:
|
||
return &img_channel;
|
||
default:
|
||
return &img_pipe;
|
||
}
|
||
}
|
||
|
||
/* Get name for module type */
|
||
static const char* get_module_name(int module_idx) {
|
||
switch (module_idx) {
|
||
case MODULE_VALVE_INLET:
|
||
return "Вход";
|
||
case MODULE_VALVE_RECIRC:
|
||
return "Рецирк";
|
||
case MODULE_FILTER_G4:
|
||
return "G4";
|
||
case MODULE_KWS:
|
||
return "KWS";
|
||
case MODULE_PWW:
|
||
return "PWW";
|
||
case MODULE_HEATER:
|
||
return "Нагрев";
|
||
case MODULE_FAN:
|
||
return "Вент";
|
||
case MODULE_FILTER_H13:
|
||
return "H13";
|
||
case MODULE_CHANNEL:
|
||
return "Канал";
|
||
default:
|
||
return "";
|
||
}
|
||
}
|
||
|
||
/* Module drag event handler */
|
||
static void module_drag_cb(lv_event_t *e) {
|
||
lv_event_code_t code = lv_event_get_code(e);
|
||
lv_obj_t *obj = lv_event_get_target(e);
|
||
int module_idx = (int)(intptr_t)lv_event_get_user_data(e);
|
||
|
||
if (code == LV_EVENT_PRESSING) {
|
||
lv_indev_t *indev = lv_indev_active();
|
||
if (indev == NULL) return;
|
||
|
||
lv_point_t vect;
|
||
lv_indev_get_vect(indev, &vect);
|
||
|
||
lv_coord_t x = lv_obj_get_x(obj) + vect.x;
|
||
lv_coord_t y = lv_obj_get_y(obj) + vect.y;
|
||
lv_obj_set_pos(obj, x, y);
|
||
}
|
||
else if (code == LV_EVENT_RELEASED) {
|
||
/* Check if module should be enabled/disabled based on Y position */
|
||
lv_coord_t y = lv_obj_get_y(obj);
|
||
int threshold = (MODULES_ACTIVE_Y + MODULES_INACTIVE_Y) / 2;
|
||
|
||
if (y < threshold) {
|
||
modules[module_idx].enabled = 1;
|
||
} else {
|
||
modules[module_idx].enabled = 0;
|
||
}
|
||
|
||
/* Reposition all modules */
|
||
update_module_positions();
|
||
}
|
||
}
|
||
|
||
/* Update positions of all modules (pipe line is continuous background) */
|
||
static void update_module_positions(void) {
|
||
int active_x = 50; /* Start after the inlet pipe */
|
||
int inactive_x = 10;
|
||
|
||
for (int i = 0; i < MODULE_COUNT; i++) {
|
||
if (modules[i].obj == NULL) continue;
|
||
|
||
if (modules[i].enabled) {
|
||
/* Module is active - position it on the active line */
|
||
lv_obj_set_pos(modules[i].obj, active_x, MODULES_ACTIVE_Y);
|
||
active_x += MODULE_WIDTH + MODULE_SPACING;
|
||
} else {
|
||
/* Module is inactive - move to inactive area, pipe line shows through */
|
||
active_x += MODULE_WIDTH + MODULE_SPACING; /* Reserve space on pipe line */
|
||
|
||
lv_obj_set_pos(modules[i].obj, inactive_x, MODULES_INACTIVE_Y);
|
||
inactive_x += MODULE_WIDTH + MODULE_SPACING;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Create a module widget with image */
|
||
static lv_obj_t* create_module_widget(lv_obj_t *parent, int module_idx, int x, int y) {
|
||
/* Container for module */
|
||
lv_obj_t *cont = lv_obj_create(parent);
|
||
lv_obj_set_pos(cont, x, y);
|
||
lv_obj_set_size(cont, MODULE_WIDTH, MODULE_HEIGHT);
|
||
lv_obj_set_style_bg_color(cont, lv_color_hex(COLOR_BG_MODULE), 0);
|
||
lv_obj_set_style_radius(cont, 5, 0);
|
||
lv_obj_set_style_border_width(cont, 1, 0);
|
||
lv_obj_set_style_border_color(cont, lv_color_hex(COLOR_BORDER), 0);
|
||
lv_obj_set_style_pad_all(cont, 2, 0);
|
||
lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Add drag flag */
|
||
lv_obj_add_flag(cont, LV_OBJ_FLAG_CLICKABLE);
|
||
lv_obj_add_event_cb(cont, module_drag_cb, LV_EVENT_PRESSING, (void*)(intptr_t)module_idx);
|
||
lv_obj_add_event_cb(cont, module_drag_cb, LV_EVENT_RELEASED, (void*)(intptr_t)module_idx);
|
||
|
||
/* Module name label at top */
|
||
lv_obj_t *lbl_name = lv_label_create(cont);
|
||
lv_label_set_text(lbl_name, get_module_name(module_idx));
|
||
lv_obj_align(lbl_name, LV_ALIGN_TOP_MID, 0, 0);
|
||
lv_obj_set_style_text_color(lbl_name, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl_name, &montserrat_16_ru_en, 0);
|
||
|
||
/* Image */
|
||
lv_obj_t *img = lv_image_create(cont);
|
||
lv_image_set_src(img, get_module_image(module_idx));
|
||
lv_image_set_scale(img, MODULE_SCALE);
|
||
lv_obj_align(img, LV_ALIGN_CENTER, 0, 5);
|
||
|
||
/* Value label at bottom */
|
||
lv_obj_t *lbl_value = lv_label_create(cont);
|
||
lv_obj_align(lbl_value, LV_ALIGN_BOTTOM_MID, 0, -2);
|
||
lv_obj_set_style_text_color(lbl_value, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_value, &montserrat_16_ru_en, 0);
|
||
|
||
modules[module_idx].obj = cont;
|
||
modules[module_idx].img = img;
|
||
modules[module_idx].label_name = lbl_name;
|
||
modules[module_idx].label_value = lbl_value;
|
||
|
||
return cont;
|
||
}
|
||
|
||
/* Apply setpoints to system state based on current mode */
|
||
static void apply_mode_setpoints(void) {
|
||
setpoint_mode_t *sp = (state.mode == 2) ? &setpoint_work : &setpoint_standby;
|
||
|
||
if (state.mode == 0) {
|
||
/* Остановка - выключаем только активные элементы (вентилятор, нагреватель) */
|
||
/* Клапаны остаются на своих позициях */
|
||
state.heater_on = 0;
|
||
state.fan_on = 0;
|
||
} else {
|
||
/* Дежурный или Рабочий режим - применяем уставки */
|
||
state.valve_inlet = (uint8_t)sp->valve_inlet;
|
||
state.valve_recirc = (uint8_t)sp->valve_recirc;
|
||
|
||
/* Вентилятор по уставке */
|
||
state.fan_on = sp->fan;
|
||
|
||
/* Электронагреватель по уставке */
|
||
if (sp->heater == 0) {
|
||
state.heater_on = 0; /* ВЫКЛ */
|
||
} else if (sp->heater == 1) {
|
||
state.heater_on = 1; /* ВКЛ */
|
||
}
|
||
/* heater == 2 (АВТО) - управляется автоматикой */
|
||
|
||
/* KWS и PWW */
|
||
if (!sp->kws_auto) {
|
||
state.kws_valve = (uint8_t)sp->kws_manual;
|
||
}
|
||
if (!sp->pww_auto) {
|
||
state.pww_valve = (uint8_t)sp->pww_manual;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Button callbacks */
|
||
static void btn_stop_cb(lv_event_t *e) {
|
||
(void)e;
|
||
state.mode = 0;
|
||
apply_mode_setpoints();
|
||
gui_update_values();
|
||
}
|
||
|
||
static void btn_standby_cb(lv_event_t *e) {
|
||
(void)e;
|
||
state.mode = 1;
|
||
apply_mode_setpoints();
|
||
gui_update_values();
|
||
}
|
||
|
||
static void btn_work_cb(lv_event_t *e) {
|
||
(void)e;
|
||
state.mode = 2;
|
||
apply_mode_setpoints();
|
||
gui_update_values();
|
||
}
|
||
|
||
static void btn_menu_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (scr_screen1) lv_scr_load(scr_screen1);
|
||
}
|
||
|
||
static void btn_back_to_main_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (scr_main) lv_scr_load(scr_main);
|
||
}
|
||
|
||
static void btn_readings_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_readings();
|
||
}
|
||
|
||
static void btn_reset_alarm_cb(lv_event_t *e) {
|
||
(void)e;
|
||
/* Reset first active alarm */
|
||
for (int i = 0; i < alarm_count; i++) {
|
||
if (alarm_log[i].active) {
|
||
alarm_log[i].active = 0;
|
||
break;
|
||
}
|
||
}
|
||
/* Refresh journal screen */
|
||
if (scr_screen2) {
|
||
lv_obj_del(scr_screen2);
|
||
scr_screen2 = NULL;
|
||
}
|
||
create_screen_screen2();
|
||
lv_scr_load(scr_screen2);
|
||
}
|
||
|
||
static void btn_clear_log_cb(lv_event_t *e) {
|
||
(void)e;
|
||
/* Check if any active alarms */
|
||
int has_active = 0;
|
||
for (int i = 0; i < alarm_count; i++) {
|
||
if (alarm_log[i].active) {
|
||
has_active = 1;
|
||
break;
|
||
}
|
||
}
|
||
if (!has_active) {
|
||
alarm_count = 0;
|
||
/* Refresh journal screen */
|
||
if (scr_screen2) {
|
||
lv_obj_del(scr_screen2);
|
||
scr_screen2 = NULL;
|
||
}
|
||
create_screen_screen2();
|
||
lv_scr_load(scr_screen2);
|
||
}
|
||
}
|
||
|
||
static void close_popup_cb(lv_event_t *e) {
|
||
lv_obj_t *popup = lv_event_get_user_data(e);
|
||
if (popup) {
|
||
lv_obj_del(popup);
|
||
}
|
||
if (popup == popup_readings) popup_readings = NULL;
|
||
if (popup == popup_alarm) popup_alarm = NULL;
|
||
if (popup == popup_password) {
|
||
popup_password = NULL;
|
||
memset(password_input, 0, sizeof(password_input));
|
||
}
|
||
if (popup == popup_param_editor) {
|
||
popup_param_editor = NULL;
|
||
memset(param_value_str, 0, sizeof(param_value_str));
|
||
param_being_edited = NULL;
|
||
}
|
||
}
|
||
|
||
static void btn_log_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (scr_screen2) lv_scr_load(scr_screen2);
|
||
}
|
||
|
||
static void btn_settings_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (scr_screen3) lv_scr_load(scr_screen3);
|
||
}
|
||
|
||
static void btn_config_cb(lv_event_t *e) {
|
||
(void)e;
|
||
password_target = 0;
|
||
create_popup_password(0);
|
||
}
|
||
|
||
/* Edit parameter callbacks - Work mode */
|
||
static void edit_work_temp_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Темп.", &setpoint_work.temp, 0);
|
||
}
|
||
|
||
static void edit_work_hum_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Влажность (Рабочий)", &setpoint_work.hum, 1);
|
||
}
|
||
|
||
static void edit_work_valve_inlet_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Клапан притока (Рабочий)", &setpoint_work.valve_inlet, 1);
|
||
}
|
||
|
||
static void edit_work_valve_recirc_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Клапан рециркуляции (Рабочий)", &setpoint_work.valve_recirc, 1);
|
||
}
|
||
|
||
/* Edit parameter callbacks - Standby mode */
|
||
static void edit_standby_temp_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Температура (Дежурный)", &setpoint_standby.temp, 0);
|
||
}
|
||
|
||
static void edit_standby_hum_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Влажность (Дежурный)", &setpoint_standby.hum, 1);
|
||
}
|
||
|
||
static void edit_standby_valve_inlet_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Клапан притока (Дежурный)", &setpoint_standby.valve_inlet, 1);
|
||
}
|
||
|
||
static void edit_standby_valve_recirc_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("Клапан рециркуляции (Дежурный)", &setpoint_standby.valve_recirc, 1);
|
||
}
|
||
|
||
/* Heater mode callbacks */
|
||
static void edit_work_heater_cb(lv_event_t *e) {
|
||
(void)e;
|
||
setpoint_work.heater = (setpoint_work.heater + 1) % 3;
|
||
/* Apply immediately if in work mode */
|
||
if (state.mode == 2) {
|
||
if (setpoint_work.heater == 0) state.heater_on = 0;
|
||
else if (setpoint_work.heater == 1) state.heater_on = 1;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
static void edit_standby_heater_cb(lv_event_t *e) {
|
||
(void)e;
|
||
setpoint_standby.heater = (setpoint_standby.heater + 1) % 3;
|
||
/* Apply immediately if in standby mode */
|
||
if (state.mode == 1) {
|
||
if (setpoint_standby.heater == 0) state.heater_on = 0;
|
||
else if (setpoint_standby.heater == 1) state.heater_on = 1;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
/* Fan toggle callbacks */
|
||
static void edit_work_fan_cb(lv_event_t *e) {
|
||
(void)e;
|
||
setpoint_work.fan = !setpoint_work.fan;
|
||
/* Apply immediately if in work mode */
|
||
if (state.mode == 2) {
|
||
state.fan_on = setpoint_work.fan;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
static void edit_standby_fan_cb(lv_event_t *e) {
|
||
(void)e;
|
||
setpoint_standby.fan = !setpoint_standby.fan;
|
||
/* Apply immediately if in standby mode */
|
||
if (state.mode == 1) {
|
||
state.fan_on = setpoint_standby.fan;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
/* KWS mode callbacks */
|
||
static void edit_work_kws_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (setpoint_work.kws_auto) {
|
||
setpoint_work.kws_auto = 0;
|
||
setpoint_work.kws_manual = 50.0f;
|
||
/* Apply immediately if in work mode */
|
||
if (state.mode == 2) {
|
||
state.kws_valve = (uint8_t)setpoint_work.kws_manual;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
} else {
|
||
create_popup_param_editor("KWS ручной (Рабочий)", &setpoint_work.kws_manual, 1);
|
||
}
|
||
}
|
||
|
||
static void edit_standby_kws_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (setpoint_standby.kws_auto) {
|
||
setpoint_standby.kws_auto = 0;
|
||
setpoint_standby.kws_manual = 50.0f;
|
||
/* Apply immediately if in standby mode */
|
||
if (state.mode == 1) {
|
||
state.kws_valve = (uint8_t)setpoint_standby.kws_manual;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
} else {
|
||
create_popup_param_editor("KWS ручной (Дежурный)", &setpoint_standby.kws_manual, 1);
|
||
}
|
||
}
|
||
|
||
/* PWW mode callbacks */
|
||
static void edit_work_pww_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (setpoint_work.pww_auto) {
|
||
setpoint_work.pww_auto = 0;
|
||
setpoint_work.pww_manual = 50.0f;
|
||
/* Apply immediately if in work mode */
|
||
if (state.mode == 2) {
|
||
state.pww_valve = (uint8_t)setpoint_work.pww_manual;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
} else {
|
||
create_popup_param_editor("PWW ручной (Рабочий)", &setpoint_work.pww_manual, 1);
|
||
}
|
||
}
|
||
|
||
static void edit_standby_pww_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (setpoint_standby.pww_auto) {
|
||
setpoint_standby.pww_auto = 0;
|
||
setpoint_standby.pww_manual = 50.0f;
|
||
/* Apply immediately if in standby mode */
|
||
if (state.mode == 1) {
|
||
state.pww_valve = (uint8_t)setpoint_standby.pww_manual;
|
||
gui_update_values();
|
||
}
|
||
refresh_setpoints_display();
|
||
} else {
|
||
create_popup_param_editor("PWW ручной (Дежурный)", &setpoint_standby.pww_manual, 1);
|
||
}
|
||
}
|
||
|
||
/* Configuration edit callbacks */
|
||
static void edit_config_offset_cb(lv_event_t *e) {
|
||
float *offset_ptr = (float*)lv_event_get_user_data(e);
|
||
create_popup_param_editor("Коррекция", offset_ptr, 0);
|
||
}
|
||
|
||
static void toggle_recirc_valve_cb(lv_event_t *e) {
|
||
(void)e;
|
||
config.has_recirc_valve = !config.has_recirc_valve;
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
static void toggle_heater_cb(lv_event_t *e) {
|
||
(void)e;
|
||
config.has_heater = !config.has_heater;
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
static void edit_pid_pww_kp_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("PWW Kp", &config.pid_pww_kp, 0);
|
||
}
|
||
|
||
static void edit_pid_pww_ti_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("PWW Ti", &config.pid_pww_ti, 0);
|
||
}
|
||
|
||
static void edit_pid_pww_d_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("PWW D", &config.pid_pww_d, 0);
|
||
}
|
||
|
||
static void edit_pid_kws_kp_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("KWS Kp", &config.pid_kws_kp, 0);
|
||
}
|
||
|
||
static void edit_pid_kws_ti_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("KWS Ti", &config.pid_kws_ti, 0);
|
||
}
|
||
|
||
static void edit_pid_kws_d_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_param_editor("KWS D", &config.pid_kws_d, 0);
|
||
}
|
||
|
||
static void toggle_force_mode_cb(lv_event_t *e) {
|
||
(void)e;
|
||
config.force_mode_enabled = !config.force_mode_enabled;
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
static void toggle_key_swap_cb(lv_event_t *e) {
|
||
(void)e;
|
||
config.key_swap_enabled = !config.key_swap_enabled;
|
||
refresh_setpoints_display();
|
||
}
|
||
|
||
/* Date/Time editor popup */
|
||
static lv_obj_t *popup_datetime = NULL;
|
||
static lv_obj_t *roller_day = NULL;
|
||
static lv_obj_t *roller_month = NULL;
|
||
static lv_obj_t *roller_year = NULL;
|
||
static lv_obj_t *roller_hour = NULL;
|
||
static lv_obj_t *roller_minute = NULL;
|
||
|
||
static void datetime_ok_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (roller_day && roller_month && roller_year && roller_hour && roller_minute) {
|
||
state.day = lv_roller_get_selected(roller_day) + 1;
|
||
state.month = lv_roller_get_selected(roller_month) + 1;
|
||
state.year = 2024 + lv_roller_get_selected(roller_year);
|
||
state.hour = lv_roller_get_selected(roller_hour);
|
||
state.minute = lv_roller_get_selected(roller_minute);
|
||
}
|
||
if (popup_datetime) {
|
||
lv_obj_del(popup_datetime);
|
||
popup_datetime = NULL;
|
||
roller_day = roller_month = roller_year = roller_hour = roller_minute = NULL;
|
||
}
|
||
/* Recreate config screen to show updated datetime */
|
||
if (scr_screen4) {
|
||
lv_obj_del(scr_screen4);
|
||
scr_screen4 = NULL;
|
||
}
|
||
create_screen_config();
|
||
lv_scr_load(scr_screen4);
|
||
}
|
||
|
||
static void datetime_close_cb(lv_event_t *e) {
|
||
(void)e;
|
||
if (popup_datetime) {
|
||
lv_obj_del(popup_datetime);
|
||
popup_datetime = NULL;
|
||
roller_day = roller_month = roller_year = roller_hour = roller_minute = NULL;
|
||
}
|
||
}
|
||
|
||
static void create_popup_datetime(void) {
|
||
if (popup_datetime) return;
|
||
|
||
popup_datetime = lv_obj_create(lv_screen_active());
|
||
lv_obj_set_size(popup_datetime, 420, 280);
|
||
lv_obj_center(popup_datetime);
|
||
lv_obj_set_style_bg_color(popup_datetime, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(popup_datetime, 8, 0);
|
||
lv_obj_set_style_border_width(popup_datetime, 1, 0);
|
||
lv_obj_set_style_border_color(popup_datetime, lv_color_hex(COLOR_BORDER), 0);
|
||
lv_obj_clear_flag(popup_datetime, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(popup_datetime);
|
||
lv_label_set_text(lbl, "Установка даты и времени");
|
||
lv_obj_set_pos(lbl, 20, 10);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Close button (X) */
|
||
lv_obj_t *btn_close = lv_btn_create(popup_datetime);
|
||
lv_obj_set_pos(btn_close, 370, 6);
|
||
lv_obj_set_size(btn_close, 32, 32);
|
||
lv_obj_set_style_bg_color(btn_close, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn_close, 4, 0);
|
||
lbl = lv_label_create(btn_close);
|
||
lv_label_set_text(lbl, "X");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_close, datetime_close_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Date section label */
|
||
lbl = lv_label_create(popup_datetime);
|
||
lv_label_set_text(lbl, "Дата:");
|
||
lv_obj_set_pos(lbl, 20, 50);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Day roller */
|
||
char days[31*4];
|
||
days[0] = '\0';
|
||
for (int i = 1; i <= 31; i++) {
|
||
char tmp[5];
|
||
snprintf(tmp, sizeof(tmp), "%02d\n", i);
|
||
strcat(days, tmp);
|
||
}
|
||
days[strlen(days)-1] = '\0';
|
||
|
||
roller_day = lv_roller_create(popup_datetime);
|
||
lv_roller_set_options(roller_day, days, LV_ROLLER_MODE_NORMAL);
|
||
lv_roller_set_selected(roller_day, state.day - 1, LV_ANIM_OFF);
|
||
lv_obj_set_pos(roller_day, 80, 45);
|
||
lv_obj_set_size(roller_day, 60, 80);
|
||
lv_obj_set_style_text_font(roller_day, &montserrat_16_ru_en, 0);
|
||
lv_obj_set_style_bg_color(roller_day, lv_color_hex(COLOR_BG_BTN), 0);
|
||
|
||
/* Month roller */
|
||
roller_month = lv_roller_create(popup_datetime);
|
||
lv_roller_set_options(roller_month, "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12", LV_ROLLER_MODE_NORMAL);
|
||
lv_roller_set_selected(roller_month, state.month - 1, LV_ANIM_OFF);
|
||
lv_obj_set_pos(roller_month, 150, 45);
|
||
lv_obj_set_size(roller_month, 60, 80);
|
||
lv_obj_set_style_text_font(roller_month, &montserrat_16_ru_en, 0);
|
||
lv_obj_set_style_bg_color(roller_month, lv_color_hex(COLOR_BG_BTN), 0);
|
||
|
||
/* Year roller (2024-2035) */
|
||
roller_year = lv_roller_create(popup_datetime);
|
||
lv_roller_set_options(roller_year, "2024\n2025\n2026\n2027\n2028\n2029\n2030\n2031\n2032\n2033\n2034\n2035", LV_ROLLER_MODE_NORMAL);
|
||
lv_roller_set_selected(roller_year, state.year - 2024, LV_ANIM_OFF);
|
||
lv_obj_set_pos(roller_year, 220, 45);
|
||
lv_obj_set_size(roller_year, 80, 80);
|
||
lv_obj_set_style_text_font(roller_year, &montserrat_16_ru_en, 0);
|
||
lv_obj_set_style_bg_color(roller_year, lv_color_hex(COLOR_BG_BTN), 0);
|
||
|
||
/* Time section label */
|
||
lbl = lv_label_create(popup_datetime);
|
||
lv_label_set_text(lbl, "Время:");
|
||
lv_obj_set_pos(lbl, 20, 140);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Hour roller */
|
||
char hours[24*4];
|
||
hours[0] = '\0';
|
||
for (int i = 0; i < 24; i++) {
|
||
char tmp[5];
|
||
snprintf(tmp, sizeof(tmp), "%02d\n", i);
|
||
strcat(hours, tmp);
|
||
}
|
||
hours[strlen(hours)-1] = '\0';
|
||
|
||
roller_hour = lv_roller_create(popup_datetime);
|
||
lv_roller_set_options(roller_hour, hours, LV_ROLLER_MODE_NORMAL);
|
||
lv_roller_set_selected(roller_hour, state.hour, LV_ANIM_OFF);
|
||
lv_obj_set_pos(roller_hour, 80, 135);
|
||
lv_obj_set_size(roller_hour, 60, 80);
|
||
lv_obj_set_style_text_font(roller_hour, &montserrat_16_ru_en, 0);
|
||
lv_obj_set_style_bg_color(roller_hour, lv_color_hex(COLOR_BG_BTN), 0);
|
||
|
||
lbl = lv_label_create(popup_datetime);
|
||
lv_label_set_text(lbl, ":");
|
||
lv_obj_set_pos(lbl, 145, 160);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Minute roller */
|
||
char minutes[60*4];
|
||
minutes[0] = '\0';
|
||
for (int i = 0; i < 60; i++) {
|
||
char tmp[5];
|
||
snprintf(tmp, sizeof(tmp), "%02d\n", i);
|
||
strcat(minutes, tmp);
|
||
}
|
||
minutes[strlen(minutes)-1] = '\0';
|
||
|
||
roller_minute = lv_roller_create(popup_datetime);
|
||
lv_roller_set_options(roller_minute, minutes, LV_ROLLER_MODE_NORMAL);
|
||
lv_roller_set_selected(roller_minute, state.minute, LV_ANIM_OFF);
|
||
lv_obj_set_pos(roller_minute, 160, 135);
|
||
lv_obj_set_size(roller_minute, 60, 80);
|
||
lv_obj_set_style_text_font(roller_minute, &montserrat_16_ru_en, 0);
|
||
lv_obj_set_style_bg_color(roller_minute, lv_color_hex(COLOR_BG_BTN), 0);
|
||
|
||
/* OK button */
|
||
lv_obj_t *btn_ok = lv_btn_create(popup_datetime);
|
||
lv_obj_set_pos(btn_ok, 160, 230);
|
||
lv_obj_set_size(btn_ok, 100, 40);
|
||
lv_obj_set_style_bg_color(btn_ok, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_radius(btn_ok, 6, 0);
|
||
lbl = lv_label_create(btn_ok);
|
||
lv_label_set_text(lbl, "OK");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_ok, datetime_ok_cb, LV_EVENT_CLICKED, NULL);
|
||
}
|
||
|
||
static void edit_datetime_cb(lv_event_t *e) {
|
||
(void)e;
|
||
create_popup_datetime();
|
||
}
|
||
|
||
/* Password keypad callback */
|
||
static void password_keypad_cb(lv_event_t *e) {
|
||
const char *key = lv_event_get_user_data(e);
|
||
int len = strlen(password_input);
|
||
|
||
if (strcmp(key, "C") == 0) {
|
||
memset(password_input, 0, sizeof(password_input));
|
||
} else if (strcmp(key, "OK") == 0) {
|
||
if (strcmp(password_input, PASSWORD) == 0) {
|
||
/* Correct password - close popup */
|
||
if (popup_password) {
|
||
lv_obj_del(popup_password);
|
||
popup_password = NULL;
|
||
lbl_password_display = NULL;
|
||
}
|
||
memset(password_input, 0, sizeof(password_input));
|
||
|
||
/* Navigate to config screen */
|
||
if (password_target == 0) {
|
||
if (scr_screen4) lv_scr_load(scr_screen4);
|
||
}
|
||
} else {
|
||
/* Wrong password */
|
||
if (lbl_password_display) {
|
||
lv_label_set_text(lbl_password_display, "НЕВЕРНЫЙ ПАРОЛЬ!");
|
||
lv_obj_set_style_text_color(lbl_password_display, lv_color_hex(COLOR_DANGER), 0);
|
||
}
|
||
memset(password_input, 0, sizeof(password_input));
|
||
}
|
||
} else if (len < 8) {
|
||
strcat(password_input, key);
|
||
}
|
||
|
||
/* Update display */
|
||
if (lbl_password_display) {
|
||
char display[20] = "";
|
||
for (int i = 0; i < (int)strlen(password_input); i++) {
|
||
strcat(display, "*");
|
||
}
|
||
if (strlen(display) == 0) {
|
||
lv_label_set_text(lbl_password_display, "Введите пароль");
|
||
lv_obj_set_style_text_color(lbl_password_display, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
} else {
|
||
lv_label_set_text(lbl_password_display, display);
|
||
lv_obj_set_style_text_color(lbl_password_display, lv_color_hex(COLOR_ACCENT), 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Create password popup with keypad */
|
||
static void create_popup_password(int target) {
|
||
if (popup_password) return;
|
||
|
||
password_target = target;
|
||
memset(password_input, 0, sizeof(password_input));
|
||
|
||
popup_password = lv_obj_create(lv_screen_active());
|
||
lv_obj_set_size(popup_password, 300, 420);
|
||
lv_obj_center(popup_password);
|
||
lv_obj_set_style_bg_color(popup_password, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(popup_password, 8, 0);
|
||
lv_obj_set_style_border_width(popup_password, 1, 0);
|
||
lv_obj_set_style_border_color(popup_password, lv_color_hex(COLOR_BORDER), 0);
|
||
lv_obj_clear_flag(popup_password, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(popup_password);
|
||
lv_label_set_text(lbl, "Введите пароль");
|
||
lv_obj_set_pos(lbl, 20, 10);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Close button (X) - справа */
|
||
lv_obj_t *btn_close = lv_btn_create(popup_password);
|
||
lv_obj_set_pos(btn_close, 230, 6);
|
||
lv_obj_set_size(btn_close, 32, 32);
|
||
lv_obj_set_style_bg_color(btn_close, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn_close, 4, 0);
|
||
lbl = lv_label_create(btn_close);
|
||
lv_label_set_text(lbl, "X");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_close, close_popup_cb, LV_EVENT_CLICKED, popup_password);
|
||
|
||
/* Password display */
|
||
lbl_password_display = lv_label_create(popup_password);
|
||
lv_label_set_text(lbl_password_display, "_ _ _ _");
|
||
lv_obj_set_pos(lbl_password_display, 105, 50);
|
||
lv_obj_set_style_text_color(lbl_password_display, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl_password_display, &montserrat_16_ru_en, 0);
|
||
|
||
/* Numeric keypad 3x4 */
|
||
const char *keys[12] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "C", "0", "OK"};
|
||
int key_size = 60;
|
||
int key_spacing = 8;
|
||
int start_x = 30;
|
||
int start_y = 90;
|
||
|
||
for (int i = 0; i < 12; i++) {
|
||
int row = i / 3;
|
||
int col = i % 3;
|
||
|
||
lv_obj_t *btn = lv_btn_create(popup_password);
|
||
lv_obj_set_pos(btn, start_x + col * (key_size + key_spacing),
|
||
start_y + row * (key_size + key_spacing));
|
||
lv_obj_set_size(btn, key_size, key_size);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, keys[i]);
|
||
if (strcmp(keys[i], "C") == 0) {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
} else if (strcmp(keys[i], "OK") == 0) {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
} else {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
}
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
lv_obj_add_event_cb(btn, password_keypad_cb, LV_EVENT_CLICKED, (void*)keys[i]);
|
||
}
|
||
}
|
||
|
||
/* Parameter editor keypad callback */
|
||
static void param_keypad_cb(lv_event_t *e) {
|
||
const char *key = lv_event_get_user_data(e);
|
||
int len = strlen(param_value_str);
|
||
|
||
if (strcmp(key, "C") == 0) {
|
||
memset(param_value_str, 0, sizeof(param_value_str));
|
||
} else if (strcmp(key, "<") == 0) {
|
||
/* Backspace - remove last character */
|
||
if (len > 0) {
|
||
param_value_str[len - 1] = '\0';
|
||
}
|
||
} else if (strcmp(key, ".") == 0) {
|
||
if (strchr(param_value_str, '.') == NULL) {
|
||
if (len == 0 || (len == 1 && param_value_str[0] == '-')) {
|
||
strcat(param_value_str, "0.");
|
||
} else {
|
||
strcat(param_value_str, ".");
|
||
}
|
||
}
|
||
} else if (strcmp(key, "-") == 0) {
|
||
if (len == 0) {
|
||
strcat(param_value_str, "-");
|
||
} else if (param_value_str[0] == '-') {
|
||
/* Remove minus */
|
||
memmove(param_value_str, param_value_str + 1, strlen(param_value_str));
|
||
} else {
|
||
/* Add minus at beginning */
|
||
memmove(param_value_str + 1, param_value_str, strlen(param_value_str) + 1);
|
||
param_value_str[0] = '-';
|
||
}
|
||
} else if (strcmp(key, "OK") == 0) {
|
||
if (param_being_edited && strlen(param_value_str) > 0) {
|
||
float val = (float)atof(param_value_str);
|
||
if (param_is_percentage) {
|
||
if (val < 0.0f) val = 0.0f;
|
||
if (val > 100.0f) val = 100.0f;
|
||
}
|
||
*param_being_edited = val;
|
||
}
|
||
if (popup_param_editor) {
|
||
lv_obj_del(popup_param_editor);
|
||
popup_param_editor = NULL;
|
||
lbl_param_value = NULL;
|
||
}
|
||
memset(param_value_str, 0, sizeof(param_value_str));
|
||
param_being_edited = NULL;
|
||
refresh_setpoints_display();
|
||
return;
|
||
} else if (len < 10) {
|
||
strcat(param_value_str, key);
|
||
}
|
||
|
||
if (lbl_param_value) {
|
||
if (strlen(param_value_str) == 0) {
|
||
lv_label_set_text(lbl_param_value, "0");
|
||
} else {
|
||
lv_label_set_text(lbl_param_value, param_value_str);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Create parameter editor popup */
|
||
static void create_popup_param_editor(const char *name, float *param_ptr, int is_percentage) {
|
||
if (popup_param_editor) return;
|
||
|
||
param_being_edited = param_ptr;
|
||
param_is_percentage = is_percentage;
|
||
strncpy(param_name, name, 49);
|
||
param_name[49] = '\0';
|
||
memset(param_value_str, 0, sizeof(param_value_str));
|
||
snprintf(param_value_str, sizeof(param_value_str), "%.1f", *param_ptr);
|
||
|
||
popup_param_editor = lv_obj_create(lv_screen_active());
|
||
lv_obj_set_size(popup_param_editor, 360, 380);
|
||
lv_obj_center(popup_param_editor);
|
||
lv_obj_set_style_bg_color(popup_param_editor, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(popup_param_editor, 8, 0);
|
||
lv_obj_set_style_border_width(popup_param_editor, 1, 0);
|
||
lv_obj_set_style_border_color(popup_param_editor, lv_color_hex(COLOR_BORDER), 0);
|
||
lv_obj_clear_flag(popup_param_editor, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title - shorter */
|
||
lv_obj_t *lbl = lv_label_create(popup_param_editor);
|
||
lv_label_set_text(lbl, param_name);
|
||
lv_obj_set_pos(lbl, 10, 10);
|
||
lv_obj_set_width(lbl, 260);
|
||
lv_label_set_long_mode(lbl, LV_LABEL_LONG_CLIP);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Close button (X) - справа */
|
||
lv_obj_t *btn_close = lv_btn_create(popup_param_editor);
|
||
lv_obj_set_pos(btn_close, 290, 8);
|
||
lv_obj_set_size(btn_close, 32, 32);
|
||
lv_obj_set_style_bg_color(btn_close, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn_close, 4, 0);
|
||
lbl = lv_label_create(btn_close);
|
||
lv_label_set_text(lbl, "X");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_close, close_popup_cb, LV_EVENT_CLICKED, popup_param_editor);
|
||
|
||
/* Value display with unit */
|
||
lbl_param_value = lv_label_create(popup_param_editor);
|
||
lv_label_set_text(lbl_param_value, param_value_str);
|
||
lv_obj_set_pos(lbl_param_value, 130, 50);
|
||
lv_obj_set_style_text_color(lbl_param_value, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_param_value, &montserrat_16_ru_en, 0);
|
||
|
||
/* Unit label */
|
||
lbl = lv_label_create(popup_param_editor);
|
||
lv_label_set_text(lbl, is_percentage ? "%" : "°C");
|
||
lv_obj_set_pos(lbl, 210, 50);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Numeric keypad 4x4 with decimal, minus, backspace */
|
||
const char *keys[16] = {"1", "2", "3", "C", "4", "5", "6", "<", "7", "8", "9", "-", "0", ".", "0", "."};
|
||
int key_size = 55;
|
||
int key_spacing = 6;
|
||
int start_x = 25;
|
||
int start_y = 85;
|
||
|
||
/* First 3 rows (12 buttons) */
|
||
for (int i = 0; i < 12; i++) {
|
||
int row = i / 4;
|
||
int col = i % 4;
|
||
|
||
lv_obj_t *btn = lv_btn_create(popup_param_editor);
|
||
lv_obj_set_pos(btn, start_x + col * (key_size + key_spacing),
|
||
start_y + row * (key_size + key_spacing));
|
||
lv_obj_set_size(btn, key_size, key_size);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, keys[i]);
|
||
if (strcmp(keys[i], "C") == 0) {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
} else if (strcmp(keys[i], "<") == 0 || strcmp(keys[i], ".") == 0 || strcmp(keys[i], "-") == 0) {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
} else {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
}
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
lv_obj_add_event_cb(btn, param_keypad_cb, LV_EVENT_CLICKED, (void*)keys[i]);
|
||
}
|
||
|
||
/* Bottom row: 0, dot, OK */
|
||
lv_obj_t *btn = lv_btn_create(popup_param_editor);
|
||
lv_obj_set_pos(btn, start_x, start_y + 3 * (key_size + key_spacing));
|
||
lv_obj_set_size(btn, key_size, key_size);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "0");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, param_keypad_cb, LV_EVENT_CLICKED, (void*)"0");
|
||
|
||
btn = lv_btn_create(popup_param_editor);
|
||
lv_obj_set_pos(btn, start_x + (key_size + key_spacing), start_y + 3 * (key_size + key_spacing));
|
||
lv_obj_set_size(btn, key_size, key_size);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, ".");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, param_keypad_cb, LV_EVENT_CLICKED, (void*)".");
|
||
|
||
btn = lv_btn_create(popup_param_editor);
|
||
lv_obj_set_pos(btn, start_x + 2 * (key_size + key_spacing), start_y + 3 * (key_size + key_spacing));
|
||
lv_obj_set_size(btn, key_size * 2 + key_spacing, key_size);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "OK");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, param_keypad_cb, LV_EVENT_CLICKED, (void*)"OK");
|
||
}
|
||
|
||
/* Create popup window for sensor readings */
|
||
static void create_popup_readings(void) {
|
||
if (popup_readings) return;
|
||
|
||
popup_readings = lv_obj_create(lv_screen_active());
|
||
lv_obj_set_size(popup_readings, 400, 280);
|
||
lv_obj_center(popup_readings);
|
||
lv_obj_set_style_bg_color(popup_readings, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(popup_readings, 12, 0);
|
||
lv_obj_set_style_border_width(popup_readings, 3, 0);
|
||
lv_obj_set_style_border_color(popup_readings, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_clear_flag(popup_readings, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(popup_readings);
|
||
lv_label_set_text(lbl, "ПОКАЗАНИЯ ДАТЧИКОВ");
|
||
lv_obj_set_pos(lbl, 10, 15);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Close button (X) - справа */
|
||
lv_obj_t *btn_close = lv_btn_create(popup_readings);
|
||
lv_obj_set_pos(btn_close, 330, 10);
|
||
lv_obj_set_size(btn_close, 35, 35);
|
||
lv_obj_set_style_bg_color(btn_close, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_radius(btn_close, 6, 0);
|
||
lbl = lv_label_create(btn_close);
|
||
lv_label_set_text(lbl, "X");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_close, close_popup_cb, LV_EVENT_CLICKED, popup_readings);
|
||
|
||
/* Room sensors */
|
||
int y = 55;
|
||
char buf[64];
|
||
|
||
lbl = lv_label_create(popup_readings);
|
||
lv_label_set_text(lbl, "КОМНАТА:");
|
||
lv_obj_set_pos(lbl, 30, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 28;
|
||
lbl = lv_label_create(popup_readings);
|
||
snprintf(buf, sizeof(buf), "Температура: %.1f°C", state.temp_room / 10.0f);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 40, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 26;
|
||
lbl = lv_label_create(popup_readings);
|
||
snprintf(buf, sizeof(buf), "Влажность: %.1f%%", state.hum_room / 10.0f);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 40, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Channel sensors */
|
||
y += 35;
|
||
lbl = lv_label_create(popup_readings);
|
||
lv_label_set_text(lbl, "КАНАЛ ПРИТОКА:");
|
||
lv_obj_set_pos(lbl, 30, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 28;
|
||
lbl = lv_label_create(popup_readings);
|
||
snprintf(buf, sizeof(buf), "Температура: %.1f°C", state.temp_channel / 10.0f);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 40, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 26;
|
||
lbl = lv_label_create(popup_readings);
|
||
snprintf(buf, sizeof(buf), "Влажность: %.1f%%", state.hum_channel / 10.0f);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 40, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
}
|
||
|
||
/* Add alarm to log */
|
||
static void add_alarm_to_log(const char *message, int active) {
|
||
if (alarm_count < MAX_ALARMS) {
|
||
strncpy(alarm_log[alarm_count].message, message, 79);
|
||
alarm_log[alarm_count].message[79] = '\0';
|
||
snprintf(alarm_log[alarm_count].timestamp, 20, "%02d.%02d.%04d %02d:%02d",
|
||
state.day, state.month, state.year, state.hour, state.minute);
|
||
alarm_log[alarm_count].active = active;
|
||
alarm_count++;
|
||
}
|
||
}
|
||
|
||
/* Create alarm popup */
|
||
static void create_popup_alarm(const char *message) {
|
||
if (popup_alarm) {
|
||
lv_obj_del(popup_alarm);
|
||
}
|
||
|
||
add_alarm_to_log(message, 1);
|
||
|
||
popup_alarm = lv_obj_create(lv_screen_active());
|
||
lv_obj_set_size(popup_alarm, 400, 240);
|
||
lv_obj_center(popup_alarm);
|
||
lv_obj_set_style_bg_color(popup_alarm, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_radius(popup_alarm, 12, 0);
|
||
lv_obj_set_style_border_width(popup_alarm, 4, 0);
|
||
lv_obj_set_style_border_color(popup_alarm, lv_color_hex(0xFF0000), 0);
|
||
lv_obj_clear_flag(popup_alarm, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(popup_alarm);
|
||
lv_label_set_text(lbl, "АВАРИЯ");
|
||
lv_obj_set_pos(lbl, 20, 15);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Close button (X) - справа в углу */
|
||
lv_obj_t *btn_close = lv_btn_create(popup_alarm);
|
||
lv_obj_set_pos(btn_close, 330, 10);
|
||
lv_obj_set_size(btn_close, 35, 35);
|
||
lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x800000), 0);
|
||
lv_obj_set_style_radius(btn_close, 6, 0);
|
||
lbl = lv_label_create(btn_close);
|
||
lv_label_set_text(lbl, "X");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn_close, close_popup_cb, LV_EVENT_CLICKED, popup_alarm);
|
||
|
||
/* Message */
|
||
lbl = lv_label_create(popup_alarm);
|
||
lv_label_set_text(lbl, message);
|
||
lv_obj_set_pos(lbl, 30, 70);
|
||
lv_obj_set_width(lbl, 340);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_label_set_long_mode(lbl, LV_LABEL_LONG_WRAP);
|
||
lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, 0);
|
||
}
|
||
|
||
static void create_screen_main(void) {
|
||
lv_obj_t *scr = lv_obj_create(NULL);
|
||
scr_main = scr;
|
||
lv_obj_set_style_bg_color(scr, lv_color_hex(COLOR_BG_DARK), 0);
|
||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* === STATUS PANEL === (нижняя часть экрана) */
|
||
lv_obj_t *panel_status = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_status, 10, 275);
|
||
lv_obj_set_size(panel_status, 240, 195);
|
||
lv_obj_set_style_bg_color(panel_status, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_status, 6, 0);
|
||
lv_obj_set_style_border_width(panel_status, 0, 0);
|
||
lv_obj_clear_flag(panel_status, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
lv_obj_t *lbl = lv_label_create(panel_status);
|
||
lv_label_set_text(lbl, "Состояние");
|
||
lv_obj_set_pos(lbl, 10, 5);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_room_num = lv_label_create(panel_status);
|
||
lv_label_set_text(lbl_room_num, "Комната: 101");
|
||
lv_obj_set_pos(lbl_room_num, 10, 30);
|
||
lv_obj_set_style_text_color(lbl_room_num, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_room_num, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_master_slave = lv_label_create(panel_status);
|
||
lv_label_set_text(lbl_master_slave, "Мастер");
|
||
lv_obj_set_pos(lbl_master_slave, 10, 55);
|
||
lv_obj_set_style_text_color(lbl_master_slave, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_master_slave, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_season = lv_label_create(panel_status);
|
||
lv_label_set_text(lbl_season, "Сезон: Зима");
|
||
lv_obj_set_pos(lbl_season, 10, 80);
|
||
lv_obj_set_style_text_color(lbl_season, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_season, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_mode = lv_label_create(panel_status);
|
||
lv_label_set_text(lbl_mode, "Режим: Дежурный");
|
||
lv_obj_set_pos(lbl_mode, 10, 105);
|
||
lv_obj_set_style_text_color(lbl_mode, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl_mode, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_datetime = lv_label_create(panel_status);
|
||
{
|
||
char dt_buf[32];
|
||
snprintf(dt_buf, sizeof(dt_buf), "%02d.%02d.%04d %02d:%02d",
|
||
state.day, state.month, state.year, state.hour, state.minute);
|
||
lv_label_set_text(lbl_datetime, dt_buf);
|
||
}
|
||
lv_obj_set_pos(lbl_datetime, 10, 135);
|
||
lv_obj_set_style_text_color(lbl_datetime, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl_datetime, &montserrat_16_ru_en, 0);
|
||
|
||
/* === CLIMATE PANEL === */
|
||
lv_obj_t *panel_climate = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_climate, 260, 275);
|
||
lv_obj_set_size(panel_climate, 230, 195);
|
||
lv_obj_set_style_bg_color(panel_climate, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_climate, 6, 0);
|
||
lv_obj_set_style_border_width(panel_climate, 0, 0);
|
||
lv_obj_clear_flag(panel_climate, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
lbl = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl, "Комната");
|
||
lv_obj_set_pos(lbl, 10, 5);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl, "Температура:");
|
||
lv_obj_set_pos(lbl, 10, 35);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_temp_room = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl_temp_room, "22.0°C");
|
||
lv_obj_set_pos(lbl_temp_room, 130, 35);
|
||
lv_obj_set_style_text_color(lbl_temp_room, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl_temp_room, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl, "Влажность:");
|
||
lv_obj_set_pos(lbl, 10, 65);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_hum_room = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl_hum_room, "45%");
|
||
lv_obj_set_pos(lbl_hum_room, 130, 65);
|
||
lv_obj_set_style_text_color(lbl_hum_room, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_hum_room, &montserrat_16_ru_en, 0);
|
||
|
||
/* Channel info */
|
||
lbl = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl, "Канал:");
|
||
lv_obj_set_pos(lbl, 10, 100);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_temp_channel = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl_temp_channel, "20.0°C");
|
||
lv_obj_set_pos(lbl_temp_channel, 70, 100);
|
||
lv_obj_set_style_text_color(lbl_temp_channel, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_temp_channel, &montserrat_16_ru_en, 0);
|
||
|
||
lbl_hum_channel = lv_label_create(panel_climate);
|
||
lv_label_set_text(lbl_hum_channel, "50%");
|
||
lv_obj_set_pos(lbl_hum_channel, 150, 100);
|
||
lv_obj_set_style_text_color(lbl_hum_channel, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_hum_channel, &montserrat_16_ru_en, 0);
|
||
|
||
/* === CONTROL BUTTONS === (правая часть нижней половины) */
|
||
int btn_x = 500;
|
||
int btn_y = 275;
|
||
|
||
/* Stop button */
|
||
lv_obj_t *btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, btn_x, btn_y);
|
||
lv_obj_set_size(btn, 140, 100);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "ОСТАНОВ");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_stop_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Standby button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, btn_x + 150, btn_y);
|
||
lv_obj_set_size(btn, 140, 48);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Дежурный");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_standby_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Work button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, btn_x + 150, btn_y + 52);
|
||
lv_obj_set_size(btn, 140, 48);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Рабочий");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_work_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Menu button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, btn_x, btn_y + 108);
|
||
lv_obj_set_size(btn, 140, 48);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Меню");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_menu_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Readings button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, btn_x + 150, btn_y + 108);
|
||
lv_obj_set_size(btn, 140, 48);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Показания");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_readings_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* === EQUIPMENT SCHEMA AREA === (верхняя половина экрана) */
|
||
schema_panel = lv_obj_create(scr);
|
||
lv_obj_set_pos(schema_panel, 10, 10);
|
||
lv_obj_set_size(schema_panel, 780, 255);
|
||
lv_obj_set_style_bg_color(schema_panel, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(schema_panel, 6, 0);
|
||
lv_obj_set_style_border_width(schema_panel, 0, 0);
|
||
lv_obj_clear_flag(schema_panel, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Hint labels */
|
||
lv_obj_t *hint1 = lv_label_create(schema_panel);
|
||
lv_label_set_text(hint1, "Активные модули (перетащите вниз для отключения):");
|
||
lv_obj_set_pos(hint1, 10, 2);
|
||
lv_obj_set_style_text_color(hint1, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(hint1, &montserrat_16_ru_en, 0);
|
||
|
||
lv_obj_t *hint2 = lv_label_create(schema_panel);
|
||
lv_label_set_text(hint2, "Неактивные (перетащите вверх для включения):");
|
||
lv_obj_set_pos(hint2, 10, 155);
|
||
lv_obj_set_style_text_color(hint2, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(hint2, &montserrat_16_ru_en, 0);
|
||
|
||
/* Create continuous pipe background line FIRST (so modules appear on top) */
|
||
/* Calculate total width needed for all module positions */
|
||
int pipe_x = 5;
|
||
int pipe_y = MODULES_ACTIVE_Y + 25;
|
||
int pipe_segment_width = 35; /* Scaled pipe image width approximately */
|
||
int total_modules_width = 50 + MODULE_COUNT * (MODULE_WIDTH + MODULE_SPACING) + 50;
|
||
|
||
/* Create pipe segments to form continuous line */
|
||
for (int px = pipe_x; px < total_modules_width; px += pipe_segment_width) {
|
||
lv_obj_t *pipe_seg = lv_image_create(schema_panel);
|
||
lv_image_set_src(pipe_seg, &img_pipe);
|
||
lv_image_set_scale(pipe_seg, MODULE_SCALE);
|
||
lv_obj_set_pos(pipe_seg, px, pipe_y);
|
||
}
|
||
|
||
/* Create all modules (they will appear on top of pipe line) */
|
||
int active_x = 50; /* Start after the inlet pipe */
|
||
int inactive_x = 10;
|
||
|
||
for (int i = 0; i < MODULE_COUNT; i++) {
|
||
int x_pos, y_pos;
|
||
if (modules[i].enabled) {
|
||
x_pos = active_x;
|
||
y_pos = MODULES_ACTIVE_Y;
|
||
active_x += MODULE_WIDTH + MODULE_SPACING;
|
||
} else {
|
||
x_pos = inactive_x;
|
||
y_pos = MODULES_INACTIVE_Y;
|
||
inactive_x += MODULE_WIDTH + MODULE_SPACING;
|
||
}
|
||
create_module_widget(schema_panel, i, x_pos, y_pos);
|
||
|
||
/* No more individual pipe placeholders needed - continuous pipe line handles it */
|
||
pipe_placeholders[i] = NULL;
|
||
}
|
||
|
||
/* Update all module displays with current values */
|
||
for (int i = 0; i < MODULE_COUNT; i++) {
|
||
update_module_display(i);
|
||
}
|
||
}
|
||
|
||
static void create_screen_screen1(void) {
|
||
lv_obj_t *scr = lv_obj_create(NULL);
|
||
scr_screen1 = scr;
|
||
lv_obj_set_style_bg_color(scr, lv_color_hex(COLOR_BG_DARK), 0);
|
||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(scr);
|
||
lv_label_set_text(lbl, "МЕНЮ");
|
||
lv_obj_set_pos(lbl, 370, 30);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
int btn_width = 300;
|
||
int btn_height = 55;
|
||
int x = 250;
|
||
int y = 90;
|
||
int spacing = 15;
|
||
|
||
/* Button: Back to Main Screen */
|
||
lv_obj_t *btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, x, y);
|
||
lv_obj_set_size(btn, btn_width, btn_height);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Главный экран");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_back_to_main_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += btn_height + spacing;
|
||
|
||
/* Button: Log (Журнал) */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, x, y);
|
||
lv_obj_set_size(btn, btn_width, btn_height);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Журнал аварий");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_log_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += btn_height + spacing;
|
||
|
||
/* Button: Settings Parameters (Уставка параметров) */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, x, y);
|
||
lv_obj_set_size(btn, btn_width, btn_height);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Уставка параметров");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_settings_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += btn_height + spacing;
|
||
|
||
/* Button: Configuration (Настройки - требует пароль) */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, x, y);
|
||
lv_obj_set_size(btn, btn_width, btn_height);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Настройки");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_config_cb, LV_EVENT_CLICKED, NULL);
|
||
}
|
||
|
||
static void create_screen_screen2(void) {
|
||
lv_obj_t *scr = lv_obj_create(NULL);
|
||
scr_screen2 = scr;
|
||
lv_obj_set_style_bg_color(scr, lv_color_hex(0x2C3E50), 0);
|
||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(scr);
|
||
lv_label_set_text(lbl, "ЖУРНАЛ АВАРИЙ");
|
||
lv_obj_set_pos(lbl, 300, 20);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xECF0F1), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Date/Time display */
|
||
char datetime_buf[32];
|
||
snprintf(datetime_buf, sizeof(datetime_buf), "%02d.%02d.%04d %02d:%02d",
|
||
state.day, state.month, state.year, state.hour, state.minute);
|
||
lbl = lv_label_create(scr);
|
||
lv_label_set_text(lbl, datetime_buf);
|
||
lv_obj_set_pos(lbl, 620, 20);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xBDC3C7), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Log panel */
|
||
lv_obj_t *log_panel = lv_obj_create(scr);
|
||
lv_obj_set_pos(log_panel, 20, 60);
|
||
lv_obj_set_size(log_panel, 760, 300);
|
||
lv_obj_set_style_bg_color(log_panel, lv_color_hex(0x34495E), 0);
|
||
lv_obj_set_style_radius(log_panel, 8, 0);
|
||
lv_obj_set_style_border_width(log_panel, 2, 0);
|
||
lv_obj_set_style_border_color(log_panel, lv_color_hex(0x7F8C8D), 0);
|
||
|
||
/* Display log entries */
|
||
int y = 10;
|
||
if (alarm_count == 0) {
|
||
lbl = lv_label_create(log_panel);
|
||
lv_label_set_text(lbl, "Аварий нет");
|
||
lv_obj_set_pos(lbl, 320, 130);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
} else {
|
||
for (int i = 0; i < alarm_count && i < 8; i++) {
|
||
char buf[150];
|
||
snprintf(buf, sizeof(buf), "%s - %s",
|
||
alarm_log[i].message, alarm_log[i].timestamp);
|
||
|
||
lbl = lv_label_create(log_panel);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 10, y);
|
||
lv_obj_set_width(lbl, 730);
|
||
if (alarm_log[i].active) {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
} else {
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
}
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
y += 35;
|
||
}
|
||
}
|
||
|
||
/* Buttons */
|
||
int btn_y = 380;
|
||
|
||
/* Clear Log button */
|
||
lv_obj_t *btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 50, btn_y);
|
||
lv_obj_set_size(btn, 160, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Очистить журнал");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_DANGER), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_clear_log_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Reset Alarm button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 230, btn_y);
|
||
lv_obj_set_size(btn, 160, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Сбросить аварию");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_reset_alarm_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Back to Menu button */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 600, btn_y);
|
||
lv_obj_set_size(btn, 120, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 6, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Меню");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_menu_cb, LV_EVENT_CLICKED, NULL);
|
||
}
|
||
|
||
static void create_screen_screen3(void) {
|
||
lv_obj_t *scr = lv_obj_create(NULL);
|
||
scr_screen3 = scr;
|
||
lv_obj_set_style_bg_color(scr, lv_color_hex(COLOR_BG_DARK), 0);
|
||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
/* Title */
|
||
lv_obj_t *lbl = lv_label_create(scr);
|
||
lv_label_set_text(lbl, "УСТАВКА ПАРАМЕТРОВ");
|
||
lv_obj_set_pos(lbl, 290, 10);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
char buf[64];
|
||
int row_h = 34;
|
||
|
||
/* Left panel - Work mode - SCROLLABLE */
|
||
lv_obj_t *panel_work = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_work, 15, 40);
|
||
lv_obj_set_size(panel_work, 380, 310);
|
||
lv_obj_set_style_bg_color(panel_work, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_work, 6, 0);
|
||
lv_obj_set_style_border_width(panel_work, 0, 0);
|
||
lv_obj_set_scroll_dir(panel_work, LV_DIR_VER);
|
||
lv_obj_set_scrollbar_mode(panel_work, LV_SCROLLBAR_MODE_AUTO);
|
||
|
||
lbl = lv_label_create(panel_work);
|
||
lv_label_set_text(lbl, "РАБОЧИЙ");
|
||
lv_obj_set_pos(lbl, 10, 8);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
int y = 38;
|
||
|
||
/* Temperature */
|
||
lv_obj_t *btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Температура: %.1f°C", setpoint_work.temp);
|
||
lbl_work_temp = lv_label_create(btn);
|
||
lv_label_set_text(lbl_work_temp, buf);
|
||
lv_obj_center(lbl_work_temp);
|
||
lv_obj_set_style_text_color(lbl_work_temp, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_work_temp, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_temp_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Humidity */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Влажность: %.1f%%", setpoint_work.hum);
|
||
lbl_work_hum = lv_label_create(btn);
|
||
lv_label_set_text(lbl_work_hum, buf);
|
||
lv_obj_center(lbl_work_hum);
|
||
lv_obj_set_style_text_color(lbl_work_hum, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_work_hum, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_hum_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Valve inlet */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Клапан притока: %.1f%%", setpoint_work.valve_inlet);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_valve_inlet_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Valve recirc */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Клапан рецирк: %.1f%%", setpoint_work.valve_recirc);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_valve_recirc_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Heater mode */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
const char *heater_modes[] = {"ВЫКЛ", "ВКЛ", "АВТО"};
|
||
snprintf(buf, sizeof(buf), "Электронагрев: %s", heater_modes[setpoint_work.heater]);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_heater_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Fan toggle */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Вентилятор: %s", setpoint_work.fan ? "ВКЛ" : "ВЫКЛ");
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, setpoint_work.fan ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_fan_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* KWS mode */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
if (setpoint_work.kws_auto) {
|
||
snprintf(buf, sizeof(buf), "KWS: АВТО");
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "KWS: %.1f%%", setpoint_work.kws_manual);
|
||
}
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_kws_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* PWW mode */
|
||
btn = lv_btn_create(panel_work);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
if (setpoint_work.pww_auto) {
|
||
snprintf(buf, sizeof(buf), "PWW: АВТО");
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "PWW: %.1f%%", setpoint_work.pww_manual);
|
||
}
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_work_pww_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Right panel - Standby mode - SCROLLABLE */
|
||
lv_obj_t *panel_standby = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_standby, 405, 40);
|
||
lv_obj_set_size(panel_standby, 380, 310);
|
||
lv_obj_set_style_bg_color(panel_standby, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_standby, 6, 0);
|
||
lv_obj_set_style_border_width(panel_standby, 0, 0);
|
||
lv_obj_set_scroll_dir(panel_standby, LV_DIR_VER);
|
||
lv_obj_set_scrollbar_mode(panel_standby, LV_SCROLLBAR_MODE_AUTO);
|
||
|
||
lbl = lv_label_create(panel_standby);
|
||
lv_label_set_text(lbl, "ДЕЖУРНЫЙ");
|
||
lv_obj_set_pos(lbl, 10, 8);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y = 38;
|
||
/* Standby Temperature */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Температура: %.1f°C", setpoint_standby.temp);
|
||
lbl_standby_temp = lv_label_create(btn);
|
||
lv_label_set_text(lbl_standby_temp, buf);
|
||
lv_obj_center(lbl_standby_temp);
|
||
lv_obj_set_style_text_color(lbl_standby_temp, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_standby_temp, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_temp_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Standby Humidity */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Влажность: %.1f%%", setpoint_standby.hum);
|
||
lbl_standby_hum = lv_label_create(btn);
|
||
lv_label_set_text(lbl_standby_hum, buf);
|
||
lv_obj_center(lbl_standby_hum);
|
||
lv_obj_set_style_text_color(lbl_standby_hum, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_standby_hum, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_hum_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Standby Valve inlet */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Клапан притока: %.1f%%", setpoint_standby.valve_inlet);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_valve_inlet_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Standby Valve recirc */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Клапан рецирк: %.1f%%", setpoint_standby.valve_recirc);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_valve_recirc_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Heater mode */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Электронагрев: %s", heater_modes[setpoint_standby.heater]);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_heater_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* Fan toggle */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
snprintf(buf, sizeof(buf), "Вентилятор: %s", setpoint_standby.fan ? "ВКЛ" : "ВЫКЛ");
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, setpoint_standby.fan ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_fan_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* KWS mode */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
if (setpoint_standby.kws_auto) {
|
||
snprintf(buf, sizeof(buf), "KWS: АВТО");
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "KWS: %.1f%%", setpoint_standby.kws_manual);
|
||
}
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_kws_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += row_h + 6;
|
||
/* PWW mode */
|
||
btn = lv_btn_create(panel_standby);
|
||
lv_obj_set_pos(btn, 5, y);
|
||
lv_obj_set_size(btn, 350, row_h);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
if (setpoint_standby.pww_auto) {
|
||
snprintf(buf, sizeof(buf), "PWW: АВТО");
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "PWW: %.1f%%", setpoint_standby.pww_manual);
|
||
}
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_standby_pww_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Common parameters section */
|
||
lv_obj_t *panel_common = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_common, 15, 355);
|
||
lv_obj_set_size(panel_common, 770, 70);
|
||
lv_obj_set_style_bg_color(panel_common, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_common, 6, 0);
|
||
lv_obj_set_style_border_width(panel_common, 0, 0);
|
||
lv_obj_clear_flag(panel_common, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
lbl = lv_label_create(panel_common);
|
||
lv_label_set_text(lbl, "Общие параметры:");
|
||
lv_obj_set_pos(lbl, 10, 5);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
snprintf(buf, sizeof(buf), "Фильтр G4: %s", filter_g4_date);
|
||
lbl = lv_label_create(panel_common);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 10, 28);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
snprintf(buf, sizeof(buf), "Фильтр H13: %s", filter_h13_date);
|
||
lbl = lv_label_create(panel_common);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 250, 28);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
snprintf(buf, sizeof(buf), "%02d:%02d %02d.%02d.%d", state.hour, state.minute, state.day, state.month, state.year);
|
||
lbl = lv_label_create(panel_common);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_set_pos(lbl, 510, 28);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(panel_common);
|
||
lv_label_set_text(lbl, "(для ввода даты замены)");
|
||
lv_obj_set_pos(lbl, 10, 48);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Buttons row */
|
||
int btn_y = 435;
|
||
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 200, btn_y);
|
||
lv_obj_set_size(btn, 110, 38);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Сохранить");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 330, btn_y);
|
||
lv_obj_set_size(btn, 140, 38);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "По умолчанию");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 490, btn_y);
|
||
lv_obj_set_size(btn, 100, 38);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Меню");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_menu_cb, LV_EVENT_CLICKED, NULL);
|
||
}
|
||
|
||
/* ============ SCREEN 4: НАСТРОЙКИ (config, after password) ============ */
|
||
static void create_screen_config(void) {
|
||
lv_obj_t *scr = lv_obj_create(NULL);
|
||
scr_screen4 = scr;
|
||
lv_obj_set_style_bg_color(scr, lv_color_hex(COLOR_BG_DARK), 0);
|
||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||
|
||
char buf[64];
|
||
|
||
/* Header */
|
||
lv_obj_t *lbl = lv_label_create(scr);
|
||
lv_label_set_text(lbl, "НАСТРОЙКИ");
|
||
lv_obj_set_pos(lbl, 340, 10);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
/* Left panel - Sensor corrections - SCROLLABLE */
|
||
lv_obj_t *panel_left = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_left, 10, 45);
|
||
lv_obj_set_size(panel_left, 385, 340);
|
||
lv_obj_set_style_bg_color(panel_left, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_left, 8, 0);
|
||
lv_obj_set_style_border_width(panel_left, 0, 0);
|
||
lv_obj_set_style_pad_all(panel_left, 8, 0);
|
||
lv_obj_set_scroll_dir(panel_left, LV_DIR_VER);
|
||
lv_obj_set_scrollbar_mode(panel_left, LV_SCROLLBAR_MODE_AUTO);
|
||
|
||
lbl = lv_label_create(panel_left);
|
||
lv_label_set_text(lbl, "Коррекция датчиков");
|
||
lv_obj_set_pos(lbl, 10, 5);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lv_obj_t *btn;
|
||
int y = 35;
|
||
const char *labels[] = {
|
||
"Темп. помещения:",
|
||
"Влажн. помещения:",
|
||
"Темп. в канале:",
|
||
"Влажн. в канале:",
|
||
"Клапан притока:",
|
||
"Клапан рецирк.:"
|
||
};
|
||
float *offsets[] = {
|
||
&config.temp_room_offset,
|
||
&config.hum_room_offset,
|
||
&config.temp_channel_offset,
|
||
&config.hum_channel_offset,
|
||
&config.valve_inlet_offset,
|
||
&config.valve_recirc_offset
|
||
};
|
||
|
||
for (int i = 0; i < 6; i++) {
|
||
btn = lv_btn_create(panel_left);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
lv_obj_t *lbl_name = lv_label_create(btn);
|
||
lv_label_set_text(lbl_name, labels[i]);
|
||
lv_obj_align(lbl_name, LV_ALIGN_LEFT_MID, 10, 0);
|
||
lv_obj_set_style_text_color(lbl_name, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl_name, &montserrat_16_ru_en, 0);
|
||
|
||
snprintf(buf, sizeof(buf), "%+.1f", *offsets[i]);
|
||
lv_obj_t *lbl_val = lv_label_create(btn);
|
||
lv_label_set_text(lbl_val, buf);
|
||
lv_obj_align(lbl_val, LV_ALIGN_RIGHT_MID, -10, 0);
|
||
lv_obj_set_style_text_color(lbl_val, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl_val, &montserrat_16_ru_en, 0);
|
||
|
||
lv_obj_add_event_cb(btn, edit_config_offset_cb, LV_EVENT_CLICKED, offsets[i]);
|
||
|
||
y += 40;
|
||
}
|
||
|
||
/* Toggles for equipment presence */
|
||
y += 10;
|
||
btn = lv_btn_create(panel_left);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Клапан рецирк.:");
|
||
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 10, 0);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, config.has_recirc_valve ? "ЕСТЬ" : "НЕТ");
|
||
lv_obj_align(lbl, LV_ALIGN_RIGHT_MID, -10, 0);
|
||
lv_obj_set_style_text_color(lbl, config.has_recirc_valve ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, toggle_recirc_valve_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += 40;
|
||
btn = lv_btn_create(panel_left);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Электронагрев.:");
|
||
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 10, 0);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, config.has_heater ? "ЕСТЬ" : "НЕТ");
|
||
lv_obj_align(lbl, LV_ALIGN_RIGHT_MID, -10, 0);
|
||
lv_obj_set_style_text_color(lbl, config.has_heater ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, toggle_heater_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Right panel - PID and mode settings - SCROLLABLE */
|
||
lv_obj_t *panel_right = lv_obj_create(scr);
|
||
lv_obj_set_pos(panel_right, 405, 45);
|
||
lv_obj_set_size(panel_right, 385, 340);
|
||
lv_obj_set_style_bg_color(panel_right, lv_color_hex(COLOR_BG_PANEL), 0);
|
||
lv_obj_set_style_radius(panel_right, 8, 0);
|
||
lv_obj_set_style_border_width(panel_right, 0, 0);
|
||
lv_obj_set_style_pad_all(panel_right, 8, 0);
|
||
lv_obj_set_scroll_dir(panel_right, LV_DIR_VER);
|
||
lv_obj_set_scrollbar_mode(panel_right, LV_SCROLLBAR_MODE_AUTO);
|
||
|
||
lbl = lv_label_create(panel_right);
|
||
lv_label_set_text(lbl, "ПИД регуляторы");
|
||
lv_obj_set_pos(lbl, 10, 5);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y = 35;
|
||
/* PWW PID - separate buttons for each parameter */
|
||
lbl = lv_label_create(panel_right);
|
||
lv_label_set_text(lbl, "PWW:");
|
||
lv_obj_set_pos(lbl, 10, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 25;
|
||
/* PWW Kp */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "Kp=%.2f", config.pid_pww_kp);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_pww_kp_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* PWW Ti */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 115, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "Ti=%.2f", config.pid_pww_ti);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_pww_ti_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* PWW D */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 230, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "D=%.2f", config.pid_pww_d);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_pww_d_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += 42;
|
||
/* KWS PID - separate buttons for each parameter */
|
||
lbl = lv_label_create(panel_right);
|
||
lv_label_set_text(lbl, "KWS:");
|
||
lv_obj_set_pos(lbl, 10, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 25;
|
||
/* KWS Kp */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "Kp=%.2f", config.pid_kws_kp);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_kws_kp_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* KWS Ti */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 115, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "Ti=%.2f", config.pid_kws_ti);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_kws_ti_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* KWS D */
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 230, y);
|
||
lv_obj_set_size(btn, 110, 32);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
snprintf(buf, sizeof(buf), "D=%.2f", config.pid_kws_d);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_pid_kws_d_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += 42;
|
||
lbl = lv_label_create(panel_right);
|
||
lv_label_set_text(lbl, "Режим работы");
|
||
lv_obj_set_pos(lbl, 10, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 30;
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Принуд. режим:");
|
||
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 10, 0);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, config.force_mode_enabled ? "ВКЛ" : "ВЫКЛ");
|
||
lv_obj_align(lbl, LV_ALIGN_RIGHT_MID, -10, 0);
|
||
lv_obj_set_style_text_color(lbl, config.force_mode_enabled ? lv_color_hex(COLOR_WARNING) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, toggle_force_mode_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += 40;
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Перемена ключа:");
|
||
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 10, 0);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, config.key_swap_enabled ? "ВКЛ" : "ВЫКЛ");
|
||
lv_obj_align(lbl, LV_ALIGN_RIGHT_MID, -10, 0);
|
||
lv_obj_set_style_text_color(lbl, config.key_swap_enabled ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, toggle_key_swap_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
y += 45;
|
||
lbl = lv_label_create(panel_right);
|
||
lv_label_set_text(lbl, "Дата и время");
|
||
lv_obj_set_pos(lbl, 10, y);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
|
||
y += 30;
|
||
btn = lv_btn_create(panel_right);
|
||
lv_obj_set_pos(btn, 0, y);
|
||
lv_obj_set_size(btn, 350, 36);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_BG_BTN), 0);
|
||
lv_obj_set_style_radius(btn, 4, 0);
|
||
lv_obj_set_style_pad_all(btn, 0, 0);
|
||
|
||
snprintf(buf, sizeof(buf), "%02d.%02d.%04d %02d:%02d",
|
||
state.day, state.month, state.year, state.hour, state.minute);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, buf);
|
||
lv_obj_center(lbl);
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_add_event_cb(btn, edit_datetime_cb, LV_EVENT_CLICKED, NULL);
|
||
|
||
/* Buttons */
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 200, 410);
|
||
lv_obj_set_size(btn, 120, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_ACCENT), 0);
|
||
lv_obj_set_style_radius(btn, 8, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Сохранить");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 340, 410);
|
||
lv_obj_set_size(btn, 140, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_WARNING), 0);
|
||
lv_obj_set_style_radius(btn, 8, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "По умолчанию");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
|
||
btn = lv_btn_create(scr);
|
||
lv_obj_set_pos(btn, 500, 410);
|
||
lv_obj_set_size(btn, 100, 40);
|
||
lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_TEXT_ACCENT), 0);
|
||
lv_obj_set_style_radius(btn, 8, 0);
|
||
lbl = lv_label_create(btn);
|
||
lv_label_set_text(lbl, "Меню");
|
||
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_font(lbl, &montserrat_16_ru_en, 0);
|
||
lv_obj_center(lbl);
|
||
lv_obj_add_event_cb(btn, btn_menu_cb, LV_EVENT_CLICKED, NULL);
|
||
}
|
||
|
||
void gui_update_values(void) {
|
||
char buf[64];
|
||
|
||
/* Update room climate */
|
||
if (lbl_temp_room) {
|
||
snprintf(buf, sizeof(buf), "%.1f°C", state.temp_room / 10.0f);
|
||
lv_label_set_text(lbl_temp_room, buf);
|
||
}
|
||
|
||
if (lbl_hum_room) {
|
||
snprintf(buf, sizeof(buf), "%.1f%%", state.hum_room / 10.0f);
|
||
lv_label_set_text(lbl_hum_room, buf);
|
||
}
|
||
|
||
/* Update channel climate */
|
||
if (lbl_temp_channel) {
|
||
snprintf(buf, sizeof(buf), "%.1f°C", state.temp_channel / 10.0f);
|
||
lv_label_set_text(lbl_temp_channel, buf);
|
||
}
|
||
|
||
if (lbl_hum_channel) {
|
||
snprintf(buf, sizeof(buf), "%.1f%%", state.hum_channel / 10.0f);
|
||
lv_label_set_text(lbl_hum_channel, buf);
|
||
}
|
||
|
||
/* Update system status */
|
||
if (lbl_room_num) {
|
||
snprintf(buf, sizeof(buf), "Комната: %d", state.room_num);
|
||
lv_label_set_text(lbl_room_num, buf);
|
||
}
|
||
|
||
if (lbl_master_slave) {
|
||
if (state.is_master) {
|
||
lv_label_set_text(lbl_master_slave, "Мастер");
|
||
lv_obj_set_style_text_color(lbl_master_slave, lv_color_hex(COLOR_ACCENT), 0);
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "Слейв %s", state.has_signal ? "(OK)" : "(НЕТ)");
|
||
lv_label_set_text(lbl_master_slave, buf);
|
||
lv_obj_set_style_text_color(lbl_master_slave,
|
||
state.has_signal ? lv_color_hex(COLOR_WARNING) : lv_color_hex(COLOR_DANGER), 0);
|
||
}
|
||
}
|
||
|
||
if (lbl_season) {
|
||
const char *seasons[] = {"Зима", "Лето", "Переход"};
|
||
const uint32_t colors[] = {COLOR_TEXT_ACCENT, COLOR_DANGER, COLOR_WARNING};
|
||
snprintf(buf, sizeof(buf), "Сезон: %s", seasons[state.season % 3]);
|
||
lv_label_set_text(lbl_season, buf);
|
||
lv_obj_set_style_text_color(lbl_season, lv_color_hex(colors[state.season % 3]), 0);
|
||
}
|
||
|
||
if (lbl_mode) {
|
||
const char *modes[] = {"Остановка", "Дежурный", "Рабочий"};
|
||
const uint32_t colors[] = {COLOR_DANGER, COLOR_WARNING, COLOR_ACCENT};
|
||
snprintf(buf, sizeof(buf), "Режим: %s", modes[state.mode % 3]);
|
||
lv_label_set_text(lbl_mode, buf);
|
||
lv_obj_set_style_text_color(lbl_mode, lv_color_hex(colors[state.mode % 3]), 0);
|
||
}
|
||
|
||
if (lbl_datetime) {
|
||
snprintf(buf, sizeof(buf), "%02d.%02d.%04d %02d:%02d",
|
||
state.day, state.month, state.year, state.hour, state.minute);
|
||
lv_label_set_text(lbl_datetime, buf);
|
||
}
|
||
|
||
/* Update equipment modules */
|
||
for (int i = 0; i < MODULE_COUNT; i++) {
|
||
update_module_display(i);
|
||
}
|
||
}
|
||
|
||
/* Update display of a single module */
|
||
static void update_module_display(int module_idx) {
|
||
if (module_idx < 0 || module_idx >= MODULE_COUNT) return;
|
||
if (!modules[module_idx].label_value) return;
|
||
|
||
char buf[32];
|
||
|
||
switch (module_idx) {
|
||
case MODULE_VALVE_INLET:
|
||
snprintf(buf, sizeof(buf), "%d%%", state.valve_inlet);
|
||
lv_label_set_text(modules[module_idx].label_value, buf);
|
||
break;
|
||
case MODULE_VALVE_RECIRC:
|
||
snprintf(buf, sizeof(buf), "%d%%", state.valve_recirc);
|
||
lv_label_set_text(modules[module_idx].label_value, buf);
|
||
break;
|
||
case MODULE_FILTER_G4:
|
||
lv_label_set_text(modules[module_idx].label_value,
|
||
state.filter_g4_clean ? "OK" : "!");
|
||
lv_obj_set_style_text_color(modules[module_idx].label_value,
|
||
state.filter_g4_clean ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_DANGER), 0);
|
||
break;
|
||
case MODULE_KWS:
|
||
snprintf(buf, sizeof(buf), "%d%%", state.kws_valve);
|
||
lv_label_set_text(modules[module_idx].label_value, buf);
|
||
break;
|
||
case MODULE_PWW:
|
||
snprintf(buf, sizeof(buf), "%d%%", state.pww_valve);
|
||
lv_label_set_text(modules[module_idx].label_value, buf);
|
||
break;
|
||
case MODULE_HEATER:
|
||
lv_label_set_text(modules[module_idx].label_value,
|
||
state.heater_on ? "ВКЛ" : "ВЫКЛ");
|
||
lv_obj_set_style_text_color(modules[module_idx].label_value,
|
||
state.heater_on ? lv_color_hex(COLOR_DANGER) : lv_color_hex(COLOR_TEXT_DIM), 0);
|
||
break;
|
||
case MODULE_FAN:
|
||
lv_label_set_text(modules[module_idx].label_value,
|
||
state.fan_on ? "ВКЛ" : "ВЫКЛ");
|
||
lv_obj_set_style_text_color(modules[module_idx].label_value,
|
||
state.fan_on ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_DANGER), 0);
|
||
break;
|
||
case MODULE_FILTER_H13:
|
||
lv_label_set_text(modules[module_idx].label_value,
|
||
state.filter_h13_clean ? "OK" : "!");
|
||
lv_obj_set_style_text_color(modules[module_idx].label_value,
|
||
state.filter_h13_clean ? lv_color_hex(COLOR_ACCENT) : lv_color_hex(COLOR_DANGER), 0);
|
||
break;
|
||
case MODULE_CHANNEL:
|
||
/* Display channel temperature and humidity */
|
||
snprintf(buf, sizeof(buf), "%.1f°\n%.0f%%", state.temp_channel / 10.0f, state.hum_channel / 10.0f);
|
||
lv_label_set_text(modules[module_idx].label_value, buf);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Refresh setpoints display on screen 3 */
|
||
static void refresh_setpoints_display(void) {
|
||
/* Remember which screen was active */
|
||
lv_obj_t *current_scr = lv_screen_active();
|
||
int was_on_screen3 = (current_scr == scr_screen3);
|
||
int was_on_screen4 = (current_scr == scr_screen4);
|
||
|
||
/* Recreate screen 3 to show updated values */
|
||
if (scr_screen3) {
|
||
lv_obj_del(scr_screen3);
|
||
scr_screen3 = NULL;
|
||
}
|
||
create_screen_screen3();
|
||
|
||
/* Recreate screen 4 to show updated values */
|
||
if (scr_screen4) {
|
||
lv_obj_del(scr_screen4);
|
||
scr_screen4 = NULL;
|
||
}
|
||
create_screen_config();
|
||
|
||
/* Load appropriate screen */
|
||
if (was_on_screen3) {
|
||
lv_scr_load(scr_screen3);
|
||
} else if (was_on_screen4) {
|
||
lv_scr_load(scr_screen4);
|
||
}
|
||
}
|
||
|
||
void gui_init(void) {
|
||
/* Add some example alarms to the log */
|
||
add_alarm_to_log("Система запущена", 0);
|
||
add_alarm_to_log("Фильтр G4 заменен", 0);
|
||
|
||
create_screen_main();
|
||
create_screen_screen1();
|
||
create_screen_screen2();
|
||
create_screen_screen3();
|
||
create_screen_config();
|
||
lv_scr_load(scr_main);
|
||
|
||
/* Проверка аварийных ситуаций после загрузки главного экрана */
|
||
if (!state.filter_g4_clean) {
|
||
create_popup_alarm("ФИЛЬТР G4 ЗАСОРИЛСЯ\n\nЗамените фильтр немедленно.\nСистема работает в аварийном режиме.");
|
||
state.mode = 0;
|
||
}
|
||
|
||
if (state.temp_channel < 50) {
|
||
create_popup_alarm("УГРОЗА ЗАМОРОЗКИ\n\nУстановка остановлена.\nПроверьте журнал и примите меры.");
|
||
state.mode = 0;
|
||
}
|
||
}
|
||
|
||
/* ── ModBus ──────────────────────────────────────── */
|
||
|
||
void gui_modbus_init(void) {
|
||
/* No-op for simulation */
|
||
}
|
||
|
||
void gui_modbus_task(void *arg) {
|
||
(void)arg;
|
||
/* Simulation: update with dummy values */
|
||
while (1) {
|
||
state.temp_room = 220 + (rand() % 40);
|
||
state.hum_room = 450 + (rand() % 100);
|
||
state.temp_channel = 200 + (rand() % 30);
|
||
state.hum_channel = 500 + (rand() % 80);
|
||
|
||
/* Simulate valve changes based on mode */
|
||
if (state.mode == 2) {
|
||
state.valve_inlet = 70 + (rand() % 30);
|
||
state.valve_recirc = 10 + (rand() % 20);
|
||
state.kws_valve = 40 + (rand() % 30);
|
||
state.pww_valve = 30 + (rand() % 25);
|
||
state.fan_on = 1;
|
||
} else if (state.mode == 1) {
|
||
state.valve_inlet = 30 + (rand() % 20);
|
||
state.valve_recirc = 40 + (rand() % 20);
|
||
state.kws_valve = 20 + (rand() % 15);
|
||
state.pww_valve = 15 + (rand() % 15);
|
||
state.fan_on = 1;
|
||
} else {
|
||
state.valve_inlet = 0;
|
||
state.valve_recirc = 0;
|
||
state.kws_valve = 0;
|
||
state.pww_valve = 0;
|
||
state.fan_on = 0;
|
||
state.heater_on = 0;
|
||
}
|
||
|
||
gui_update_values();
|
||
/* Sleep simulation - requires FreeRTOS or use SDL delay */
|
||
}
|
||
}
|