From 2a7b9e17bc3a57789b0c28a255ec55f646edfd6d Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Wed, 28 Oct 2026 14:06:56 +0300 Subject: [PATCH] wip: --- main/zh_sim_race_button_box.c | 350 +++++++++++++++------------------- main/zh_sim_race_button_box.h | 40 +++- 2 files changed, 193 insertions(+), 197 deletions(-) diff --git a/main/zh_sim_race_button_box.c b/main/zh_sim_race_button_box.c index f4dd92f..1d0d268 100755 --- a/main/zh_sim_race_button_box.c +++ b/main/zh_sim_race_button_box.c @@ -2,17 +2,18 @@ #include "freertos/semphr.h" #include "nvs.h" /* NEW */ -#define ENCODER_NUMBER 0x01 -#define LCD_VIEWPORT_ROWS 3 -#define LCD_COLS 16 -#define LCD_TOTAL_ROWS 4 +// Хэндлы - функции - переменные +// #define ENCODER_NUMBER 0x01 +// #define LCD_VIEWPORT_ROWS 3 +// #define LCD_TOTAL_COLS 16 +// #define LCD_TOTAL_ROWS 4 #define BOOT_SPLASH_MS 5000 #define MAIN_SCREEN_COUNT 5 #define LOAD_SCREEN_COUNT 5 -#define NVS_NAMESPACE "sim_race_box" /* NEW */ -#define NVS_KEY_SETTINGS "settings" /* NEW */ +// #define NVS_NAMESPACE "sim_race_box" /* NEW */ +// #define NVS_KEY_SETTINGS "settings" /* NEW */ static const char *TAG = "sim_race_box"; @@ -26,20 +27,20 @@ int16_t main_debug_data[VIEW_CELLS] = {45, -42, 152, 12, 0}; int16_t main_blank_data[VIEW_CELLS] = {0, 0, 0, 0, 0}; /* ---------- Дескрипторы экранов ---------- */ -typedef struct -{ - const char *name; - const char *const labels[VIEW_ROWS]; - int16_t *data; -} main_screen_desc_t; +// typedef struct +// { +// const char *name; +// const char *const labels[VIEW_ROWS]; +// int16_t *data; +// } main_screen_desc_t; -typedef struct -{ - const char *name; - const char *const lines[VIEW_ROWS]; -} load_screen_desc_t; +// typedef struct +// { +// const char *name; +// const char *const lines[VIEW_ROWS]; +// } load_screen_desc_t; -static const main_screen_desc_t main_screens[MAIN_SCREEN_COUNT] = { +static const zh_main_screen_description_t main_screens[MAIN_SCREEN_COUNT] = { {"Race", {"SPD", "RPM", "GER", "LAP"}, main_race_data}, {"Engine", {"OIL", "WTR", "BAT", "FUL"}, main_engine_data}, {"Times", {"LAP", "BST", "LST", "DLT"}, main_times_data}, @@ -47,7 +48,7 @@ static const main_screen_desc_t main_screens[MAIN_SCREEN_COUNT] = { {"Blank", {"---", "---", "---", "---"}, main_blank_data}, }; -static const load_screen_desc_t load_screens[LOAD_SCREEN_COUNT] = { +static const zh_boot_screen_description_t load_screens[LOAD_SCREEN_COUNT] = { {"Boot", {"Booting...", "RAM: OK", "I2C: OK", "Starting..."}}, {"Version", {"FW v1.0", "Build 2025-10", "ESP-IDF v5.4", "zh_menu v1.2"}}, {"Diag", {"I2C: OK", "LCD: OK", "ENC: OK", "All: OK"}}, @@ -71,116 +72,82 @@ char lbl_pw_ch_2[MENU_LABEL_LEN] = "PW CH 2 100%"; char lbl_pw_ch_3[MENU_LABEL_LEN] = "PW CH 3 100%"; char lbl_pw_ch_4[MENU_LABEL_LEN] = "PW CH 4 100%"; -int16_t g_temp_on = 15; -int16_t g_temp_off = 35; -uint8_t g_pw_ch[4] = {100, 100, 100, 100}; - /* ---------- Handle'ы ---------- */ zh_encoder_handle_t *encoder_handle = NULL; zh_menu_handle_t *menu_handle = NULL; zh_pcf8574_handle_t *pcf8574_handle = NULL; zh_160x_i2c_handle_t *lcd_handle = NULL; -/* ---------- Состояние меню ---------- */ -static zh_menu_node_t *_last_node = NULL; -static uint8_t _last_index = 0; -static zh_menu_node_t *_cached_node = NULL; - -/* ---------- Кэш строк LCD ---------- */ -static char _row_cache[LCD_TOTAL_ROWS][LCD_COLS]; -static bool _row_valid[LCD_TOTAL_ROWS] = {false, false, false, false}; - /* ---------- Мьютекс LCD ---------- */ static SemaphoreHandle_t _lcd_mutex = NULL; -void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data); +static void zh_load_settings(void); +static void zh_save_settings(void); -/* ============================================================ - * NEW: NVS blob — все сохраняемые поля в одной структуре - * ============================================================ */ -typedef struct -{ - int16_t temp_on; - int16_t temp_off; - uint8_t pw_ch[4]; - uint8_t boot_screen; - uint8_t main_screen; -} settings_blob_t; +void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); -/* ============================================================ - * NEW: загрузка настроек из NVS. - * Безопасно к отсутствию записи, к несовпадению размера blob - * и к устаревшим индексам экранов (выходящим за границы). - * ============================================================ */ -static void _load_settings(void) +/* ---------- Состояние меню ---------- */ +static zh_menu_node_t *last_node = NULL; +static uint8_t last_index = 0; +static zh_menu_node_t *cached_node = NULL; + +/* ---------- Кэш строк LCD ---------- */ +static char row_cache[LCD_TOTAL_ROWS][LCD_TOTAL_COLS]; +static bool row_valid[LCD_TOTAL_ROWS] = {false, false, false, false}; + +volatile static uint8_t air_condition_temp_on = 25; +volatile static uint8_t air_condition_temp_off = 20; +volatile static uint8_t exit_channel_power[4] = {100, 100, 100, 100}; +volatile static uint8_t bulb_brightness = 100; +volatile static uint8_t boot_screen_number = 0; +volatile static uint8_t main_screen_number = 0; + +static void zh_load_settings(void) { nvs_handle_t nvs; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs); - if (err != ESP_OK) + if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK) { - ESP_LOGW(TAG, "No saved settings (%s), using defaults", esp_err_to_name(err)); return; } - - settings_blob_t blob; - size_t size = sizeof(blob); - err = nvs_get_blob(nvs, NVS_KEY_SETTINGS, &blob, &size); + zh_nvs_setting_data_blob_t setting_data_blob = {0}; + uint8_t size = sizeof(setting_data_blob); + if (nvs_get_blob(nvs, NVS_KEY_SETTINGS, &setting_data_blob, (size_t *)&size) != ESP_OK) + { + nvs_close(nvs); + return; + } nvs_close(nvs); - - if (err != ESP_OK || size != sizeof(blob)) - { - ESP_LOGW(TAG, "Settings blob missing or size mismatch (%s)", esp_err_to_name(err)); - return; - } - - /* Защита от устаревших индексов после обновления прошивки */ - if (blob.temp_on >= 0 && blob.temp_on <= 100) - g_temp_on = blob.temp_on; - if (blob.temp_off >= 0 && blob.temp_off <= 100) - g_temp_off = blob.temp_off; - memcpy(g_pw_ch, blob.pw_ch, sizeof(g_pw_ch)); - - if (blob.boot_screen < LOAD_SCREEN_COUNT) - _boot_screen = blob.boot_screen; - if (blob.main_screen < MAIN_SCREEN_COUNT) - _main_screen = blob.main_screen; - - ESP_LOGI(TAG, "Settings loaded: temp_on=%d temp_off=%d pw=[%u,%u,%u,%u] boot=%d main=%d", - g_temp_on, g_temp_off, g_pw_ch[0], g_pw_ch[1], g_pw_ch[2], g_pw_ch[3], - _boot_screen, _main_screen); + air_condition_temp_on = setting_data_blob.air_condition_temp_on; + air_condition_temp_off = setting_data_blob.air_condition_temp_off; + exit_channel_power[0] = setting_data_blob.exit_channel_power[0]; + exit_channel_power[1] = setting_data_blob.exit_channel_power[1]; + exit_channel_power[2] = setting_data_blob.exit_channel_power[2]; + exit_channel_power[3] = setting_data_blob.exit_channel_power[3]; + bulb_brightness = setting_data_blob.bulb_brightness; + boot_screen_number = setting_data_blob.boot_screen_number; + main_screen_number = setting_data_blob.main_screen_number; } -/* ============================================================ - * NEW: сохранение настроек в NVS (единый blob, атомарно). - * ============================================================ */ -static void _save_settings(void) +static void zh_save_settings(void) { - nvs_handle_t nvs; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs); - if (err != ESP_OK) + nvs_handle_t nvs = 0; + if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs) != ESP_OK) { - ESP_LOGE(TAG, "nvs_open RW failed: %s", esp_err_to_name(err)); return; } - - settings_blob_t blob = { - .temp_on = g_temp_on, - .temp_off = g_temp_off, - .boot_screen = (uint8_t)_boot_screen, - .main_screen = (uint8_t)_main_screen, - }; - memcpy(blob.pw_ch, g_pw_ch, sizeof(g_pw_ch)); - - err = nvs_set_blob(nvs, NVS_KEY_SETTINGS, &blob, sizeof(blob)); - if (err == ESP_OK) - err = nvs_commit(nvs); + zh_nvs_setting_data_blob_t setting_data_blob = {0}; + setting_data_blob.air_condition_temp_on = air_condition_temp_on; + setting_data_blob.air_condition_temp_off = air_condition_temp_off; + setting_data_blob.exit_channel_power[0] = exit_channel_power[0]; + setting_data_blob.exit_channel_power[1] = exit_channel_power[1]; + setting_data_blob.exit_channel_power[2] = exit_channel_power[2]; + setting_data_blob.exit_channel_power[3] = exit_channel_power[3]; + setting_data_blob.bulb_brightness = bulb_brightness; + setting_data_blob.boot_screen_number = boot_screen_number; + setting_data_blob.main_screen_number = main_screen_number; + nvs_set_blob(nvs, NVS_KEY_SETTINGS, &setting_data_blob, sizeof(setting_data_blob)); + nvs_commit(nvs); nvs_close(nvs); - - if (err != ESP_OK) - ESP_LOGE(TAG, "Failed to save settings: %s", esp_err_to_name(err)); - else - ESP_LOGI(TAG, "Settings saved"); } /* ---------- Утилиты ---------- */ @@ -194,20 +161,20 @@ static inline uint8_t _clamp_u8(int16_t v, uint8_t lo, uint8_t hi) return v < lo ? lo : (v > hi ? hi : v); } -static void _invalidate_row_cache(void) +static void _invalidaterow_cache(void) { - memset(_row_valid, 0, sizeof(_row_valid)); + memset(row_valid, 0, sizeof(row_valid)); } /* ---------- Форматирование ---------- */ static void _format_row(char *buf, const char *label, bool selected, bool edit, bool arrow_up, bool arrow_down) { - memset(buf, ' ', LCD_COLS); - buf[LCD_COLS] = '\0'; + memset(buf, ' ', LCD_TOTAL_COLS); + buf[LCD_TOTAL_COLS] = '\0'; buf[0] = edit ? '*' : (selected ? '>' : ' '); - uint8_t text_cols = (arrow_up || arrow_down) ? (LCD_COLS - 2) : (LCD_COLS - 1); + uint8_t text_cols = (arrow_up || arrow_down) ? (LCD_TOTAL_COLS - 2) : (LCD_TOTAL_COLS - 1); size_t len = strlen(label); if (len > text_cols) len = text_cols; @@ -215,73 +182,77 @@ static void _format_row(char *buf, const char *label, bool selected, bool edit, if (arrow_up) { - buf[LCD_COLS - 1] = '^'; + buf[LCD_TOTAL_COLS - 1] = '^'; } else if (arrow_down) { - buf[LCD_COLS - 1] = 'v'; + buf[LCD_TOTAL_COLS - 1] = 'v'; } } static void _format_main_row(char *buf, const char *label, int16_t value) { - memset(buf, ' ', LCD_COLS); - buf[LCD_COLS] = '\0'; - snprintf(buf, LCD_COLS + 1, "%3s:%5d", label, (int)value); + snprintf(buf, LCD_TOTAL_COLS + 1, "%3.3s:%5d", label, (int)value); + // memset(buf, ' ', LCD_TOTAL_COLS); + // buf[LCD_TOTAL_COLS] = '\0'; + // snprintf(buf, LCD_TOTAL_COLS + 1, "%3s:%5d", label, (int)value); } static void _format_load_row(char *buf, const char *line) { - memset(buf, ' ', LCD_COLS); - buf[LCD_COLS] = '\0'; - size_t len = strlen(line); - if (len > LCD_COLS) - len = LCD_COLS; - memcpy(buf, line, len); + snprintf(buf, LCD_TOTAL_COLS + 1, "%-*.*s", LCD_TOTAL_COLS, LCD_TOTAL_COLS, line); + // memset(buf, ' ', LCD_TOTAL_COLS); + // buf[LCD_TOTAL_COLS] = '\0'; + // size_t len = strlen(line); + // if (len > LCD_TOTAL_COLS) + // len = LCD_TOTAL_COLS; + // memcpy(buf, line, len); } static void _lcd_put_row(uint8_t row, const char *text) { - if (_row_valid[row] && memcmp(_row_cache[row], text, LCD_COLS) == 0) + if (row_valid[row] && memcmp(row_cache[row], text, LCD_TOTAL_COLS) == 0) return; zh_160x_set_cursor(&lcd_handle, row, 0); zh_160x_print_char(&lcd_handle, text); - memcpy(_row_cache[row], text, LCD_COLS); - _row_valid[row] = true; + memcpy(row_cache[row], text, LCD_TOTAL_COLS); + row_valid[row] = true; } /* ---------- Точечное обновление меток ---------- */ static void _refresh_dynamic_labels(zh_menu_node_t *node) { if (node == NULL || node->submenu == NULL) + { return; + } for (uint8_t i = 0; i < node->item_numbers; ++i) { const char *lbl = node->submenu[i].label; if (lbl == lbl_temp_on) { - snprintf(lbl_temp_on, sizeof(lbl_temp_on), "Temp ON %2dC", (int)g_temp_on); + snprintf(lbl_temp_on, sizeof(lbl_temp_on), "Temp ON %2dC", (int)air_condition_temp_on); } else if (lbl == lbl_temp_off) { - snprintf(lbl_temp_off, sizeof(lbl_temp_off), "Temp OFF %2dC", (int)g_temp_off); + snprintf(lbl_temp_off, sizeof(lbl_temp_off), "Temp OFF %2dC", (int)air_condition_temp_off); } else if (lbl == lbl_pw_ch_1) { - snprintf(lbl_pw_ch_1, sizeof(lbl_pw_ch_1), "PW CH 1 %3d%%", (int)g_pw_ch[0]); + snprintf(lbl_pw_ch_1, sizeof(lbl_pw_ch_1), "PW CH 1 %3d%%", (int)exit_channel_power[0]); } else if (lbl == lbl_pw_ch_2) { - snprintf(lbl_pw_ch_2, sizeof(lbl_pw_ch_2), "PW CH 2 %3d%%", (int)g_pw_ch[1]); + snprintf(lbl_pw_ch_2, sizeof(lbl_pw_ch_2), "PW CH 2 %3d%%", (int)exit_channel_power[1]); } else if (lbl == lbl_pw_ch_3) { - snprintf(lbl_pw_ch_3, sizeof(lbl_pw_ch_3), "PW CH 3 %3d%%", (int)g_pw_ch[2]); + snprintf(lbl_pw_ch_3, sizeof(lbl_pw_ch_3), "PW CH 3 %3d%%", (int)exit_channel_power[2]); } else if (lbl == lbl_pw_ch_4) { - snprintf(lbl_pw_ch_4, sizeof(lbl_pw_ch_4), "PW CH 4 %3d%%", (int)g_pw_ch[3]); + snprintf(lbl_pw_ch_4, sizeof(lbl_pw_ch_4), "PW CH 4 %3d%%", (int)exit_channel_power[3]); } else if (lbl == lbl_boot_screen) { @@ -297,34 +268,33 @@ static void _refresh_dynamic_labels(zh_menu_node_t *node) /* ---------- Отрисовка экранов ---------- */ static void _draw_main_screen(void) { - const main_screen_desc_t *s = &main_screens[_main_screen]; - for (uint8_t i = 0; i < VIEW_ROWS; ++i) + const zh_main_screen_description_t *s = &main_screens[_main_screen]; + for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++i) { - char buf[LCD_COLS + 1]; - _format_main_row(buf, s->labels[i], s->data[i]); + char buf[LCD_TOTAL_COLS + 1]; + _format_main_row(buf, s->lines[i], s->data[i]); _lcd_put_row(i, buf); } } static void _draw_load_screen(void) { - const load_screen_desc_t *s = &load_screens[_boot_screen]; - for (uint8_t i = 0; i < VIEW_ROWS; ++i) + const zh_boot_screen_description_t *s = &load_screens[_boot_screen]; + for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++i) { - char buf[LCD_COLS + 1]; + char buf[LCD_TOTAL_COLS + 1]; _format_load_row(buf, s->lines[i]); _lcd_put_row(i, buf); } } /* ---------- Отрисовка меню ---------- */ -static void _draw_menu(zh_menu_node_t *current_node, uint8_t current_index, - const zh_menu_status_t *status) +static void _draw_menu(zh_menu_node_t *current_node, uint8_t current_index, const zh_menu_status_t *status) { - if (current_node != _cached_node) + if (current_node != cached_node) { - _invalidate_row_cache(); - _cached_node = current_node; + _invalidaterow_cache(); + cached_node = current_node; } _refresh_dynamic_labels(current_node); @@ -332,35 +302,37 @@ static void _draw_menu(zh_menu_node_t *current_node, uint8_t current_index, zh_menu_view_t view = {0}; zh_menu_get_view(&menu_handle, &view); - char header[LCD_COLS + 1]; - memset(header, ' ', LCD_COLS); - header[LCD_COLS] = '\0'; - size_t hlen = strlen(current_node->label); - if (hlen > LCD_COLS) - hlen = LCD_COLS; - memcpy(header, current_node->label, hlen); + char header[LCD_TOTAL_COLS + 1]; + // memset(header, ' ', LCD_TOTAL_COLS); + // header[LCD_TOTAL_COLS] = '\0'; + // size_t hlen = strlen(current_node->label); + // if (hlen > LCD_TOTAL_COLS) + // { + // hlen = LCD_TOTAL_COLS; + // } + // memcpy(header, current_node->label, hlen); + + snprintf(header, sizeof(header), "%-*.*s", LCD_TOTAL_COLS, LCD_TOTAL_COLS, current_node->label); + _lcd_put_row(0, header); - for (uint8_t i = 0; i < LCD_VIEWPORT_ROWS; ++i) + for (uint8_t i = 0; i < LCD_TOTAL_ROWS - 1; ++i) { - char buf[LCD_COLS + 1]; + char buf[LCD_TOTAL_COLS + 1]; if (i < view.visible_count) { uint8_t idx = view.first_visible + i; bool is_cursor = (idx == current_index); - bool is_edit = is_cursor && status->is_selected && - current_node->submenu[idx].selectable; + bool is_edit = is_cursor && status->is_selected && current_node->submenu[idx].selectable; bool arrow_up = (i == 0) && (view.first_visible > 0); - bool arrow_down = (i == (view.visible_count - 1)) && - ((uint8_t)(view.first_visible + view.visible_count) < current_node->item_numbers); - _format_row(buf, current_node->submenu[idx].label, - is_cursor, is_edit, arrow_up, arrow_down); + bool arrow_down = (i == (view.visible_count - 1)) && ((uint8_t)(view.first_visible + view.visible_count) < current_node->item_numbers); + _format_row(buf, current_node->submenu[idx].label, is_cursor, is_edit, arrow_up, arrow_down); } else { - memset(buf, ' ', LCD_COLS); - buf[LCD_COLS] = '\0'; + memset(buf, ' ', LCD_TOTAL_COLS); + buf[LCD_TOTAL_COLS] = '\0'; } _lcd_put_row((uint8_t)(i + 1), buf); @@ -380,7 +352,7 @@ static void _boot_task(void *pv) { xSemaphoreTake(_lcd_mutex, portMAX_DELAY); zh_160x_lcd_clear(&lcd_handle); /* NEW: очистка перед первым main view */ - _invalidate_row_cache(); + _invalidaterow_cache(); _draw_main_screen(); xSemaphoreGive(_lcd_mutex); } @@ -405,11 +377,10 @@ void app_main(void) } ESP_ERROR_CHECK(nvs_err); - _load_settings(); /* до отрисовки splash и до чтения _boot_screen */ + zh_load_settings(); esp_event_loop_create_default(); - esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, - &zh_encoder_event_handler, NULL, NULL); + esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL); /* ---------- Encoder ---------- */ zh_encoder_init_config_t encoder_config = ZH_ENCODER_INIT_CONFIG_DEFAULT(); @@ -456,7 +427,7 @@ void app_main(void) menu_config.queue_size = 5; menu_config.root = &main_menu; menu_config.loop = false; - menu_config.viewport_rows = LCD_VIEWPORT_ROWS; + menu_config.viewport_rows = LCD_TOTAL_ROWS - 1; menu_config.enter_cb = &zh_menu_enter_main_menu_callback; menu_config.exit_cb = &zh_menu_exit_main_menu_callback; menu_config.change_cb = &zh_menu_change_menu_callback; @@ -465,14 +436,13 @@ void app_main(void) /* ---------- Splash: сохранённый load-экран ---------- */ _boot_done = false; _prev_selected = false; - _invalidate_row_cache(); + _invalidaterow_cache(); _draw_load_screen(); xTaskCreate(_boot_task, "boot", 3072, NULL, 3, NULL); } /* ---------- Encoder event handler ---------- */ -void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) +void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { (void)arg; zh_menu_status_t menu_status = {0}; @@ -484,9 +454,8 @@ void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, zh_encoder_button_event_on_isr_t *button_event = event_data; if (button_event->button_status == true) { - menu_status.is_active == false ? zh_menu_set(&menu_handle, ZH_MENU_ENTER, NULL, 0) - : menu_status.is_selected == false ? zh_menu_set(&menu_handle, ZH_MENU_SELECT, NULL, 0) - : zh_menu_set(&menu_handle, ZH_MENU_BACK, NULL, 0); + menu_status.is_active == false ? zh_menu_set(&menu_handle, ZH_MENU_ENTER, NULL, 0) : menu_status.is_selected == false ? zh_menu_set(&menu_handle, ZH_MENU_SELECT, NULL, 0) + : zh_menu_set(&menu_handle, ZH_MENU_BACK, NULL, 0); } break; } @@ -497,14 +466,11 @@ void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, { if (menu_status.is_selected == true) { - zh_menu_set(&menu_handle, ZH_MENU_SELECT, encoder_event, - sizeof(zh_encoder_event_on_isr_t)); + zh_menu_set(&menu_handle, ZH_MENU_SELECT, encoder_event, sizeof(zh_encoder_event_on_isr_t)); } else { - encoder_event->encoder_status == true - ? zh_menu_set(&menu_handle, ZH_MENU_UP, NULL, 0) - : zh_menu_set(&menu_handle, ZH_MENU_DOWN, NULL, 0); + encoder_event->encoder_status == true ? zh_menu_set(&menu_handle, ZH_MENU_UP, NULL, 0) : zh_menu_set(&menu_handle, ZH_MENU_DOWN, NULL, 0); } } break; @@ -519,8 +485,8 @@ void zh_menu_enter_main_menu_callback(void *arg) { (void)arg; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - _cached_node = NULL; - _invalidate_row_cache(); + cached_node = NULL; + _invalidaterow_cache(); xSemaphoreGive(_lcd_mutex); } @@ -528,24 +494,24 @@ void zh_menu_exit_main_menu_callback(void *arg) { (void)arg; /* NEW: страховочное сохранение при выходе из меню */ - _save_settings(); + zh_save_settings(); _prev_selected = false; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); zh_160x_lcd_clear(&lcd_handle); - _invalidate_row_cache(); + _invalidaterow_cache(); if (_boot_done) _draw_main_screen(); else _draw_load_screen(); - _cached_node = NULL; + cached_node = NULL; xSemaphoreGive(_lcd_mutex); } void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index) { - _last_node = current_node; - _last_index = current_index; + last_node = current_node; + last_index = current_index; zh_menu_status_t status = {0}; zh_menu_get(&menu_handle, &status); @@ -553,7 +519,7 @@ void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_ /* NEW: сохраняем при выходе из режима редактирования (BACK) */ if (_prev_selected && !status.is_selected) { - _save_settings(); + zh_save_settings(); } _prev_selected = status.is_selected; @@ -565,12 +531,12 @@ void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_ /* ---------- Selectable: Temp / PW ---------- */ void zh_menu_selectable_item_callback(void *arg) { - if (_last_node == NULL || _last_node->submenu == NULL) + if (last_node == NULL || last_node->submenu == NULL) return; - if (_last_index >= _last_node->item_numbers) + if (last_index >= last_node->item_numbers) return; - zh_menu_node_t *item = &_last_node->submenu[_last_index]; + zh_menu_node_t *item = &last_node->submenu[last_index]; if (arg == NULL) { @@ -584,32 +550,32 @@ void zh_menu_selectable_item_callback(void *arg) if (item->label == lbl_temp_on) { - g_temp_on = _clamp_i16(g_temp_on + dir, 10, 50); + air_condition_temp_on = _clamp_i16(air_condition_temp_on + dir, 15, 30); changed = true; } else if (item->label == lbl_temp_off) { - g_temp_off = _clamp_i16(g_temp_off + dir, 10, 50); + air_condition_temp_off = _clamp_i16(air_condition_temp_off + dir, 15, 30); changed = true; } else if (item->label == lbl_pw_ch_1) { - g_pw_ch[0] = _clamp_u8(g_pw_ch[0] + dir * 5, 0, 100); + exit_channel_power[0] = _clamp_u8(exit_channel_power[0] + dir * 5, 0, 100); changed = true; } else if (item->label == lbl_pw_ch_2) { - g_pw_ch[1] = _clamp_u8(g_pw_ch[1] + dir * 5, 0, 100); + exit_channel_power[1] = _clamp_u8(exit_channel_power[1] + dir * 5, 0, 100); changed = true; } else if (item->label == lbl_pw_ch_3) { - g_pw_ch[2] = _clamp_u8(g_pw_ch[2] + dir * 5, 0, 100); + exit_channel_power[2] = _clamp_u8(exit_channel_power[2] + dir * 5, 0, 100); changed = true; } else if (item->label == lbl_pw_ch_4) { - g_pw_ch[3] = _clamp_u8(g_pw_ch[3] + dir * 5, 0, 100); + exit_channel_power[3] = _clamp_u8(exit_channel_power[3] + dir * 5, 0, 100); changed = true; } diff --git a/main/zh_sim_race_button_box.h b/main/zh_sim_race_button_box.h index b8bcb6f..79b2fde 100644 --- a/main/zh_sim_race_button_box.h +++ b/main/zh_sim_race_button_box.h @@ -7,14 +7,21 @@ #include "zh_encoder.h" #include "zh_ota_server.h" -#define ENCODER_NUMBER 0x01 -#define MENU_VIEWPORT_ROWS 3 +#define ENCODER_NUMBER 1 + +#define LCD_TOTAL_ROWS 4 +#define LCD_TOTAL_COLS 16 +// #define LCD_VIEWPORT_ROWS (LCD_TOTAL_ROWS - 1) + #define MENU_LABEL_LEN 17 /* ---------- Размеры view ---------- */ #define VIEW_CELLS 5 /* ячеек в массиве данных (main) */ #define VIEW_ROWS 4 /* строк отрисовывается на LCD */ +#define NVS_NAMESPACE "sim_race_box" /* NEW */ +#define NVS_KEY_SETTINGS "settings" /* NEW */ + /* ---------- Пул основных экранов (idle) — 5 массивов ---------- */ extern int16_t main_race_data[VIEW_CELLS]; extern int16_t main_engine_data[VIEW_CELLS]; @@ -33,9 +40,9 @@ extern char lbl_boot_screen[MENU_LABEL_LEN]; /* "B: <имя load-экрана>" extern char lbl_main_screen[MENU_LABEL_LEN]; /* "M: <имя main-экрана>" */ /* ---------- Переменные значений ---------- */ -extern int16_t g_temp_on; -extern int16_t g_temp_off; -extern uint8_t g_pw_ch[4]; +// extern int16_t g_temp_on; +// extern int16_t g_temp_off; +// extern uint8_t g_pw_ch[4]; /* ---------- Callbacks ---------- */ void zh_menu_enter_main_menu_callback(void *arg); @@ -52,6 +59,29 @@ extern "C" { #endif + typedef struct + { + uint8_t air_condition_temp_on; + uint8_t air_condition_temp_off; + uint8_t exit_channel_power[4]; + uint8_t bulb_brightness; + uint8_t boot_screen_number; + uint8_t main_screen_number; + } zh_nvs_setting_data_blob_t; + + typedef struct + { + const char *name; + const char *const lines[LCD_TOTAL_ROWS]; + int16_t *data; + } zh_main_screen_description_t; + + typedef struct + { + const char *name; + const char *const lines[LCD_TOTAL_ROWS]; + } zh_boot_screen_description_t; + zh_menu_node_t main_menu = { .label = "Settings", .submenu = (zh_menu_node_t[]){