Вот кусок функционального кода (Наименования переменных и пунктов меню изменены, все совпадения с реальными именами - случайны!)
#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; // Наименование пункта меню
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", &str_val }, // Ссылка на переменную типа String
{ 2, "Команда №2", &float_val1 }, // Ссылка на переменную типа float
{ 3, "Команда №3", &float_val2 }, // Ссылка на переменную типа float
{ 4, "Команда №4", &unt8t_val1 }, // Ссылка на переменную типа uint8_t
{ 5, "Команда №5", &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];
break;
case ProgramMode:
item = menuPRG[MenuPos];
break;
}
MenuID = getMenuItem(item);
return (MenuID > 0);
}
void menuExit() {
// Тут идет сохранение переменных в EEPROM и часть из них передается обратно в главный контроллер
}
//Процедура вывода на дисплей. Кусок, отвечающий за вывод текущего пункта меню
void printDisplay() {
float* t = static_cast<float*>(menuItems[MenuID]._value); // Тут значение t = 0
CurrentValue = String(*t);
//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();
}
Переменные изначально получают свои значения с другого контроллера и отображаются на дисплее через menuINFO, могут изменяться пользователем через menuPRG.
У меня на экране названия пунктов выводятся, при нажатии кнопок “+” и “-” меняются пункты меню (названия пунктов), а значения во всех пунктах 0.00. Хотя в Serial значения переменных выводятся правильно. Следовательно ошибка в работе с указателями в строке:
float* t = static_cast<float*>(menuItems[MenuID]._value);