diff --git a/main/zh_sim_race_button_box.c b/main/zh_sim_race_button_box.c index 6dcc389..0893fb2 100755 --- a/main/zh_sim_race_button_box.c +++ b/main/zh_sim_race_button_box.c @@ -2,6 +2,19 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define VALUE_CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : ((v) > (hi) ? (hi) : (v))) +#define CLEAR_ROW_CACHE() memset(_row_valid, 0, sizeof(_row_valid)) + +#define VIEW_CELLS 5 + +#define LABEL_BOOT_SCREEN "Boot: " +#define LABEL_MAIN_SCREEN "Main: " +#define LABEL_AIR_CONDITION_TEMP_ON "Temp ON: " +#define LABEL_AIR_CONDITION_TEMP_OFF "Temp OFF: " +#define LABEL_EXIT_CHANNEL_1_POWER "PW CH 1: " +#define LABEL_EXIT_CHANNEL_2_POWER "PW CH 2: " +#define LABEL_EXIT_CHANNEL_3_POWER "PW CH 3: " +#define LABEL_EXIT_CHANNEL_4_POWER "PW CH 4: " +#define LABEL_BULB_BRIGHTNESS "PW BULB: " typedef struct { @@ -13,7 +26,18 @@ typedef struct uint8_t main_screen_number; } _zh_nvs_setting_data_blob_t; -// Хэндлы - функции - переменные +typedef struct +{ + const char *name; + const char *const lines[LCD_TOTAL_ROWS]; + char *values[LCD_TOTAL_ROWS]; +} _zh_main_screen_description_t; + +typedef struct +{ + const char *name; + const char *const lines[LCD_TOTAL_ROWS]; +} _zh_boot_screen_description_t; static httpd_handle_t _http_server_handle = NULL; static zh_encoder_handle_t *_mode_encoder_handle = NULL; @@ -21,7 +45,7 @@ static zh_menu_handle_t *_menu_handle = NULL; // static zh_pcf8574_handle_t *_button_handle = NULL; static zh_pcf8574_handle_t *_lcd_handle = NULL; static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL; -// static TaskHandle_t _stats_task_handle = NULL; +static TaskHandle_t _stats_task_handle = NULL; static void _zh_load_settings(void); static void _zh_save_settings(void); @@ -34,331 +58,111 @@ static void _zh_system_load(void); static void _zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void _zh_stats_processing_task(void *pvParameter); -void zh_menu_enter_main_menu_callback(void *arg); -void zh_menu_exit_main_menu_callback(void *arg); -void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index); -void zh_menu_selectable_item_callback(void *arg); -void zh_menu_back_command_callback(void *arg); -void zh_menu_boot_screen_callback(void *arg); -void zh_menu_main_screen_callback(void *arg); +static void _zh_menu_enter_main_menu_callback(void *arg); +static void _zh_menu_exit_main_menu_callback(void *arg); +static void _zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index); +static void _zh_menu_selectable_item_callback(void *arg); +static void _zh_menu_back_command_callback(void *arg); +static void _zh_menu_boot_screen_callback(void *arg); +static void _zh_menu_main_screen_callback(void *arg); +static void _zh_draw_main_screen(void); +static void _zh_draw_boot_screen(void); +static void _zh_draw_main_menu(zh_menu_node_t *current_node, uint8_t current_index, const zh_menu_status_t *status); -// #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 */ - -// static const char *TAG = "sim_race_box"; - -/* ============================================================ - * Данные main-экранов (idle) - * ============================================================ */ -int16_t main_race_data[VIEW_CELLS] = {185, 7500, 4, 234, 0}; -int16_t main_engine_data[VIEW_CELLS] = {78, 82, 12, 45, 0}; -int16_t main_times_data[VIEW_CELLS] = {1234, 1201, 1250, -49, 0}; -int16_t main_debug_data[VIEW_CELLS] = {45, -42, 152, 12, 0}; -int16_t main_blank_data[VIEW_CELLS] = {0, 0, 0, 0, 0}; - -static const zh_main_screen_description_t main_screens[] = { - {"Race", {"SPD", "RPM", "GER", "LAP"}, main_race_data}, - {"Engine", {"OIL", "WTR", "BAT", "FUL"}, main_engine_data}, - {"Times", {"LAP", "BST", "LST", "DLT"}, main_times_data}, - {"Debug", {"HEP", "SIG", "UPT", "CPU"}, main_debug_data}, - {"Blank", {"---", "---", "---", "---"}, main_blank_data}, +static const _zh_boot_screen_description_t _boot_screens[] = { + {"Boot", {"Booting...", "RAM: OK", "I2C: OK", "Starting..."}}, + {"Version", {"FW v1.0", "Build 2025-10", "ESP-IDF v5.4", ""}}, + {"Diag", {"I2C: OK", "LCD: OK", "ENC: OK", "All: OK"}}, + {"Ready", {"System ready", "Ready for race", "Let's go", ""}}, + {"Blank", {"---", "---", "---", "---"}}, }; -static bool _boot_done = false; -static bool _prev_selected = false; /* NEW: для детекции выхода из edit */ +static char _cabin_temp_status[5] = "23C"; +static char _air_condition_status[5] = "AUTO"; +static char _side_light_status[5] = "ON"; +static char _flash_light_status[5] = "OFF"; -/* ---------- Динамические буферы меток ---------- */ -// char lbl_boot_screen[LCD_TOTAL_COLS + 1] = "B:Boot"; -// char lbl_main_screen[LCD_TOTAL_COLS + 1] = "M:Race"; -// char lbl_temp_on[LCD_TOTAL_COLS + 1] = "Temp ON 15C"; -// char lbl_temp_off[LCD_TOTAL_COLS + 1] = "Temp OFF 35C"; -// char lbl_pw_ch_1[LCD_TOTAL_COLS + 1] = "PW CH 1 100%"; -// char lbl_pw_ch_2[LCD_TOTAL_COLS + 1] = "PW CH 2 100%"; -// char lbl_pw_ch_3[LCD_TOTAL_COLS + 1] = "PW CH 3 100%"; -// char lbl_pw_ch_4[LCD_TOTAL_COLS + 1] = "PW CH 4 100%"; +static _zh_main_screen_description_t _main_screens[] = { + {"Race", {"CABIN TEMP:", "AIR COND:", "SIDE LT:", "FLASH LT:"}, {_cabin_temp_status, _air_condition_status, _side_light_status, _flash_light_status}}, +}; -/* ---------- 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 char _label_boot_screen[LCD_TOTAL_COLS + 1] = LABEL_BOOT_SCREEN; +static char _label_main_screen[LCD_TOTAL_COLS + 1] = LABEL_MAIN_SCREEN; +static char _label_air_condition_temp_on[LCD_TOTAL_COLS + 1] = LABEL_AIR_CONDITION_TEMP_ON; +static char _label_air_condition_temp_off[LCD_TOTAL_COLS + 1] = LABEL_AIR_CONDITION_TEMP_OFF; +static char _label_exit_channel_1_power[LCD_TOTAL_COLS + 1] = LABEL_EXIT_CHANNEL_1_POWER; +static char _label_exit_channel_2_power[LCD_TOTAL_COLS + 1] = LABEL_EXIT_CHANNEL_2_POWER; +static char _label_exit_channel_3_power[LCD_TOTAL_COLS + 1] = LABEL_EXIT_CHANNEL_3_POWER; +static char _label_exit_channel_4_power[LCD_TOTAL_COLS + 1] = LABEL_EXIT_CHANNEL_4_POWER; +static char _label_bulb_brightness[LCD_TOTAL_COLS + 1] = LABEL_BULB_BRIGHTNESS; -/* ---------- Мьютекс LCD ---------- */ -static SemaphoreHandle_t _lcd_mutex = NULL; +zh_menu_node_t _main_menu = { + .label = "Settings", + .submenu = (zh_menu_node_t[]){ + {.label = "Screens", .submenu = (zh_menu_node_t[]){ + {.label = _label_boot_screen, .item_cb = &_zh_menu_boot_screen_callback, .selectable = true}, + {.label = _label_main_screen, .item_cb = &_zh_menu_main_screen_callback, .selectable = true}, + {.label = "Back", .item_cb = &_zh_menu_back_command_callback}, + }, + .item_numbers = 3}, + {.label = "Air condition", .submenu = (zh_menu_node_t[]){ + {.label = _label_air_condition_temp_on, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = _label_air_condition_temp_off, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = "Back", .item_cb = &_zh_menu_back_command_callback}, + }, + .item_numbers = 3}, + {.label = "Lights", .submenu = (zh_menu_node_t[]){ + {.label = _label_exit_channel_1_power, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = _label_exit_channel_2_power, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = _label_exit_channel_3_power, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = _label_exit_channel_4_power, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = _label_bulb_brightness, .item_cb = &_zh_menu_selectable_item_callback, .selectable = true}, + {.label = "Back", .item_cb = &_zh_menu_back_command_callback}, + }, + .item_numbers = 6}, + {.label = "Exit", .item_cb = &_zh_menu_back_command_callback}, + }, + .item_numbers = 4, +}; -// static void zh_load_settings(void); -// static void zh_save_settings(void); +static bool _prev_selected = false; +static zh_menu_node_t *_last_node = NULL; +static uint8_t _last_index = 0; +static zh_menu_node_t *_cached_node = NULL; +static char _row_cache[LCD_TOTAL_ROWS][LCD_TOTAL_COLS] = {0}; +static bool _row_valid[LCD_TOTAL_ROWS] = {0}; +volatile static uint8_t _air_condition_temp_on = 30; +volatile static uint8_t _air_condition_temp_off = 15; +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; -// void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); - -// void zh_menu_enter_main_menu_callback(void *arg); -// void zh_menu_exit_main_menu_callback(void *arg); -// void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index); -// void zh_menu_selectable_item_callback(void *arg); -// void zh_menu_back_command_callback(void *arg); -// void zh_menu_boot_screen_callback(void *arg); -// void zh_menu_main_screen_callback(void *arg); - -/* ---------- Состояние меню ---------- */ -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 inline int16_t _clamp_i16(int16_t v, int16_t lo, int16_t hi) -// { -// return v < lo ? lo : (v > hi ? hi : v); -// } - -// 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 _invalidaterow_cache(void) -{ - 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_TOTAL_COLS); - buf[LCD_TOTAL_COLS] = '\0'; - buf[0] = edit ? '*' : (selected ? '>' : ' '); - - 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; - memcpy(&buf[1], label, len); - - if (arrow_up) - { - buf[LCD_TOTAL_COLS - 1] = '^'; - } - else if (arrow_down) - { - buf[LCD_TOTAL_COLS - 1] = 'v'; - } -} - -static void _format_main_row(char *buf, const char *label, int16_t 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) -{ - 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_TOTAL_COLS) == 0) - { - return; - } - zh_160x_set_cursor(&_lcd_160x_handle, row, 0); - zh_160x_print_char(&_lcd_160x_handle, text); - 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)air_condition_temp_on); - } - else if (lbl == lbl_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)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)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)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)exit_channel_power[3]); - } - else if (lbl == lbl_boot_screen) - { - snprintf(lbl_boot_screen, sizeof(lbl_boot_screen), "B:%s", boot_screens[boot_screen_number].name); - } - else if (lbl == lbl_main_screen) - { - snprintf(lbl_main_screen, sizeof(lbl_main_screen), "M:%s", main_screens[main_screen_number].name); - } - } -} - -/* ---------- Отрисовка экранов ---------- */ -static void _draw_main_screen(void) -{ - const zh_main_screen_description_t *s = &main_screens[main_screen_number]; - for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++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 zh_boot_screen_description_t *s = &boot_screens[boot_screen_number]; - for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++i) - { - 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) -{ - if (current_node != cached_node) - { - _invalidaterow_cache(); - cached_node = current_node; - } - - _refresh_dynamic_labels(current_node); - - zh_menu_view_t view = {0}; - zh_menu_get_view(&_menu_handle, &view); - - 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_TOTAL_ROWS - 1; ++i) - { - 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 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); - } - else - { - memset(buf, ' ', LCD_TOTAL_COLS); - buf[LCD_TOTAL_COLS] = '\0'; - } - - _lcd_put_row((uint8_t)(i + 1), buf); - } -} - -/* ---------- Boot: 5 сек splash → main ---------- */ -static void _boot_task(void *pv) -{ - (void)pv; - vTaskDelay(pdMS_TO_TICKS(BOOT_SPLASH_MS)); - _boot_done = true; - - zh_menu_status_t s = {0}; - zh_menu_get(&_menu_handle, &s); - if (!s.is_active) - { - xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_lcd_clear(&_lcd_160x_handle); /* NEW: очистка перед первым main view */ - _invalidaterow_cache(); - _draw_main_screen(); - xSemaphoreGive(_lcd_mutex); - } - vTaskDelete(NULL); -} - -/* ---------- app_main ---------- */ void app_main(void) { - nvs_flash_init(); + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + ESP_ERROR_CHECK(nvs_flash_init()); + } esp_event_loop_create_default(); - _lcd_mutex = xSemaphoreCreateMutex(); _zh_load_settings(); _zh_pcf8574_init(); _zh_system_load(); _zh_encoder_init(); _zh_menu_init(); - // _zh_wifi_init(); - // _zh_webserver_init(); - // const esp_partition_t *running = esp_ota_get_running_partition(); - // esp_ota_img_states_t ota_state = {0}; - // esp_ota_get_state_partition(running, &ota_state); - // if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) - // { - // vTaskDelay(60000 / portTICK_PERIOD_MS); - // esp_ota_mark_app_valid_cancel_rollback(); - // } - _boot_done = false; - _prev_selected = false; - _invalidaterow_cache(); - _draw_load_screen(); - xTaskCreate(_boot_task, "boot", 3072, NULL, 3, NULL); + _zh_wifi_init(); + _zh_webserver_init(); + xTaskCreatePinnedToCore(&_zh_stats_processing_task, "stats_processing_task", STATS_TASK_STACK_SIZE, NULL, STATS_TASK_PRIORITY, &_stats_task_handle, tskNO_AFFINITY); + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state = {0}; + esp_ota_get_state_partition(running, &ota_state); + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { + vTaskDelay(60000 / portTICK_PERIOD_MS); + esp_ota_mark_app_valid_cancel_rollback(); + } } static void _zh_load_settings(void) @@ -369,22 +173,22 @@ static void _zh_load_settings(void) return; } _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) + size_t size = sizeof(setting_data_blob); + if (nvs_get_blob(nvs, NVS_KEY_SETTINGS, &setting_data_blob, &size) != ESP_OK) { nvs_close(nvs); return; } nvs_close(nvs); - 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; + _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; } static void _zh_save_settings(void) @@ -395,15 +199,15 @@ static void _zh_save_settings(void) return; } _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; + 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); @@ -419,8 +223,9 @@ static void _zh_wifi_init(void) .ap = { .ssid = WIFI_AP_SSID, .password = WIFI_AP_PASS, - .max_connection = 4, + .max_connection = MAX_AP_CONNECTION, .authmode = WIFI_AUTH_WPA2_PSK, + .channel = WIFI_AP_CHANNEL, }, }; esp_wifi_set_mode(WIFI_MODE_AP); @@ -487,24 +292,30 @@ static void _zh_menu_init(void) config.task_priority = MENU_TASK_PRIORITY; config.stack_size = MENU_TASK_STACK_SIZE; config.queue_size = MENU_QUEUE_SIZE; - config.root = &main_menu; + config.root = &_main_menu; config.loop = false; config.viewport_rows = LCD_TOTAL_ROWS - 1; - config.enter_cb = &zh_menu_enter_main_menu_callback; - config.exit_cb = &zh_menu_exit_main_menu_callback; - config.change_cb = &zh_menu_change_menu_callback; + config.enter_cb = &_zh_menu_enter_main_menu_callback; + config.exit_cb = &_zh_menu_exit_main_menu_callback; + config.change_cb = &_zh_menu_change_menu_callback; zh_menu_init(&config, &_menu_handle); } static void _zh_system_load(void) { - // TO DO + zh_160x_lcd_clear(&_lcd_160x_handle); + _zh_draw_boot_screen(); + vTaskDelay(5000); + zh_160x_lcd_clear(&_lcd_160x_handle); + CLEAR_ROW_CACHE(); + _zh_draw_main_screen(); } static void _zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + // TO DO } -/* ---------- Encoder event handler ---------- */ + void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { (void)arg; @@ -514,7 +325,7 @@ void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t e { case ZH_BUTTON_EVENT: { - zh_encoder_button_event_on_isr_t *button_event = event_data; + zh_encoder_button_event_on_isr_t *button_event = (zh_encoder_button_event_on_isr_t *)event_data; if (button_event->button_status == ZH_ENCODER_BUTTON_STATUS_RELEASED) { 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) @@ -524,7 +335,7 @@ void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t e } case ZH_ENCODER_EVENT: { - zh_encoder_event_on_isr_t *encoder_event = event_data; + zh_encoder_event_on_isr_t *encoder_event = (zh_encoder_event_on_isr_t *)event_data; if (menu_status.is_active == true) { if (menu_status.is_selected == true) @@ -545,68 +356,72 @@ void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t e static void _zh_stats_processing_task(void *pvParameter) { + (void)pvParameter; + for (;;) + { + const zh_encoder_stats_t *encoder_stats = zh_encoder_get_stats(); + const zh_pcf8574_stats_t *pcf8574_stats = zh_pcf8574_get_stats(); + const zh_menu_stats_t *menu_stats = zh_menu_get_stats(); + printf("Stats task minimum free stack size: %lu.\n", (uint32_t)uxTaskGetStackHighWaterMark(NULL)); + printf("Encoder task minimum free stack size: %lu.\n", encoder_stats->min_stack_size); + printf("Encoder event post error: %lu.\n", encoder_stats->event_post_error); + printf("Encoder queue overflow error: %lu.\n", encoder_stats->queue_overflow_error); + printf("Expander task minimum free stack size: %lu.\n", pcf8574_stats->min_stack_size); + printf("Expander i2c driver error: %lu.\n", pcf8574_stats->i2c_driver_error); + printf("Expander event post error: %lu.\n", pcf8574_stats->event_post_error); + printf("Expander queue overflow error: %lu.\n", pcf8574_stats->queue_overflow_error); + printf("Expander vector error: %lu.\n", pcf8574_stats->vector_error); + printf("Menu task minimum free stack size: %lu.\n", menu_stats->min_stack_size); + printf("Menu queue overflow error: %lu.\n", menu_stats->queue_overflow_error); + printf("\n"); + vTaskDelay(60000 / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); } -/* ---------- Menu callbacks ---------- */ -void zh_menu_enter_main_menu_callback(void *arg) +static void _zh_menu_enter_main_menu_callback(void *arg) { (void)arg; - xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - cached_node = NULL; - _invalidaterow_cache(); - xSemaphoreGive(_lcd_mutex); + _cached_node = NULL; + CLEAR_ROW_CACHE(); } -void zh_menu_exit_main_menu_callback(void *arg) +static void _zh_menu_exit_main_menu_callback(void *arg) { (void)arg; - /* NEW: страховочное сохранение при выходе из меню */ _zh_save_settings(); _prev_selected = false; - - xSemaphoreTake(_lcd_mutex, portMAX_DELAY); zh_160x_lcd_clear(&_lcd_160x_handle); - _invalidaterow_cache(); - if (_boot_done) - _draw_main_screen(); - else - _draw_load_screen(); - cached_node = NULL; - xSemaphoreGive(_lcd_mutex); + CLEAR_ROW_CACHE(); + _zh_draw_main_screen(); + _cached_node = NULL; } -void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index) +static 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); - - /* NEW: сохраняем при выходе из режима редактирования (BACK) */ - if (_prev_selected && !status.is_selected) + if (_prev_selected == true && status.is_selected == false) { _zh_save_settings(); } _prev_selected = status.is_selected; - - xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - _draw_menu(current_node, current_index, &status); - xSemaphoreGive(_lcd_mutex); + _zh_draw_main_menu(current_node, current_index, &status); } -/* ---------- Selectable: Temp / PW ---------- */ -void zh_menu_selectable_item_callback(void *arg) +static 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) { zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0); @@ -615,34 +430,39 @@ void zh_menu_selectable_item_callback(void *arg) zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg; int8_t dir = encoder->encoder_status == ZH_ENCODER_STATUS_INCREASED ? +1 : -1; bool changed = false; - if (item->label == lbl_temp_on) + if (item->label == _label_air_condition_temp_on) { - air_condition_temp_on = VALUE_CLAMP(air_condition_temp_on + dir, 15, 30); + _air_condition_temp_on = VALUE_CLAMP(_air_condition_temp_on + dir, 15, 30); changed = true; } - else if (item->label == lbl_temp_off) + else if (item->label == _label_air_condition_temp_off) { - air_condition_temp_off = VALUE_CLAMP(air_condition_temp_off + dir, 15, 30); + _air_condition_temp_off = VALUE_CLAMP(_air_condition_temp_off + dir, 15, 30); changed = true; } - else if (item->label == lbl_pw_ch_1) + else if (item->label == _label_exit_channel_1_power) { - exit_channel_power[0] = VALUE_CLAMP(exit_channel_power[0] + dir * 5, 0, 100); + _exit_channel_power[0] = VALUE_CLAMP(_exit_channel_power[0] + dir * 5, 0, 100); changed = true; } - else if (item->label == lbl_pw_ch_2) + else if (item->label == _label_exit_channel_2_power) { - exit_channel_power[1] = VALUE_CLAMP(exit_channel_power[1] + dir * 5, 0, 100); + _exit_channel_power[1] = VALUE_CLAMP(_exit_channel_power[1] + dir * 5, 0, 100); changed = true; } - else if (item->label == lbl_pw_ch_3) + else if (item->label == _label_exit_channel_3_power) { - exit_channel_power[2] = VALUE_CLAMP(exit_channel_power[2] + dir * 5, 0, 100); + _exit_channel_power[2] = VALUE_CLAMP(_exit_channel_power[2] + dir * 5, 0, 100); changed = true; } - else if (item->label == lbl_pw_ch_4) + else if (item->label == _label_exit_channel_4_power) { - exit_channel_power[3] = VALUE_CLAMP(exit_channel_power[3] + dir * 5, 0, 100); + _exit_channel_power[3] = VALUE_CLAMP(_exit_channel_power[3] + dir * 5, 0, 100); + changed = true; + } + else if (item->label == _label_bulb_brightness) + { + _bulb_brightness = VALUE_CLAMP(_bulb_brightness + dir * 5, 0, 100); changed = true; } if (changed == true) @@ -651,7 +471,7 @@ void zh_menu_selectable_item_callback(void *arg) } } -void zh_menu_boot_screen_callback(void *arg) +static void _zh_menu_boot_screen_callback(void *arg) { if (arg == NULL) { @@ -660,23 +480,23 @@ void zh_menu_boot_screen_callback(void *arg) } zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg; int8_t dir = encoder->encoder_status == ZH_ENCODER_STATUS_INCREASED ? +1 : -1; - int next = boot_screen_number + dir; + int next = _boot_screen_number + dir; if (next < 0) { - next = ARRAY_SIZE(boot_screens) - 1; + next = ARRAY_SIZE(_boot_screens) - 1; } - if (next >= ARRAY_SIZE(boot_screens)) + if (next >= ARRAY_SIZE(_boot_screens)) { next = 0; } - if (next != boot_screen_number) + if (next != _boot_screen_number) { - boot_screen_number = next; + _boot_screen_number = next; zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0); } } -void zh_menu_main_screen_callback(void *arg) +static void _zh_menu_main_screen_callback(void *arg) { if (arg == NULL) { @@ -685,24 +505,144 @@ void zh_menu_main_screen_callback(void *arg) } zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg; int8_t dir = encoder->encoder_status == ZH_ENCODER_STATUS_INCREASED ? +1 : -1; - int next = main_screen_number + dir; + int next = _main_screen_number + dir; if (next < 0) { - next = ARRAY_SIZE(main_screens) - 1; + next = ARRAY_SIZE(_main_screens) - 1; } - if (next >= ARRAY_SIZE(main_screens)) + if (next >= ARRAY_SIZE(_main_screens)) { next = 0; } - if (next != main_screen_number) + if (next != _main_screen_number) { - main_screen_number = next; + _main_screen_number = next; zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0); } } -void zh_menu_back_command_callback(void *arg) +static void _zh_menu_back_command_callback(void *arg) { (void)arg; zh_menu_set(&_menu_handle, ZH_MENU_BACK, NULL, 0); +} + +static void _zh_draw_main_screen(void) +{ + const _zh_main_screen_description_t *main_screen = &_main_screens[_main_screen_number]; + for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++i) + { + char buf[LCD_TOTAL_COLS + 1]; + snprintf(buf, sizeof(buf), "%-12.12s%-4s", main_screen->lines[i], main_screen->values[i]); + if (_row_valid[i] == false || memcmp(_row_cache[i], buf, LCD_TOTAL_COLS) != 0) + { + zh_160x_set_cursor(&_lcd_160x_handle, i, 0); + zh_160x_print_char(&_lcd_160x_handle, buf); + memcpy(_row_cache[i], buf, LCD_TOTAL_COLS); + _row_valid[i] = true; + } + } +} + +static void _zh_draw_boot_screen(void) +{ + const _zh_boot_screen_description_t *boot_screen = &_boot_screens[_boot_screen_number]; + for (uint8_t i = 0; i < LCD_TOTAL_ROWS; ++i) + { + char buf[LCD_TOTAL_COLS + 1]; + snprintf(buf, sizeof(buf), "%-*.*s", LCD_TOTAL_COLS, LCD_TOTAL_COLS, boot_screen->lines[i]); + if (_row_valid[i] == false || memcmp(_row_cache[i], buf, LCD_TOTAL_COLS) != 0) + { + zh_160x_set_cursor(&_lcd_160x_handle, i, 0); + zh_160x_print_char(&_lcd_160x_handle, buf); + memcpy(_row_cache[i], buf, LCD_TOTAL_COLS); + _row_valid[i] = true; + }; + } +} + +static void _zh_draw_main_menu(zh_menu_node_t *current_node, uint8_t current_index, const zh_menu_status_t *status) +{ + if (current_node != _cached_node) + { + CLEAR_ROW_CACHE(); + _cached_node = current_node; + } + for (uint8_t i = 0; i < current_node->item_numbers; ++i) + { + const char *label = current_node->submenu[i].label; + if (label == _label_air_condition_temp_on) + { + snprintf(_label_air_condition_temp_on, sizeof(_label_air_condition_temp_on), LABEL_AIR_CONDITION_TEMP_ON "%2dC", (uint8_t)_air_condition_temp_on); + } + else if (label == _label_air_condition_temp_off) + { + snprintf(_label_air_condition_temp_off, sizeof(_label_air_condition_temp_off), LABEL_AIR_CONDITION_TEMP_OFF "%2dC", (uint8_t)_air_condition_temp_off); + } + else if (label == _label_exit_channel_1_power) + { + snprintf(_label_exit_channel_1_power, sizeof(_label_exit_channel_1_power), LABEL_EXIT_CHANNEL_1_POWER "%3d%%", (uint8_t)_exit_channel_power[0]); + } + else if (label == _label_exit_channel_2_power) + { + snprintf(_label_exit_channel_2_power, sizeof(_label_exit_channel_2_power), LABEL_EXIT_CHANNEL_2_POWER "%3d%%", (uint8_t)_exit_channel_power[1]); + } + else if (label == _label_exit_channel_3_power) + { + snprintf(_label_exit_channel_3_power, sizeof(_label_exit_channel_3_power), LABEL_EXIT_CHANNEL_3_POWER "%3d%%", (uint8_t)_exit_channel_power[2]); + } + else if (label == _label_exit_channel_4_power) + { + snprintf(_label_exit_channel_4_power, sizeof(_label_exit_channel_4_power), LABEL_EXIT_CHANNEL_4_POWER "%3d%%", (uint8_t)_exit_channel_power[3]); + } + else if (label == _label_bulb_brightness) + { + snprintf(_label_bulb_brightness, sizeof(_label_bulb_brightness), LABEL_BULB_BRIGHTNESS "%3d%%", (uint8_t)_bulb_brightness); + } + else if (label == _label_boot_screen) + { + snprintf(_label_boot_screen, sizeof(_label_boot_screen), LABEL_BOOT_SCREEN "%s", _boot_screens[_boot_screen_number].name); + } + else if (label == _label_main_screen) + { + snprintf(_label_main_screen, sizeof(_label_main_screen), LABEL_MAIN_SCREEN "%s", _main_screens[_main_screen_number].name); + } + } + zh_menu_view_t view = {0}; + zh_menu_get_view(&_menu_handle, &view); + char header[LCD_TOTAL_COLS + 1]; + snprintf(header, sizeof(header), "%-*.*s", LCD_TOTAL_COLS, LCD_TOTAL_COLS, current_node->label); + if (_row_valid[0] == false || memcmp(_row_cache[0], header, LCD_TOTAL_COLS) != 0) + { + zh_160x_set_cursor(&_lcd_160x_handle, 0, 0); + zh_160x_print_char(&_lcd_160x_handle, header); + memcpy(_row_cache[0], header, LCD_TOTAL_COLS); + _row_valid[0] = true; + }; + for (uint8_t i = 0; i < LCD_TOTAL_ROWS - 1; ++i) + { + 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 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); + uint8_t text_cols = LCD_TOTAL_COLS - 1 - (arrow_up || arrow_down); + snprintf(buf, sizeof(buf), "%c%-*.*s%c", is_edit == true ? '*' : (is_cursor == true ? '>' : ' '), text_cols, text_cols, current_node->submenu[idx].label, arrow_up == true ? '^' : (arrow_down == true ? 'v' : ' ')); + } + else + { + memset(buf, ' ', LCD_TOTAL_COLS); + buf[LCD_TOTAL_COLS] = '\0'; + } + if (_row_valid[i + 1] == false || memcmp(_row_cache[i + 1], buf, LCD_TOTAL_COLS) != 0) + { + zh_160x_set_cursor(&_lcd_160x_handle, i + 1, 0); + zh_160x_print_char(&_lcd_160x_handle, buf); + memcpy(_row_cache[i + 1], buf, LCD_TOTAL_COLS); + _row_valid[i + 1] = true; + }; + } } \ No newline at end of file diff --git a/main/zh_sim_race_button_box.h b/main/zh_sim_race_button_box.h index f1c3428..7db0b29 100644 --- a/main/zh_sim_race_button_box.h +++ b/main/zh_sim_race_button_box.h @@ -10,133 +10,42 @@ #define LCD_TOTAL_ROWS 4 #define LCD_TOTAL_COLS 16 -#define STATS_TASK_PRIORITY tskIDLE_PRIORITY /*!< Priority of the statistics/monitoring task */ -#define STATS_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 /*!< Stack size of the statistics/monitoring task */ -#define EXPANDER_TASK_PRIORITY tskIDLE_PRIORITY + 5 /*!< Priority of the PCF8574 expander polling task */ -#define EXPANDER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 /*!< Stack size of the expander polling task */ -#define ENCODER_TASK_PRIORITY tskIDLE_PRIORITY + 4 /*!< Priority of the encoder reading task */ -#define ENCODER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 /*!< Stack size of the encoder task */ +#define NVS_NAMESPACE "sim_race_box" +#define NVS_KEY_SETTINGS "settings" + +#define STATS_TASK_PRIORITY tskIDLE_PRIORITY +#define STATS_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#define EXPANDER_TASK_PRIORITY tskIDLE_PRIORITY + 5 +#define EXPANDER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#define ENCODER_TASK_PRIORITY tskIDLE_PRIORITY + 4 +#define ENCODER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE #define ENCODER_QUEUE_SIZE 5 #define MENU_TASK_PRIORITY tskIDLE_PRIORITY + 4 -#define MENU_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 + 1280 + 1280 -#define MENU_QUEUE_SIZE 5 +#define MENU_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 +#define MENU_QUEUE_SIZE 20 -#define WIFI_AP_SSID "CABANLEO" /*!< SSID of the access point (service mode) */ -#define WIFI_AP_PASS "cabanleo" /*!< Password of the access point (service mode) */ -#define WIFI_AP_CHANNEL 1 /*!< Wi-Fi channel for the access point */ -#define MAX_AP_CONNECTION 4 /*!< Maximum number of connections to the access point */ +#define WIFI_AP_SSID "CABANLEO" +#define WIFI_AP_PASS "cabanleo" +#define WIFI_AP_CHANNEL 1 +#define MAX_AP_CONNECTION 4 -#define MODE_ENCODER_NUMBER 1 /*!< Encoder identifier for mode selection (connector EN1) */ -#define MODE_ENCODER_A_GPIO GPIO_NUM_4 /*!< Mode encoder channel A input */ -#define MODE_ENCODER_B_GPIO GPIO_NUM_16 /*!< Mode encoder channel B input */ -#define MODE_ENCODER_S_GPIO GPIO_NUM_15 /*!< Mode encoder switch/button input */ +#define MODE_ENCODER_NUMBER 1 +#define MODE_ENCODER_A_GPIO GPIO_NUM_4 +#define MODE_ENCODER_B_GPIO GPIO_NUM_16 +#define MODE_ENCODER_S_GPIO GPIO_NUM_15 -#define EXPANDERS_I2C_SCL_GPIO GPIO_NUM_26 /*!< I2C SCL (clock) GPIO pin (connected to expanders) */ -#define EXPANDERS_I2C_SDA_GPIO GPIO_NUM_25 /*!< I2C SDA (data) GPIO pin (connected to expanders) */ -#define EXPANDERS_INT_GPIO GPIO_NUM_17 /*!< GPIO pin for interrupt from PCF8574 expanders (combined IRQ) */ +#define EXPANDERS_I2C_SCL_GPIO GPIO_NUM_26 +#define EXPANDERS_I2C_SDA_GPIO GPIO_NUM_25 +#define EXPANDERS_INT_GPIO GPIO_NUM_17 -#define LCD_I2C_ADDRESS 0x27 /*!< I2C address of PCF8574 U4 (LCD display expander) */ -// #define BUTTON_I2C_ADDRESS 0x24 /*!< I2C address of PCF8574 U5 (button module / BT1–BT6) */ - -// #define LCD_VIEWPORT_ROWS (LCD_TOTAL_ROWS - 1) - -// #define ENCODER_BUTTON_RELEASED true /*!< State: encoder button released */ -// #define ENCODER_BUTTON_PRESSED false /*!< State: encoder button pressed */ - -// #define LCD_TOTAL_COLS+1 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]; -extern int16_t main_times_data[VIEW_CELLS]; -extern int16_t main_debug_data[VIEW_CELLS]; -extern int16_t main_blank_data[VIEW_CELLS]; +#define LCD_I2C_ADDRESS 0x27 +#define BUTTON_I2C_ADDRESS 0x24 #ifdef __cplusplus extern "C" { #endif - extern void zh_menu_selectable_item_callback(void *arg); - extern void zh_menu_back_command_callback(void *arg); - extern void zh_menu_boot_screen_callback(void *arg); - extern void zh_menu_main_screen_callback(void *arg); - - static char lbl_boot_screen[LCD_TOTAL_COLS + 1] = "B:Boot"; - static char lbl_main_screen[LCD_TOTAL_COLS + 1] = "M:Race"; - static char lbl_temp_on[LCD_TOTAL_COLS + 1] = "Temp ON 15C"; - static char lbl_temp_off[LCD_TOTAL_COLS + 1] = "Temp OFF 35C"; - static char lbl_pw_ch_1[LCD_TOTAL_COLS + 1] = "PW CH 1 100%"; - static char lbl_pw_ch_2[LCD_TOTAL_COLS + 1] = "PW CH 2 100%"; - static char lbl_pw_ch_3[LCD_TOTAL_COLS + 1] = "PW CH 3 100%"; - static char lbl_pw_ch_4[LCD_TOTAL_COLS + 1] = "PW CH 4 100%"; - - // 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; - - static const zh_boot_screen_description_t boot_screens[] = { - {"Boot", {"Booting...", "RAM: OK", "I2C: OK", "Starting..."}}, - {"Version", {"FW v1.0", "Build 2025-10", "ESP-IDF v5.4", ""}}, - {"Diag", {"I2C: OK", "LCD: OK", "ENC: OK", "All: OK"}}, - {"Ready", {"System ready", "Ready for race", "Let's go", ""}}, - {"Blank", {"---", "---", "---", "---"}}, - }; - - zh_menu_node_t main_menu = { - .label = "Settings", - .submenu = (zh_menu_node_t[]){ - {.label = "Screens", .submenu = (zh_menu_node_t[]){ - {.label = lbl_boot_screen, .item_cb = &zh_menu_boot_screen_callback, .selectable = true}, - {.label = lbl_main_screen, .item_cb = &zh_menu_main_screen_callback, .selectable = true}, - {.label = "Back", .item_cb = &zh_menu_back_command_callback}, - }, - .item_numbers = 3}, - {.label = "Air condition", .submenu = (zh_menu_node_t[]){ - {.label = lbl_temp_on, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = lbl_temp_off, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = "Back", .item_cb = &zh_menu_back_command_callback}, - }, - .item_numbers = 3}, - {.label = "Lights", .submenu = (zh_menu_node_t[]){ - {.label = lbl_pw_ch_1, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = lbl_pw_ch_2, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = lbl_pw_ch_3, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = lbl_pw_ch_4, .item_cb = &zh_menu_selectable_item_callback, .selectable = true}, - {.label = "Back", .item_cb = &zh_menu_back_command_callback}, - }, - .item_numbers = 5}, - {.label = "Exit", .item_cb = &zh_menu_back_command_callback}, - }, - .item_numbers = 4, - }; - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/partitions.csv b/partitions.csv deleted file mode 100755 index 18d3d80..0000000 --- a/partitions.csv +++ /dev/null @@ -1,8 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, , 0x6000, -otadata, data, ota, , 0x2000, -phy_init, data, phy, , 0x1000, -ota_0, app, ota_0, , 3500K, -ota_1, app, ota_1, , 3500K, -storage, data,fat , , 1M, \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index 10722a9..db5041a 100644 --- a/sdkconfig +++ b/sdkconfig @@ -670,26 +670,17 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set # default: CONFIG_ESPTOOLPY_FLASHFREQ="40m" -# default: # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -# default: -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# default: -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# default: +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# default: # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -# default: # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set -# default: # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set -# default: # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set # default: -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" -# default: -# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y # default: CONFIG_ESPTOOLPY_BEFORE_RESET=y # default: @@ -709,20 +700,15 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # # Partition Table # -# default: -CONFIG_PARTITION_TABLE_SINGLE_APP=y -# default: +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set -# default: # CONFIG_PARTITION_TABLE_TWO_OTA is not set -# default: -# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set -# default: +CONFIG_PARTITION_TABLE_TWO_OTA_LARGE=y # CONFIG_PARTITION_TABLE_CUSTOM is not set # default: CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" # default: -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_two_ota_large.csv" # default: CONFIG_PARTITION_TABLE_OFFSET=0x8000 # default: @@ -1655,14 +1641,11 @@ CONFIG_ESP_STDIO_MAX_VFS_ENTRIES=2 # # ESP System Settings # -# default: # CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y # default: -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y -# default: -# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set -# default: -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 # # Memory @@ -3596,9 +3579,9 @@ CONFIG_CONSOLE_UART=y CONFIG_CONSOLE_UART_NUM=0 CONFIG_CONSOLE_UART_BAUDRATE=115200 # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y -# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_TRACEMEM_RESERVE_DRAM=0x0 # CONFIG_ESP32_PANIC_PRINT_HALT is not set CONFIG_ESP32_PANIC_PRINT_REBOOT=y