|
|
@@ -1,6 +1,47 @@
|
|
|
|
#include "zh_sim_race_button_box.h"
|
|
|
|
#include "zh_sim_race_button_box.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
|
|
|
|
|
|
|
#define VALUE_CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : ((v) > (hi) ? (hi) : (v)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// Хэндлы - функции - переменные
|
|
|
|
// Хэндлы - функции - переменные
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static httpd_handle_t _http_server_handle = NULL;
|
|
|
|
|
|
|
|
static zh_encoder_handle_t *_mode_encoder_handle = NULL;
|
|
|
|
|
|
|
|
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 void _zh_load_settings(void);
|
|
|
|
|
|
|
|
static void _zh_save_settings(void);
|
|
|
|
|
|
|
|
static void _zh_wifi_init(void);
|
|
|
|
|
|
|
|
static void _zh_webserver_init(void);
|
|
|
|
|
|
|
|
static void _zh_encoder_init(void);
|
|
|
|
|
|
|
|
static void _zh_pcf8574_init(void);
|
|
|
|
|
|
|
|
static void _zh_menu_init(void);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
// #define ENCODER_NUMBER 0x01
|
|
|
|
// #define ENCODER_NUMBER 0x01
|
|
|
|
// #define LCD_VIEWPORT_ROWS 3
|
|
|
|
// #define LCD_VIEWPORT_ROWS 3
|
|
|
|
// #define LCD_TOTAL_COLS 16
|
|
|
|
// #define LCD_TOTAL_COLS 16
|
|
|
@@ -46,26 +87,26 @@ static bool _prev_selected = false; /* NEW: для детекции выхода
|
|
|
|
// char lbl_pw_ch_4[LCD_TOTAL_COLS + 1] = "PW CH 4 100%";
|
|
|
|
// char lbl_pw_ch_4[LCD_TOTAL_COLS + 1] = "PW CH 4 100%";
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Handle'ы ---------- */
|
|
|
|
/* ---------- Handle'ы ---------- */
|
|
|
|
zh_encoder_handle_t *encoder_handle = NULL;
|
|
|
|
// zh_encoder_handle_t *encoder_handle = NULL;
|
|
|
|
zh_menu_handle_t *menu_handle = NULL;
|
|
|
|
// zh_menu_handle_t *menu_handle = NULL;
|
|
|
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
|
|
|
// zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
|
|
|
zh_160x_i2c_handle_t *lcd_handle = NULL;
|
|
|
|
// zh_160x_i2c_handle_t *lcd_handle = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Мьютекс LCD ---------- */
|
|
|
|
/* ---------- Мьютекс LCD ---------- */
|
|
|
|
static SemaphoreHandle_t _lcd_mutex = NULL;
|
|
|
|
static SemaphoreHandle_t _lcd_mutex = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static void zh_load_settings(void);
|
|
|
|
// static void zh_load_settings(void);
|
|
|
|
static void zh_save_settings(void);
|
|
|
|
// static void zh_save_settings(void);
|
|
|
|
|
|
|
|
|
|
|
|
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 zh_menu_enter_main_menu_callback(void *arg);
|
|
|
|
// void zh_menu_enter_main_menu_callback(void *arg);
|
|
|
|
void zh_menu_exit_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_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_index);
|
|
|
|
void zh_menu_selectable_item_callback(void *arg);
|
|
|
|
// void zh_menu_selectable_item_callback(void *arg);
|
|
|
|
void zh_menu_back_command_callback(void *arg);
|
|
|
|
// void zh_menu_back_command_callback(void *arg);
|
|
|
|
void zh_menu_boot_screen_callback(void *arg);
|
|
|
|
// void zh_menu_boot_screen_callback(void *arg);
|
|
|
|
void zh_menu_main_screen_callback(void *arg);
|
|
|
|
// void zh_menu_main_screen_callback(void *arg);
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Состояние меню ---------- */
|
|
|
|
/* ---------- Состояние меню ---------- */
|
|
|
|
static zh_menu_node_t *last_node = NULL;
|
|
|
|
static zh_menu_node_t *last_node = NULL;
|
|
|
@@ -83,54 +124,6 @@ volatile static uint8_t bulb_brightness = 100;
|
|
|
|
volatile static uint8_t boot_screen_number = 0;
|
|
|
|
volatile static uint8_t boot_screen_number = 0;
|
|
|
|
volatile static uint8_t main_screen_number = 0;
|
|
|
|
volatile static uint8_t main_screen_number = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static void zh_load_settings(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
nvs_handle_t nvs;
|
|
|
|
|
|
|
|
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void zh_save_settings(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
nvs_handle_t nvs = 0;
|
|
|
|
|
|
|
|
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs) != ESP_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
nvs_set_blob(nvs, NVS_KEY_SETTINGS, &setting_data_blob, sizeof(setting_data_blob));
|
|
|
|
|
|
|
|
nvs_commit(nvs);
|
|
|
|
|
|
|
|
nvs_close(nvs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Утилиты ---------- */
|
|
|
|
/* ---------- Утилиты ---------- */
|
|
|
|
// static inline int16_t _clamp_i16(int16_t v, int16_t lo, int16_t hi)
|
|
|
|
// static inline int16_t _clamp_i16(int16_t v, int16_t lo, int16_t hi)
|
|
|
|
// {
|
|
|
|
// {
|
|
|
@@ -195,8 +188,8 @@ static void _lcd_put_row(uint8_t row, const char *text)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zh_160x_set_cursor(&lcd_handle, row, 0);
|
|
|
|
zh_160x_set_cursor(&_lcd_160x_handle, row, 0);
|
|
|
|
zh_160x_print_char(&lcd_handle, text);
|
|
|
|
zh_160x_print_char(&_lcd_160x_handle, text);
|
|
|
|
memcpy(row_cache[row], text, LCD_TOTAL_COLS);
|
|
|
|
memcpy(row_cache[row], text, LCD_TOTAL_COLS);
|
|
|
|
row_valid[row] = true;
|
|
|
|
row_valid[row] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -281,7 +274,7 @@ static void _draw_menu(zh_menu_node_t *current_node, uint8_t current_index, cons
|
|
|
|
_refresh_dynamic_labels(current_node);
|
|
|
|
_refresh_dynamic_labels(current_node);
|
|
|
|
|
|
|
|
|
|
|
|
zh_menu_view_t view = {0};
|
|
|
|
zh_menu_view_t view = {0};
|
|
|
|
zh_menu_get_view(&menu_handle, &view);
|
|
|
|
zh_menu_get_view(&_menu_handle, &view);
|
|
|
|
|
|
|
|
|
|
|
|
char header[LCD_TOTAL_COLS + 1];
|
|
|
|
char header[LCD_TOTAL_COLS + 1];
|
|
|
|
// memset(header, ' ', LCD_TOTAL_COLS);
|
|
|
|
// memset(header, ' ', LCD_TOTAL_COLS);
|
|
|
@@ -328,11 +321,11 @@ static void _boot_task(void *pv)
|
|
|
|
_boot_done = true;
|
|
|
|
_boot_done = true;
|
|
|
|
|
|
|
|
|
|
|
|
zh_menu_status_t s = {0};
|
|
|
|
zh_menu_status_t s = {0};
|
|
|
|
zh_menu_get(&menu_handle, &s);
|
|
|
|
zh_menu_get(&_menu_handle, &s);
|
|
|
|
if (!s.is_active)
|
|
|
|
if (!s.is_active)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
|
|
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
|
|
|
zh_160x_lcd_clear(&lcd_handle); /* NEW: очистка перед первым main view */
|
|
|
|
zh_160x_lcd_clear(&_lcd_160x_handle); /* NEW: очистка перед первым main view */
|
|
|
|
_invalidaterow_cache();
|
|
|
|
_invalidaterow_cache();
|
|
|
|
_draw_main_screen();
|
|
|
|
_draw_main_screen();
|
|
|
|
xSemaphoreGive(_lcd_mutex);
|
|
|
|
xSemaphoreGive(_lcd_mutex);
|
|
|
@@ -343,78 +336,24 @@ static void _boot_task(void *pv)
|
|
|
|
/* ---------- app_main ---------- */
|
|
|
|
/* ---------- app_main ---------- */
|
|
|
|
void app_main(void)
|
|
|
|
void app_main(void)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
esp_log_level_set("zh_menu", ESP_LOG_ERROR);
|
|
|
|
nvs_flash_init();
|
|
|
|
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
|
|
|
|
|
|
|
|
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
|
|
|
|
|
|
|
esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR);
|
|
|
|
|
|
|
|
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- NEW: NVS init + загрузка настроек ---------- */
|
|
|
|
|
|
|
|
esp_err_t nvs_err = nvs_flash_init();
|
|
|
|
|
|
|
|
if (nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
|
|
|
|
|
|
nvs_err = nvs_flash_init();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(nvs_err);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zh_load_settings();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
esp_event_loop_create_default();
|
|
|
|
esp_event_loop_create_default();
|
|
|
|
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();
|
|
|
|
|
|
|
|
encoder_config.task_priority = 5;
|
|
|
|
|
|
|
|
encoder_config.stack_size = configMINIMAL_STACK_SIZE;
|
|
|
|
|
|
|
|
encoder_config.queue_size = 5;
|
|
|
|
|
|
|
|
encoder_config.a_gpio_number = GPIO_NUM_4;
|
|
|
|
|
|
|
|
encoder_config.b_gpio_number = GPIO_NUM_16;
|
|
|
|
|
|
|
|
encoder_config.s_gpio_number = GPIO_NUM_15;
|
|
|
|
|
|
|
|
encoder_config.encoder_min_value = -1000000;
|
|
|
|
|
|
|
|
encoder_config.encoder_max_value = 1000000;
|
|
|
|
|
|
|
|
encoder_config.encoder_step = 1;
|
|
|
|
|
|
|
|
encoder_config.encoder_number = ENCODER_NUMBER;
|
|
|
|
|
|
|
|
zh_encoder_init(&encoder_config, &encoder_handle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- I2C + PCF8574 + LCD ---------- */
|
|
|
|
|
|
|
|
i2c_master_bus_config_t i2c_bus_config = {
|
|
|
|
|
|
|
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
|
|
|
|
|
|
.i2c_port = I2C_NUM_MAX - 1,
|
|
|
|
|
|
|
|
.scl_io_num = GPIO_NUM_26,
|
|
|
|
|
|
|
|
.sda_io_num = GPIO_NUM_25,
|
|
|
|
|
|
|
|
.glitch_ignore_cnt = 7,
|
|
|
|
|
|
|
|
.flags.enable_internal_pullup = true,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zh_pcf8574_init_config_t pcf_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
|
|
|
|
|
|
|
pcf_config.i2c_handle = i2c_bus_handle;
|
|
|
|
|
|
|
|
pcf_config.i2c_address = 0x27;
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(zh_pcf8574_init(&pcf_config, &pcf8574_handle));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(zh_160x_init(&pcf8574_handle, &lcd_handle, ZH_LCD_16X4));
|
|
|
|
|
|
|
|
zh_160x_lcd_clear(&lcd_handle);
|
|
|
|
|
|
|
|
zh_160x_off_cursor(&lcd_handle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_lcd_mutex = xSemaphoreCreateMutex();
|
|
|
|
_lcd_mutex = xSemaphoreCreateMutex();
|
|
|
|
configASSERT(_lcd_mutex != NULL);
|
|
|
|
_zh_load_settings();
|
|
|
|
|
|
|
|
_zh_pcf8574_init();
|
|
|
|
/* ---------- Menu ---------- */
|
|
|
|
_zh_system_load();
|
|
|
|
zh_menu_init_config_t menu_config = ZH_MENU_INIT_CONFIG_DEFAULT();
|
|
|
|
_zh_encoder_init();
|
|
|
|
menu_config.task_priority = 5;
|
|
|
|
_zh_menu_init();
|
|
|
|
menu_config.stack_size = 4096;
|
|
|
|
// _zh_wifi_init();
|
|
|
|
menu_config.queue_size = 5;
|
|
|
|
// _zh_webserver_init();
|
|
|
|
menu_config.root = &main_menu;
|
|
|
|
// const esp_partition_t *running = esp_ota_get_running_partition();
|
|
|
|
menu_config.loop = false;
|
|
|
|
// esp_ota_img_states_t ota_state = {0};
|
|
|
|
menu_config.viewport_rows = LCD_TOTAL_ROWS - 1;
|
|
|
|
// esp_ota_get_state_partition(running, &ota_state);
|
|
|
|
menu_config.enter_cb = &zh_menu_enter_main_menu_callback;
|
|
|
|
// if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
|
|
|
|
menu_config.exit_cb = &zh_menu_exit_main_menu_callback;
|
|
|
|
// {
|
|
|
|
menu_config.change_cb = &zh_menu_change_menu_callback;
|
|
|
|
// vTaskDelay(60000 / portTICK_PERIOD_MS);
|
|
|
|
zh_menu_init(&menu_config, &menu_handle);
|
|
|
|
// esp_ota_mark_app_valid_cancel_rollback();
|
|
|
|
|
|
|
|
// }
|
|
|
|
/* ---------- Splash: сохранённый load-экран ---------- */
|
|
|
|
|
|
|
|
_boot_done = false;
|
|
|
|
_boot_done = false;
|
|
|
|
_prev_selected = false;
|
|
|
|
_prev_selected = false;
|
|
|
|
_invalidaterow_cache();
|
|
|
|
_invalidaterow_cache();
|
|
|
@@ -422,21 +361,164 @@ void app_main(void)
|
|
|
|
xTaskCreate(_boot_task, "boot", 3072, NULL, 3, NULL);
|
|
|
|
xTaskCreate(_boot_task, "boot", 3072, NULL, 3, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_load_settings(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
nvs_handle_t nvs;
|
|
|
|
|
|
|
|
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_save_settings(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
nvs_handle_t nvs = 0;
|
|
|
|
|
|
|
|
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs) != ESP_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
nvs_set_blob(nvs, NVS_KEY_SETTINGS, &setting_data_blob, sizeof(setting_data_blob));
|
|
|
|
|
|
|
|
nvs_commit(nvs);
|
|
|
|
|
|
|
|
nvs_close(nvs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_wifi_init(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
esp_netif_init();
|
|
|
|
|
|
|
|
esp_netif_create_default_wifi_ap();
|
|
|
|
|
|
|
|
wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
|
|
|
|
|
|
|
|
esp_wifi_init(&wifi_config);
|
|
|
|
|
|
|
|
wifi_config_t ap_config = {
|
|
|
|
|
|
|
|
.ap = {
|
|
|
|
|
|
|
|
.ssid = WIFI_AP_SSID,
|
|
|
|
|
|
|
|
.password = WIFI_AP_PASS,
|
|
|
|
|
|
|
|
.max_connection = 4,
|
|
|
|
|
|
|
|
.authmode = WIFI_AUTH_WPA2_PSK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
esp_wifi_set_mode(WIFI_MODE_AP);
|
|
|
|
|
|
|
|
esp_wifi_set_config(WIFI_IF_AP, &ap_config);
|
|
|
|
|
|
|
|
esp_wifi_start();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_webserver_init(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
|
|
|
|
httpd_start(&_http_server_handle, &config);
|
|
|
|
|
|
|
|
zh_ota_server_init(_http_server_handle, "/");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_encoder_init(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &_zh_encoder_event_handler, NULL, NULL);
|
|
|
|
|
|
|
|
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
|
|
|
|
|
|
|
|
config.task_priority = ENCODER_TASK_PRIORITY;
|
|
|
|
|
|
|
|
config.stack_size = ENCODER_TASK_STACK_SIZE;
|
|
|
|
|
|
|
|
config.queue_size = ENCODER_QUEUE_SIZE;
|
|
|
|
|
|
|
|
config.a_gpio_number = MODE_ENCODER_A_GPIO;
|
|
|
|
|
|
|
|
config.b_gpio_number = MODE_ENCODER_B_GPIO;
|
|
|
|
|
|
|
|
config.s_gpio_number = MODE_ENCODER_S_GPIO;
|
|
|
|
|
|
|
|
config.encoder_min_value = -1000000;
|
|
|
|
|
|
|
|
config.encoder_max_value = 1000000;
|
|
|
|
|
|
|
|
config.encoder_step = 1;
|
|
|
|
|
|
|
|
config.encoder_number = MODE_ENCODER_NUMBER;
|
|
|
|
|
|
|
|
zh_encoder_init(&config, &_mode_encoder_handle);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_pcf8574_init(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
|
|
|
|
|
|
|
i2c_master_bus_config_t i2c_bus_config = {
|
|
|
|
|
|
|
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
|
|
|
|
|
|
.scl_io_num = EXPANDERS_I2C_SCL_GPIO,
|
|
|
|
|
|
|
|
.sda_io_num = EXPANDERS_I2C_SDA_GPIO,
|
|
|
|
|
|
|
|
.glitch_ignore_cnt = 7,
|
|
|
|
|
|
|
|
.flags.enable_internal_pullup = true,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
|
|
|
|
|
|
|
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &_zh_pcf8574_event_handler, NULL, NULL);
|
|
|
|
|
|
|
|
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
|
|
|
|
|
|
|
config.task_priority = EXPANDER_TASK_PRIORITY;
|
|
|
|
|
|
|
|
config.stack_size = EXPANDER_TASK_STACK_SIZE;
|
|
|
|
|
|
|
|
config.i2c_handle = i2c_bus_handle;
|
|
|
|
|
|
|
|
config.i2c_address = LCD_I2C_ADDRESS;
|
|
|
|
|
|
|
|
zh_pcf8574_init(&config, &_lcd_handle);
|
|
|
|
|
|
|
|
zh_160x_init(&_lcd_handle, &_lcd_160x_handle, ZH_LCD_16X4);
|
|
|
|
|
|
|
|
// config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
|
|
|
|
|
|
|
// config.p1_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
|
|
|
|
|
|
|
// config.p2_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
|
|
|
|
|
|
|
// config.p3_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
|
|
|
|
|
|
|
// config.p4_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
|
|
|
|
|
|
|
// config.interrupt_gpio = EXPANDERS_INT_GPIO;
|
|
|
|
|
|
|
|
// config.i2c_address = BUTTON_I2C_ADDRESS;
|
|
|
|
|
|
|
|
// zh_pcf8574_init(&config, &_button_handle);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_menu_init(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
zh_menu_init_config_t config = ZH_MENU_INIT_CONFIG_DEFAULT();
|
|
|
|
|
|
|
|
config.task_priority = MENU_TASK_PRIORITY;
|
|
|
|
|
|
|
|
config.stack_size = MENU_TASK_STACK_SIZE;
|
|
|
|
|
|
|
|
config.queue_size = MENU_QUEUE_SIZE;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
zh_menu_init(&config, &_menu_handle);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_system_load(void)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// TO DO
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
/* ---------- Encoder event handler ---------- */
|
|
|
|
/* ---------- 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;
|
|
|
|
(void)arg;
|
|
|
|
zh_menu_status_t menu_status = {0};
|
|
|
|
zh_menu_status_t menu_status = {0};
|
|
|
|
zh_menu_get(&menu_handle, &menu_status);
|
|
|
|
zh_menu_get(&_menu_handle, &menu_status);
|
|
|
|
switch (event_id)
|
|
|
|
switch (event_id)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
case ZH_BUTTON_EVENT:
|
|
|
|
case ZH_BUTTON_EVENT:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_encoder_button_event_on_isr_t *button_event = event_data;
|
|
|
|
zh_encoder_button_event_on_isr_t *button_event = event_data;
|
|
|
|
if (button_event->button_status == ENCODER_BUTTON_RELEASED)
|
|
|
|
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)
|
|
|
|
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);
|
|
|
|
: zh_menu_set(&_menu_handle, ZH_MENU_BACK, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -447,11 +529,11 @@ void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t ev
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (menu_status.is_selected == true)
|
|
|
|
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
|
|
|
|
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 == ZH_ENCODER_STATUS_INCREASED ? zh_menu_set(&_menu_handle, ZH_MENU_UP, NULL, 0) : zh_menu_set(&_menu_handle, ZH_MENU_DOWN, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
@@ -461,6 +543,10 @@ void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t ev
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void _zh_stats_processing_task(void *pvParameter)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Menu callbacks ---------- */
|
|
|
|
/* ---------- Menu callbacks ---------- */
|
|
|
|
void zh_menu_enter_main_menu_callback(void *arg)
|
|
|
|
void zh_menu_enter_main_menu_callback(void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -475,11 +561,11 @@ void zh_menu_exit_main_menu_callback(void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
(void)arg;
|
|
|
|
(void)arg;
|
|
|
|
/* NEW: страховочное сохранение при выходе из меню */
|
|
|
|
/* NEW: страховочное сохранение при выходе из меню */
|
|
|
|
zh_save_settings();
|
|
|
|
_zh_save_settings();
|
|
|
|
_prev_selected = false;
|
|
|
|
_prev_selected = false;
|
|
|
|
|
|
|
|
|
|
|
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
|
|
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
|
|
|
zh_160x_lcd_clear(&lcd_handle);
|
|
|
|
zh_160x_lcd_clear(&_lcd_160x_handle);
|
|
|
|
_invalidaterow_cache();
|
|
|
|
_invalidaterow_cache();
|
|
|
|
if (_boot_done)
|
|
|
|
if (_boot_done)
|
|
|
|
_draw_main_screen();
|
|
|
|
_draw_main_screen();
|
|
|
@@ -495,12 +581,12 @@ void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_
|
|
|
|
last_index = current_index;
|
|
|
|
last_index = current_index;
|
|
|
|
|
|
|
|
|
|
|
|
zh_menu_status_t status = {0};
|
|
|
|
zh_menu_status_t status = {0};
|
|
|
|
zh_menu_get(&menu_handle, &status);
|
|
|
|
zh_menu_get(&_menu_handle, &status);
|
|
|
|
|
|
|
|
|
|
|
|
/* NEW: сохраняем при выходе из режима редактирования (BACK) */
|
|
|
|
/* NEW: сохраняем при выходе из режима редактирования (BACK) */
|
|
|
|
if (_prev_selected && !status.is_selected)
|
|
|
|
if (_prev_selected && !status.is_selected)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_save_settings();
|
|
|
|
_zh_save_settings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_prev_selected = status.is_selected;
|
|
|
|
_prev_selected = status.is_selected;
|
|
|
|
|
|
|
|
|
|
|
@@ -523,11 +609,11 @@ void zh_menu_selectable_item_callback(void *arg)
|
|
|
|
zh_menu_node_t *item = &last_node->submenu[last_index];
|
|
|
|
zh_menu_node_t *item = &last_node->submenu[last_index];
|
|
|
|
if (arg == NULL)
|
|
|
|
if (arg == NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zh_encoder_event_on_isr_t *encoder = arg;
|
|
|
|
zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg;
|
|
|
|
int8_t dir = encoder->encoder_status ? +1 : -1;
|
|
|
|
int8_t dir = encoder->encoder_status == ZH_ENCODER_STATUS_INCREASED ? +1 : -1;
|
|
|
|
bool changed = false;
|
|
|
|
bool changed = false;
|
|
|
|
if (item->label == lbl_temp_on)
|
|
|
|
if (item->label == lbl_temp_on)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -561,7 +647,7 @@ void zh_menu_selectable_item_callback(void *arg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changed == true)
|
|
|
|
if (changed == true)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -569,11 +655,11 @@ void zh_menu_boot_screen_callback(void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (arg == NULL)
|
|
|
|
if (arg == NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zh_encoder_event_on_isr_t *encoder = arg;
|
|
|
|
zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg;
|
|
|
|
int8_t dir = encoder->encoder_status == true ? +1 : -1;
|
|
|
|
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)
|
|
|
|
if (next < 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -586,7 +672,7 @@ void zh_menu_boot_screen_callback(void *arg)
|
|
|
|
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);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -594,11 +680,11 @@ void zh_menu_main_screen_callback(void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (arg == NULL)
|
|
|
|
if (arg == NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zh_encoder_event_on_isr_t *encoder = arg;
|
|
|
|
zh_encoder_event_on_isr_t *encoder = (zh_encoder_event_on_isr_t *)arg;
|
|
|
|
int8_t dir = encoder->encoder_status == true ? +1 : -1;
|
|
|
|
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)
|
|
|
|
if (next < 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -611,18 +697,12 @@ void zh_menu_main_screen_callback(void *arg)
|
|
|
|
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);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_SHOW, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// void zh_menu_simple_item_callback(void *arg)
|
|
|
|
|
|
|
|
// {
|
|
|
|
|
|
|
|
// (void)arg;
|
|
|
|
|
|
|
|
// ESP_LOGI(TAG, "Simple item callback performed");
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void zh_menu_back_command_callback(void *arg)
|
|
|
|
void zh_menu_back_command_callback(void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
(void)arg;
|
|
|
|
(void)arg;
|
|
|
|
zh_menu_set(&menu_handle, ZH_MENU_BACK, NULL, 0);
|
|
|
|
zh_menu_set(&_menu_handle, ZH_MENU_BACK, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|