wip:
This commit is contained in:
1 parent
b57a9eac15
commit
c884a61f6e
2 files changed
+115
-124
No files matched your search
@@ -1,6 +1,5 @@
|
||||
#include "zh_sim_race_button_box.h"
|
||||
|
||||
|
||||
// Хэндлы - функции - переменные
|
||||
// #define ENCODER_NUMBER 0x01
|
||||
// #define LCD_VIEWPORT_ROWS 3
|
||||
@@ -8,13 +7,13 @@
|
||||
// #define LCD_TOTAL_ROWS 4
|
||||
#define BOOT_SPLASH_MS 5000
|
||||
|
||||
#define MAIN_SCREEN_COUNT 5
|
||||
#define LOAD_SCREEN_COUNT 5
|
||||
// #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";
|
||||
// static const char *TAG = "sim_race_box";
|
||||
|
||||
/* ============================================================
|
||||
* Данные main-экранов (idle)
|
||||
@@ -25,21 +24,7 @@ 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};
|
||||
|
||||
/* ---------- Дескрипторы экранов ---------- */
|
||||
// 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;
|
||||
|
||||
static const zh_main_screen_description_t main_screens[MAIN_SCREEN_COUNT] = {
|
||||
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},
|
||||
@@ -47,29 +32,18 @@ static const zh_main_screen_description_t main_screens[MAIN_SCREEN_COUNT] = {
|
||||
{"Blank", {"---", "---", "---", "---"}, main_blank_data},
|
||||
};
|
||||
|
||||
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"}},
|
||||
{"Ready", {"System ready", "Ready for race", "Let's go", ""}},
|
||||
{"Blank", {"", "", "", ""}},
|
||||
};
|
||||
|
||||
/* ---------- Пользовательский выбор ---------- */
|
||||
static int _boot_screen = 0;
|
||||
static int _main_screen = 0;
|
||||
static bool _boot_done = false;
|
||||
static bool _prev_selected = false; /* NEW: для детекции выхода из edit */
|
||||
|
||||
/* ---------- Динамические буферы меток ---------- */
|
||||
char lbl_boot_screen[MENU_LABEL_LEN] = "B:Boot";
|
||||
char lbl_main_screen[MENU_LABEL_LEN] = "M:Race";
|
||||
char lbl_temp_on[MENU_LABEL_LEN] = "Temp ON 15C";
|
||||
char lbl_temp_off[MENU_LABEL_LEN] = "Temp OFF 35C";
|
||||
char lbl_pw_ch_1[MENU_LABEL_LEN] = "PW CH 1 100%";
|
||||
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%";
|
||||
// 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%";
|
||||
|
||||
/* ---------- Handle'ы ---------- */
|
||||
zh_encoder_handle_t *encoder_handle = NULL;
|
||||
@@ -85,6 +59,14 @@ 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_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;
|
||||
@@ -150,15 +132,15 @@ static void zh_save_settings(void)
|
||||
}
|
||||
|
||||
/* ---------- Утилиты ---------- */
|
||||
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 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 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)
|
||||
{
|
||||
@@ -166,8 +148,7 @@ static void _invalidaterow_cache(void)
|
||||
}
|
||||
|
||||
/* ---------- Форматирование ---------- */
|
||||
static void _format_row(char *buf, const char *label, bool selected, bool edit,
|
||||
bool arrow_up, bool arrow_down)
|
||||
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';
|
||||
@@ -211,8 +192,9 @@ static void _format_load_row(char *buf, const char *line)
|
||||
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_handle, row, 0);
|
||||
zh_160x_print_char(&lcd_handle, text);
|
||||
memcpy(row_cache[row], text, LCD_TOTAL_COLS);
|
||||
@@ -255,11 +237,11 @@ static void _refresh_dynamic_labels(zh_menu_node_t *node)
|
||||
}
|
||||
else if (lbl == lbl_boot_screen)
|
||||
{
|
||||
snprintf(lbl_boot_screen, sizeof(lbl_boot_screen), "B:%s", load_screens[_boot_screen].name);
|
||||
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].name);
|
||||
snprintf(lbl_main_screen, sizeof(lbl_main_screen), "M:%s", main_screens[main_screen_number].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +249,7 @@ static void _refresh_dynamic_labels(zh_menu_node_t *node)
|
||||
/* ---------- Отрисовка экранов ---------- */
|
||||
static void _draw_main_screen(void)
|
||||
{
|
||||
const zh_main_screen_description_t *s = &main_screens[_main_screen];
|
||||
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];
|
||||
@@ -278,7 +260,7 @@ static void _draw_main_screen(void)
|
||||
|
||||
static void _draw_load_screen(void)
|
||||
{
|
||||
const zh_boot_screen_description_t *s = &load_screens[_boot_screen];
|
||||
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];
|
||||
@@ -451,7 +433,7 @@ void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t ev
|
||||
case ZH_BUTTON_EVENT:
|
||||
{
|
||||
zh_encoder_button_event_on_isr_t *button_event = event_data;
|
||||
if (button_event->button_status == true)
|
||||
if (button_event->button_status == ENCODER_BUTTON_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)
|
||||
: zh_menu_set(&menu_handle, ZH_MENU_BACK, NULL, 0);
|
||||
@@ -531,60 +513,58 @@ void zh_menu_change_menu_callback(zh_menu_node_t *current_node, uint8_t current_
|
||||
void zh_menu_selectable_item_callback(void *arg)
|
||||
{
|
||||
if (last_node == NULL || last_node->submenu == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (last_index >= last_node->item_numbers)
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
zh_menu_node_t *item = &last_node->submenu[last_index];
|
||||
|
||||
if (arg == NULL)
|
||||
{
|
||||
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
zh_encoder_event_on_isr_t *enc = arg;
|
||||
int8_t dir = enc->encoder_status ? +1 : -1;
|
||||
zh_encoder_event_on_isr_t *encoder = arg;
|
||||
int8_t dir = encoder->encoder_status ? +1 : -1;
|
||||
bool changed = false;
|
||||
|
||||
if (item->label == lbl_temp_on)
|
||||
{
|
||||
air_condition_temp_on = _clamp_i16(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)
|
||||
{
|
||||
air_condition_temp_off = _clamp_i16(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)
|
||||
{
|
||||
exit_channel_power[0] = _clamp_u8(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)
|
||||
{
|
||||
exit_channel_power[1] = _clamp_u8(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)
|
||||
{
|
||||
exit_channel_power[2] = _clamp_u8(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)
|
||||
{
|
||||
exit_channel_power[3] = _clamp_u8(exit_channel_power[3] + dir * 5, 0, 100);
|
||||
exit_channel_power[3] = VALUE_CLAMP(exit_channel_power[3] + dir * 5, 0, 100);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
if (changed == true)
|
||||
{
|
||||
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Selectable: выбор load-экрана ---------- */
|
||||
void zh_menu_boot_screen_callback(void *arg)
|
||||
{
|
||||
if (arg == NULL)
|
||||
@@ -592,24 +572,24 @@ void zh_menu_boot_screen_callback(void *arg)
|
||||
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
zh_encoder_event_on_isr_t *enc = arg;
|
||||
int8_t dir = enc->encoder_status ? +1 : -1;
|
||||
|
||||
int next = _boot_screen + dir;
|
||||
zh_encoder_event_on_isr_t *encoder = arg;
|
||||
int8_t dir = encoder->encoder_status == true ? +1 : -1;
|
||||
int next = boot_screen_number + dir;
|
||||
if (next < 0)
|
||||
next = LOAD_SCREEN_COUNT - 1;
|
||||
if (next >= LOAD_SCREEN_COUNT)
|
||||
next = 0;
|
||||
|
||||
if (next != _boot_screen)
|
||||
{
|
||||
_boot_screen = next;
|
||||
next = ARRAY_SIZE(boot_screens) - 1;
|
||||
}
|
||||
if (next >= ARRAY_SIZE(boot_screens))
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
if (next != boot_screen_number)
|
||||
{
|
||||
boot_screen_number = next;
|
||||
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Selectable: выбор main-экрана ---------- */
|
||||
void zh_menu_main_screen_callback(void *arg)
|
||||
{
|
||||
if (arg == NULL)
|
||||
@@ -617,28 +597,29 @@ void zh_menu_main_screen_callback(void *arg)
|
||||
zh_menu_set(&menu_handle, ZH_MENU_SHOW, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
zh_encoder_event_on_isr_t *enc = arg;
|
||||
int8_t dir = enc->encoder_status ? +1 : -1;
|
||||
|
||||
int next = _main_screen + dir;
|
||||
zh_encoder_event_on_isr_t *encoder = arg;
|
||||
int8_t dir = encoder->encoder_status == true ? +1 : -1;
|
||||
int next = main_screen_number + dir;
|
||||
if (next < 0)
|
||||
next = MAIN_SCREEN_COUNT - 1;
|
||||
if (next >= MAIN_SCREEN_COUNT)
|
||||
next = 0;
|
||||
|
||||
if (next != _main_screen)
|
||||
{
|
||||
_main_screen = next;
|
||||
next = ARRAY_SIZE(main_screens) - 1;
|
||||
}
|
||||
if (next >= ARRAY_SIZE(main_screens))
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
if (next != main_screen_number)
|
||||
{
|
||||
main_screen_number = next;
|
||||
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_simple_item_callback(void *arg)
|
||||
// {
|
||||
// (void)arg;
|
||||
// ESP_LOGI(TAG, "Simple item callback performed");
|
||||
// }
|
||||
|
||||
void zh_menu_back_command_callback(void *arg)
|
||||
{
|
||||
|
||||
@@ -7,17 +7,30 @@
|
||||
#include "zh_encoder.h"
|
||||
#include "zh_ota_server.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define VALUE_CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : ((v) > (hi) ? (hi) : (v)))
|
||||
|
||||
#define ENCODER_NUMBER 1
|
||||
|
||||
#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 /*!< 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 /*!< Stack size of the encoder task */
|
||||
// #define LCD_VIEWPORT_ROWS (LCD_TOTAL_ROWS - 1)
|
||||
|
||||
#define MENU_LABEL_LEN 17
|
||||
#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 VIEW_ROWS 4 /* строк отрисовывается на LCD */
|
||||
|
||||
#define NVS_NAMESPACE "sim_race_box" /* NEW */
|
||||
#define NVS_KEY_SETTINGS "settings" /* NEW */
|
||||
@@ -29,36 +42,25 @@ extern int16_t main_times_data[VIEW_CELLS];
|
||||
extern int16_t main_debug_data[VIEW_CELLS];
|
||||
extern int16_t main_blank_data[VIEW_CELLS];
|
||||
|
||||
/* ---------- Динамические буферы меток ---------- */
|
||||
extern char lbl_temp_on[MENU_LABEL_LEN];
|
||||
extern char lbl_temp_off[MENU_LABEL_LEN];
|
||||
extern char lbl_pw_ch_1[MENU_LABEL_LEN];
|
||||
extern char lbl_pw_ch_2[MENU_LABEL_LEN];
|
||||
extern char lbl_pw_ch_3[MENU_LABEL_LEN];
|
||||
extern char lbl_pw_ch_4[MENU_LABEL_LEN];
|
||||
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];
|
||||
|
||||
/* ---------- Callbacks ---------- */
|
||||
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_simple_item_callback(void *args);
|
||||
void zh_menu_back_command_callback(void *arg);
|
||||
void zh_menu_boot_screen_callback(void *arg);
|
||||
void zh_menu_main_screen_callback(void *arg);
|
||||
|
||||
#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;
|
||||
@@ -82,6 +84,14 @@ extern "C"
|
||||
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[]){
|
||||
|
||||
Reference in new issue
Block a user