Да, это я уже сделал. Только не выложил. Простите.
#define _keyMinus 0x01 // Кнопка "Минус"
#define _keyOk 0x02 // Кнопка "Ок"
#define _keyPlus 0x04 // Кнопка "Плюс"
#define _keyPRG 0x08 // Кнопка "Программа". Используется для установки параметров
#define _keyInfo 0x20 // Кнопка "Инфо". Используется для отображении информации текущих параметров
#define _keySchedule 0x80 // Кнопка "Расписание"
struct str_MenuItem { // Структура для хранения элемента меню и привязки его к переменным
byte Item_ID; // ID-команды
String Item_Name; // Наименование пункта меню
uint8_t value_type; // Тип перенной: 0 - char, 1 - float, 2 - uint8_t, 3 - uint16_t
void* _value; //Указатель на переменную, хранящую текущее значение команды
};
enum menuType : uint8_t { // Режимы отображения информации на экране
NormalMode, // Нормальный режим (обычный)
SchedulerMode, // Режим программирования расписания
InfoMode, // Режим отображения подробной информации
ProgramMode, // Режим изменения параметров
};
enum menuINFO_namesItems : uint8_t {
menuINFO_command1,
menuINFO_command2,
menuINFO_command3,
menuINFO_command4,
menuINFO_command5,
menuINFO_command6,
menuINFO_names_len,
};
enum menuPRG_namesItems : uint8_t {
menuPRG_command1,
menuPRG_command2,
menuPRG_command3,
menuPRG_command4,
menuPRG_command5,
menuPRG_command6,
menuPRG_command7,
menuPRG_names_len,
};
menuType MenuLevel = NormalMode; // Тип меню, вызванного на экран
byte
MenuPos = 0, // Позиция в текущем меню
MenuID = 0, // ID ОТ-команды в позиции меню
mnuNULL = 0, // Заглушка для несуществующего пункта меню
Key = 0; // Код нажатой кнопки
float float_val1 = 100.4, float_val2 = 4.8;
uint8_t unt8t_val1 = 12, unt8t_val2 = 26;
uint16_t unt16t_val1 = 768, unt16t_val2 = 223;
String CurrentValue = ""; // Значение текущего пункта меню
String str_val = "--";
bool isNeed2Display = true; // признак необходимости обновить информацию на экране
bool isMenuChanged = false; // Сбросим признак, что были внесены изменения
const byte menuItemsCount = 127;
str_MenuItem menuItems[] = {
// Массив со структурами элементов меню
{ 1, "Команда #1", 0, &str_val }, // Ссылка на переменную типа String
{ 2, "Команда №2", 1, &float_val1 }, // Ссылка на переменную типа float
{ 3, "Команда №3", 1, &float_val2 }, // Ссылка на переменную типа float
{ 4, "Команда №4", 2, &unt8t_val1 }, // Ссылка на переменную типа uint8_t
{ 5, "Команда №5", 3, &unt16t_val1 }, // Ссылка на переменную типа uint16_t
// Тут еще куча пунктов меню
};
const byte menuINFO[] = {
// Меню для просмотра
1, 3, 4, 6, 7, 10 // ID пунктов меню из массива структур
};
const byte menuPRG[] = { // Меню для корректировки параметров
2, 3, 5, 7, 9, 10, 11
};
//Поиск пункта меню по ID команды
byte getMenuItem(byte ID) {
for (byte i = 0; i < menuItemsCount; i++) {
if (ID == menuItems[i].Item_ID) return i;
}
return 0;
}
// Чтение текущего пункта активного меню
bool ReadMenuValue() {
byte item = 0;
switch (MenuLevel) {
case SchedulerMode:
break;
case InfoMode:
item = menuINFO[MenuPos - 1];
break;
case ProgramMode:
item = menuPRG[MenuPos - 1];
break;
}
MenuID = getMenuItem(item);
return (MenuID > 0);
}
void menuExit() {
// Тут идет сохранение переменных в EEPROM и часть из них передается обратно в главный контроллер
Serial.begin(115200);
}
//Процедура вывода на дисплей. Кусок, отвечающий за вывод текущего пункта меню
void printDisplay() {
char buff[10];
String* str;
float* flt;
uint8_t* t8;
uint16_t* t16;
switch (menuItems[MenuID].value_type) {
case 0:
str = static_cast<String*>(menuItems[MenuID]._value);
Serial.printf("t: %s\n", *str);
sprintf(buff, "%s", *str);
CurrentValue = String(buff);
break;
case 1:
flt = static_cast<float*>(menuItems[MenuID]._value);
Serial.printf("t: %f\n", *flt);
sprintf(buff, "%.1f", *flt);
CurrentValue = String(buff);
break;
case 2:
t8 = static_cast<uint8_t*>(menuItems[MenuID]._value);
Serial.printf("t: %d\n", *t8);
sprintf(buff, "%d", *t8);
CurrentValue = String(buff);
break;
case 3:
t16 = static_cast<uint16_t*>(menuItems[MenuID]._value);
Serial.printf("t: %d\n", *t16);
sprintf(buff, "%d", *t16);
CurrentValue = String(buff);
break;
}
//display.setTextSize(1);
//display.setTextSize(2);
//display.setCursor(0, 0);
//display.print(utf8rus(menuItems[MenuID].Item_Name)); // Название пункта меню выводится без проблем
//display.setCursor(63 - CurrentValue.length() * 6, 34);
//display.print(CurrentValue); // Значение пункта меню
isNeed2Display = false;
}
void setup() {
// Тут куча кода по инициализации кнопок, LSD и много еще чего.
}
void loop() {
// Допустим кнопку нажали
Key = _keyInfo;
if (Key > 0) { // Если нажата, то
switch (Key) { // Обработка нажатых кнопок
case _keyInfo: // Меню ИНФО
if (MenuLevel == NormalMode) { // Если только входим в меню
MenuLevel = InfoMode; // Установить режим ИНФО
MenuPos = 1; // Первый пункт меню
ReadMenuValue();
} else { // Если уже в меню ИНФО
MenuLevel = NormalMode; // выходим из ИНФО
MenuPos = 0; // Сброс позиции меню
}
isNeed2Display = true; // Обновляем дисплей
break;
case _keyPRG: // кнопка Программирование (PRG)
if (MenuPos == 0) { // И еще не в режиме "Программирование"
isMenuChanged = false; // Сбросим признак, что были внесены изменения
MenuLevel = ProgramMode; // Включим режим "Программирование"
MenuPos = 1; // Меню на первый пункт
ReadMenuValue();
} else {
menuExit(); // Если были в режиме "Программирование", то выходим
}
break;
case _keyMinus:
if (MenuPos > 1) MenuPos--;
else MenuPos = (MenuLevel == InfoMode) ? menuINFO_names_len : (MenuLevel == ProgramMode) ? menuPRG_names_len
: (MenuLevel == SchedulerMode) ? 1
: 0;
ReadMenuValue();
break;
case _keyPlus:
if (MenuPos == ((MenuLevel == InfoMode) ? menuINFO_names_len : (MenuLevel == ProgramMode) ? menuPRG_names_len
: (MenuLevel == SchedulerMode) ? 1
: 0)) MenuPos = 1;
else MenuPos++;
ReadMenuValue();
break;
}
Key = 0;
}
if (isNeed2Display) printDisplay();
}
Только мне очень не нравится добавление разных промежуточных переменных. Очень хотелось свести к одной строке, но, пока не понял, как это сделать:
// Отказаться от этих переменных
String* str;
float* flt;
uint8_t* t8;
uint16_t* t16;
// И сделать что-нибудь так для каждого типа
sprintf(buff, "%s", *(static_cast<String*>(menuItems[MenuID]._value);)));
// или
sprintf(buff, "%.1f", *(static_cast<float*>(menuItems[MenuID]._value)));
но, понимаю, что это неправильно.