Давайте Вы таки нарисуете совё дерево и приведёте код.
Код ковыряю Ваш. Но я скорее как первый человек на первом плоту...
struct SMenuItem {
int id; // уникальный идентификатор данного элемента
SMenuItem * prev; // следующий элемент данного урвоня (nullptr - если этот - последний)
SMenuItem * next; // предыдущий элемент данного урвоня (nullptr - если этот - первый)
SMenuItem * parent; // адрес родительского элемента (nullptr - если нет родителей)
SMenuItem * children; // адрес списка элементов "подменю" (nullptrll - если это лист)
const char * itemText; // Название данного элемента (тут может быть не текст, а адрес картинки. Функция show знает, что с этим делать)
//
// Показать данный элемент
void show(void) {
// ДОПОЛНЕНИЕ - если мы активны, печатаем звёздочку, иначе - пробел
Serial.print(iAmActive ? '*' : ' ');
for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
if (ptr->parent) Serial.print(" ");
}
Serial.println(itemText);
}
//
// Показать всех детей данного элемента
// Параметр: глубина показа "внуков".
// Если 1 - только дети, если 2 - то и внук и т.д.
void showChildren(const int showGrandChilren = 1) {
if (! showGrandChilren) return;
for (SMenuItem * ptr = children; ptr; ptr = ptr->next) {
ptr->show();
ptr->showChildren(showGrandChilren - 1);
}
}
//
// выполнить действие, когда этот элемент выбран
void action(void) {
Serial.println("***********************************************");
Serial.print("***** The menu \"");
Serial.print(itemText);
Serial.println("\" is executed");
Serial.println("***********************************************");
}
//
// Удаление данного элемент из меню
/* void removeMe(void) {
//
// Если есть следующий элемент, то
// делаем мой предыдущий, его предыдущим
if (next) next->prev = prev;
//
// Если есть предыдущий
// то делаем наш следущий, его следущим
// Иначе говорим родителю, что наше next - его первый ребёнок.
if (prev) prev->next = next;
else parent->children = next;
} */
//
// Вставить себя как первого ребёнка объекта _parent
void insertAsFirstChild(SMenuItem & _parent) {
//
// Вставляем первого ребёнка после себя, а себя делаем первым ребёнком
next = _parent.children;
prev = nullptr;
_parent.children = this;
}
//
// Вставить себя после объекта _prev
void insertAfter(SMenuItem & _prev) {
//
// Ставим себя следующим объекту _prev, а его следующего - своим следующим
next = _prev.next;
_prev.next = this;
}
//
// Конструктор - создать элемент
SMenuItem(const char * const _itemText, SMenuItem * _prev = nullptr, SMenuItem * _parent = nullptr) {
itemText = _itemText;
prev = _prev;
parent = _parent;
children = nullptr;
next = nullptr;
//
// Если у нас есть родитель
if (parent) {
// если у родителя пока нет детей, записываемся началом списка детей.
if (parent->children == nullptr) parent->children = this;
}
//
// Если у нас есть предыдущий элемент
if (prev) {
// записываемся ему в "следующие"
prev->next = this;
}
//
// ДОПОЛНЕНИЕ - мы неактивны
iAmActive = false;
}
//
// ДОПОЛНЕНИЯ
//
bool iAmActive;
// Активировать (параметр - true) или деактивировать
// Возвращает адрес себя
SMenuItem * activate(const bool actDeact) {
iAmActive = actDeact;
return this;
}
// Перейти к следующему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goNext(void) {
// Если есть следующий, то активируем его
if (next) {
activate(false); // деактивируем себя
return next->activate(true);
}
return this;
}
// Перейти к предыдущему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goPrev(void) {
// Если есть предыдущий, то активируем его
if (prev) {
activate(false); // деактивируем себя
return prev->activate(true);
}
return this;
}
// Перейти к родителю (если есть)
// на самом деле переходим только в том случае, если у родителя есть
// ещё и свой родитель, т.к. нам не нужно переходиь к общему прародителю
// (в нашем случае к m0)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goParent(void) {
// Если есть родитель и у того тоже есть родитель, то активируем родителя
if (parent && parent->parent) {
activate(false); // деактивируем себя
return parent->activate(true);
}
return this;
}
// Если нет детей, то выполнить данный пункт
// А если есть дети, то перейти к первому ребёнку
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goChildOrRun(void) {
// Если нет детей, то выполняем данный элемент
if (! children) action();
else {
activate(false); // деактивируем себя
return children->activate(true);
}
return this;
}
};
// Корневой элемент всего меню. Он обычно невидимый.
// он родитель элементов верхнего уровня!
SMenuItem m0(""); // у этого нет ни предыдущего, ни родителя
SMenuItem m1("Menu 1", nullptr, & m0);
SMenuItem m2("Menu 2", & m1, &m0);
SMenuItem m3("Menu 3", & m2, &m0);
SMenuItem m21("Submenu 21", nullptr, & m2);// у этого нет предыдущего, но есть родитель
SMenuItem m22("Submenu 22", & m21, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m23("Submenu 23", & m22, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m24("Submenu 24", & m23, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m211("Submenu 211", nullptr, & m21);
SMenuItem m212("Submenu 212", & m211, & m21);
SMenuItem m213("Submenu 213", & m212, & m21);
SMenuItem m221("Submenu 221", nullptr, & m22);
SMenuItem m222("Submenu 222", & m221, & m22);
SMenuItem m223("Submenu 223", & m222, & m22);
SMenuItem m231("Submenu 231", nullptr, & m23);
SMenuItem m232("Submenu 232", & m231, & m23);
SMenuItem m233("Submenu 233", & m232, & m23);
SMenuItem m241("Submenu 241", nullptr, & m24);
SMenuItem m242("Submenu 242", & m241, & m24);
SMenuItem m243("Submenu 243", & m242, & m24);
//
// Ввод числа от 1 до 4
//
int prompt(void) {
int res = 0;
while (res < 1 || res > 4) {
Serial.println ("Введи число от 1 до 4: ");
Serial.println (" 1 - к предыдущему;");
Serial.println (" 2 - к следующему;");
Serial.println (" 3 - к родителю;");
Serial.println (" 4 - к ребёнку или (если нет детей, то выполнить)");
res = Serial.parseInt(); // Вводим число
while(Serial.available()) Serial.read(); // Вычитываем всё, что осталось
}
return res;
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(0xFFFFFFFF);
Serial.println("Веселье начинается!");
}
void loop() {
static SMenuItem * activeElement = m1.activate(true);
Serial.println("----------------------------------");
m0.showChildren(1); // Печатаем меню
// Обрабатываем комнду
// 1 - продвинуться по меню вверх, если есть куда (на том же уровне)
// 2 - продвинуться вниз, если есть куда (на том же уровне)
// 3 - перейти на уровень вверх (к родителю)
// 4 - если это не листик, то перейти на уровень вниз, а если листик, то выполнить этот пункт
switch (prompt()) {
case 1: activeElement = activeElement->goPrev(); break;
case 2: activeElement = activeElement->goNext(); break;
case 3: activeElement = activeElement->goParent(); break;
case 4: activeElement = activeElement->goChildOrRun(); break;
}
}
Дерево у меня простое.
Для "Подменю 22-24" иерархия такая-же как и для "Подменю 21". Т.е. по 3 дитя у каждого.
Но все это планируется на экран 0,96", а на нем помещается только 4 строки.
Поэтому план такой:
На начальном экране 3 пункта меню. Это все дети m0, поэтому я и обозвал "дети активного уровня"
При выборе 2го пункта отображаются 4 подменю, дети m2.
При выборе Подменю-1 отображаются дети m21
В качестве костыля я уже собирался прописывать каждый экран по отдельности... Всего-то 19 экранов :)
Да, знаю, что это строковая переменная и жрет дохрена памяти, но по другому не получается поймать.
В show() добавил
void show(void) {
// ДОПОЛНЕНИЕ - если мы активны, печатаем звёздочку, иначе - пробел
Serial.print(iAmActive ? '*' : ' ');
if (iAmActive == true){
curMenu = itemText;
}
for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
if (ptr->parent) Serial.print(" ");
}
Serial.println(itemText);
}
Ну и в loop()
void loop() {
static SMenuItem * activeElement = m1.activate(true);
Serial.println("----------------------------------");
m0.showChildren(1000); // Печатаем меню
Serial.println();
Serial.print ("Активное меню ");
Serial.println(curMenu);
Serial.println();
// Обрабатываем комнду
// 1 - продвинуться по меню вверх, если есть куда (на том же уровне)
// 2 - продвинуться вниз, если есть куда (на том же уровне)
// 3 - перейти на уровень вверх (к родителю)
// 4 - если это не листик, то перейти на уровень вниз, а если листик, то выполнить этот пункт
switch (prompt()) {
case 1: activeElement = activeElement->goPrev(); break;
case 2: activeElement = activeElement->goNext(); break;
case 3: activeElement = activeElement->goParent(); break;
case 4: activeElement = activeElement->goChildOrRun(); break;
}
}
В этом случае удается выловить название активного меню. Но, блин, памяти жрет на 4% больше. Это при условии, что кроме данного скетча ничего больше нет.
Скетч использует 4844 байт (15%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 1078 байт (52%) динамической памяти, оставляя 970 байт для локальных переменных. Максимум: 2048 байт.
Попытался внести изменения в структуру
struct SMenuItem {
int id; // уникальный идентификатор данного элемента
SMenuItem * prev; // следующий элемент данного урвоня (nullptr - если этот - последний)
SMenuItem * next; // предыдущий элемент данного урвоня (nullptr - если этот - первый)
SMenuItem * parent; // адрес родительского элемента (nullptr - если нет родителей)
SMenuItem * children; // адрес списка элементов "подменю" (nullptrll - если это лист)
const char * itemText; // Название данного элемента (тут может быть не текст, а адрес картинки. Функция show знает, что с этим делать)
const int * itemId; // ID активного пункта меню
Вот знаете, уже ведь несколько раз говорил, выкладывайте скетч, а Вы каждый раз отвечаете "как у Вас ... как у Вас". У меня таких проблем не было. Если Вы выкладываете скетч (целиком, чтобы я мог его просто запустить у себя и увидеть Вашу проблемы), то я, возможно, запущу его и посмотрю. Если же Вы каждый раз мне что-то говорите, чего я не понимаю, то чем я могу Вам помочь?
int curMenu;
struct SMenuItem {
int id; // уникальный идентификатор данного элемента
SMenuItem * prev; // следующий элемент данного урвоня (nullptr - если этот - последний)
SMenuItem * next; // предыдущий элемент данного урвоня (nullptr - если этот - первый)
SMenuItem * parent; // адрес родительского элемента (nullptr - если нет родителей)
SMenuItem * children; // адрес списка элементов "подменю" (nullptrll - если это лист)
const char * itemText; // Название данного элемента (тут может быть не текст, а адрес картинки. Функция show знает, что с этим делать)
//
// Показать данный элемент
void show(void) {
// ДОПОЛНЕНИЕ - если мы активны, печатаем звёздочку, иначе - пробел
Serial.print(iAmActive ? '*' : ' ');
if (iAmActive == true){
curMenu = itemText;
}
for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
if (ptr->parent) Serial.print(" ");
}
Serial.println("Меню");
}
//
// Показать всех детей данного элемента
// Параметр: глубина показа "внуков".
// Если 1 - только дети, если 2 - то и внук и т.д.
void showChildren(const int showGrandChilren = 1) {
if (! showGrandChilren) return;
for (SMenuItem * ptr = children; ptr; ptr = ptr->next) {
ptr->show();
ptr->showChildren(showGrandChilren - 1);
}
}
//
// выполнить действие, когда этот элемент выбран
void action(void) {
Serial.println("***********************************************");
Serial.print("***** The menu \"");
Serial.print(curMenu);
Serial.println("\" is executed");
Serial.println("***********************************************");
}
//
// Удаление данного элемент из меню
void removeMe(void) {
//
// Если есть следующий элемент, то
// делаем мой предыдущий, его предыдущим
if (next) next->prev = prev;
//
// Если есть предыдущий
// то делаем наш следущий, его следущим
// Иначе говорим родителю, что наше next - его первый ребёнок.
if (prev) prev->next = next;
else parent->children = next;
}
//
// Вставить себя как первого ребёнка объекта _parent
void insertAsFirstChild(SMenuItem & _parent) {
//
// Вставляем первого ребёнка после себя, а себя делаем первым ребёнком
next = _parent.children;
prev = nullptr;
_parent.children = this;
}
//
// Вставить себя после объекта _prev
void insertAfter(SMenuItem & _prev) {
//
// Ставим себя следующим объекту _prev, а его следующего - своим следующим
next = _prev.next;
_prev.next = this;
}
//
// Конструктор - создать элемент
SMenuItem(const char * const _itemText, SMenuItem * _prev = nullptr, SMenuItem * _parent = nullptr) {
itemText = _itemText;
prev = _prev;
parent = _parent;
children = nullptr;
next = nullptr;
//
// Если у нас есть родитель
if (parent) {
// если у родителя пока нет детей, записываемся началом списка детей.
if (parent->children == nullptr) parent->children = this;
}
//
// Если у нас есть предыдущий элемент
if (prev) {
// записываемся ему в "следующие"
prev->next = this;
}
//
// ДОПОЛНЕНИЕ - мы неактивны
iAmActive = false;
}
//
// ДОПОЛНЕНИЯ
//
bool iAmActive;
// Активировать (параметр - true) или деактивировать
// Возвращает адрес себя
SMenuItem * activate(const bool actDeact) {
iAmActive = actDeact;
return this;
}
// Перейти к следующему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goNext(void) {
// Если есть следующий, то активируем его
if (next) {
activate(false); // деактивируем себя
return next->activate(true);
// Serial.print (id);
}
return this;
}
// Перейти к предыдущему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goPrev(void) {
// Если есть предыдущий, то активируем его
if (prev) {
activate(false); // деактивируем себя
return prev->activate(true);
}
return this;
}
// Перейти к родителю (если есть)
// на самом деле переходим только в том случае, если у родителя есть
// ещё и свой родитель, т.к. нам не нужно переходиь к общему прародителю
// (в нашем случае к m0)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goParent(void) {
// Если есть родитель и у того тоже есть родитель, то активируем родителя
if (parent && parent->parent) {
activate(false); // деактивируем себя
return parent->activate(true);
}
return this;
}
// Если нет детей, то выполнить данный пункт
// А если есть дети, то перейти к первому ребёнку
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goChildOrRun(void) {
// Если нет детей, то выполняем данный элемент
if (! children) action();
else {
activate(false); // деактивируем себя
return children->activate(true);
}
return this;
}
};
// Корневой элемент всего меню. Он обычно невидимый.
// он родитель элементов верхнего уровня!
SMenuItem m0(""); // у этого нет ни предыдущего, ни родителя
SMenuItem m1(1, nullptr, & m0);
SMenuItem m2(2, & m1, &m0);
SMenuItem m3(3, & m2, &m0);
SMenuItem m21(21, nullptr, & m2);// у этого нет предыдущего, но есть родитель
SMenuItem m22(22, & m21, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m23(23, & m22, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m24(24, & m23, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m211(211, nullptr, & m21);
SMenuItem m212(212, & m211, & m21);
SMenuItem m213(213, & m212, & m21);
SMenuItem m221(221, nullptr, & m22);
SMenuItem m222(222, & m221, & m22);
SMenuItem m223(223, & m222, & m22);
SMenuItem m231(231, nullptr, & m23);
SMenuItem m232(232, & m231, & m23);
SMenuItem m233(233, & m232, & m23);
SMenuItem m241(241, nullptr, & m24);
SMenuItem m242(242, & m241, & m24);
SMenuItem m243(243, & m242, & m24);
//
// Ввод числа от 1 до 4
//
int prompt(void) {
int res = 0;
while (res < 1 || res > 4) {
Serial.println ("Введи число от 1 до 4: ");
Serial.println (" 1 - к предыдущему;");
Serial.println (" 2 - к следующему;");
Serial.println (" 3 - к родителю;");
Serial.println (" 4 - к ребёнку или (если нет детей, то выполнить)");
res = Serial.parseInt(); // Вводим число
while(Serial.available()) Serial.read(); // Вычитываем всё, что осталось
}
return res;
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(0xFFFFFFFF);
Serial.println("Веселье начинается!");
}
void loop() {
static SMenuItem * activeElement = m1.activate(true);
Serial.println("----------------------------------");
m0.showChildren(1000); // Печатаем меню
menu_select();
// Обрабатываем комнду
// 1 - продвинуться по меню вверх, если есть куда (на том же уровне)
// 2 - продвинуться вниз, если есть куда (на том же уровне)
// 3 - перейти на уровень вверх (к родителю)
// 4 - если это не листик, то перейти на уровень вниз, а если листик, то выполнить этот пункт
switch (prompt()) {
case 1: activeElement = activeElement->goPrev(); break;
case 2: activeElement = activeElement->goNext(); break;
case 3: activeElement = activeElement->goParent(); break;
case 4: activeElement = activeElement->goChildOrRun(); break;
}
}
void menu_select() {
switch (curMenu) {
case 1: Serial.println();
Serial.print ("Активное меню один");
Serial.println();
break;
case 2: Serial.println();
Serial.print ("Активное меню два");
Serial.println();
break;
}
}
Костыльным методом начал получать номер активного пункта меню :)
#include <iarduino_OLED_txt.h> // Подключаем библиотеку iarduino_OLED_txt.
//#include <EEPROM.h> // Подключаем работу с EEPROM
//////////////////////////////////////////////
//
extern uint8_t MediumFontRus[]; // Подключаем шрифт MediumFontRus.
iarduino_OLED_txt myOLED(0x3C); // Объявляем объект myOLED_1, указывая адрес первого дисплея на шине I2C: 0x3C.
//
extern uint8_t MediumFontRus[]; // Подключаем шрифт MediumFontRus.
extern uint8_t SmallFontRus[]; // Подключаем шрифт SmallFontRus.
// Если Вы не используете Кириллицу, то лучше подключить шрифты MediumFont и SmallFont, они займут меньше места в памяти программ.
#define DEBUG // Если не закомментированно, то, Serial.print() актевен! Только для отладки!!
// #define DEBUG_R // Настройка радио
////////////////////////////////////////////
// Переменные
////////////////////////////////////////////
boolean programMode = false; // режим программирования
boolean editMode = false; // режим изменения параметров
boolean bttnPressed = false; // флаг нажатия джойстика
int menuScreen; // Номер экрана меню
int curMenu; // выбранный пункт
int menuCommand;
const int8_t Xaxis = A0; // Определяем номер вывода, к которому подключен контакт оси Х джойстика
const int8_t Yaxis = A1; // Определяем номер вывода, к которому подключен контакт оси У джойстика
uint16_t Xvol = 0, Yvol = 0; // Задаём переменные, которые будут принимать значения, считанные с осей джойстика
// Заполняем меню
struct SMenuItem {
int id; // уникальный идентификатор данного элемента
SMenuItem * prev; // следующий элемент данного урвоня (nullptr - если этот - последний)
SMenuItem * next; // предыдущий элемент данного урвоня (nullptr - если этот - первый)
SMenuItem * parent; // адрес родительского элемента (nullptr - если нет родителей)
SMenuItem * children; // адрес списка элементов "подменю" (nullptrll - если это лист)
const char * itemText; // Название данного элемента (тут может быть не текст, а адрес картинки. Функция show знает, что с этим делать)
//
// Показать данный элемент
void show(void) {
// ДОПОЛНЕНИЕ - если мы активны, печатаем звёздочку, иначе - пробел
// Serial.print(iAmActive ? '*' : ' ');
if (iAmActive == true){
curMenu = itemText;
}
// for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
// if (ptr->parent) Serial.print(" ");
// }
// Serial.println("Меню");
}
//
// Показать всех детей данного элемента
// Параметр: глубина показа "внуков".
// Если 1 - только дети, если 2 - то и внук и т.д.
void showChildren(const int showGrandChilren = 1) {
if (! showGrandChilren) return;
for (SMenuItem * ptr = children; ptr; ptr = ptr->next) {
ptr->show();
ptr->showChildren(showGrandChilren - 1);
}
}
//
// выполнить действие, когда этот элемент выбран
void action(void) {
Serial.println("***********************************************");
Serial.print("***** The menu \"");
Serial.print(curMenu);
Serial.println("\" is executed");
Serial.println("***********************************************");
}
//
// Удаление данного элемент из меню
/* void removeMe(void) {
//
// Если есть следующий элемент, то
// делаем мой предыдущий, его предыдущим
if (next) next->prev = prev;
//
// Если есть предыдущий
// то делаем наш следущий, его следущим
// Иначе говорим родителю, что наше next - его первый ребёнок.
if (prev) prev->next = next;
else parent->children = next;
} */
//
// Вставить себя как первого ребёнка объекта _parent
void insertAsFirstChild(SMenuItem & _parent) {
//
// Вставляем первого ребёнка после себя, а себя делаем первым ребёнком
next = _parent.children;
prev = nullptr;
_parent.children = this;
}
//
// Вставить себя после объекта _prev
void insertAfter(SMenuItem & _prev) {
//
// Ставим себя следующим объекту _prev, а его следующего - своим следующим
next = _prev.next;
_prev.next = this;
}
//
// Конструктор - создать элемент
SMenuItem(const char * const _itemText, SMenuItem * _prev = nullptr, SMenuItem * _parent = nullptr) {
itemText = _itemText;
prev = _prev;
parent = _parent;
children = nullptr;
next = nullptr;
//
// Если у нас есть родитель
if (parent) {
// если у родителя пока нет детей, записываемся началом списка детей.
if (parent->children == nullptr) parent->children = this;
}
//
// Если у нас есть предыдущий элемент
if (prev) {
// записываемся ему в "следующие"
prev->next = this;
}
//
// ДОПОЛНЕНИЕ - мы неактивны
iAmActive = false;
}
//
// ДОПОЛНЕНИЯ
//
bool iAmActive;
// Активировать (параметр - true) или деактивировать
// Возвращает адрес себя
SMenuItem * activate(const bool actDeact) {
iAmActive = actDeact;
return this;
}
// Перейти к следующему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goNext(void) {
// Если есть следующий, то активируем его
if (next) {
activate(false); // деактивируем себя
return next->activate(true);
// Serial.print (id);
}
return this;
}
// Перейти к предыдущему (если есть)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goPrev(void) {
// Если есть предыдущий, то активируем его
if (prev) {
activate(false); // деактивируем себя
return prev->activate(true);
}
return this;
}
// Перейти к родителю (если есть)
// на самом деле переходим только в том случае, если у родителя есть
// ещё и свой родитель, т.к. нам не нужно переходиь к общему прародителю
// (в нашем случае к m0)
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goParent(void) {
// Если есть родитель и у того тоже есть родитель, то активируем родителя
if (parent && parent->parent) {
activate(false); // деактивируем себя
return parent->activate(true);
}
return this;
}
// Если нет детей, то выполнить данный пункт
// А если есть дети, то перейти к первому ребёнку
// Возвращает адрес нового (или старого, если не перешли) активного элемента
SMenuItem * goChildOrRun(void) {
// Если нет детей, то выполняем данный элемент
if (! children) action();
else {
activate(false); // деактивируем себя
return children->activate(true);
}
return this;
}
};
// Корневой элемент всего меню. Он обычно невидимый.
// он родитель элементов верхнего уровня!
SMenuItem m0(""); // у этого нет ни предыдущего, ни родителя
SMenuItem m1(1, nullptr, & m0);
SMenuItem m2(2, & m1, &m0);
SMenuItem m3(3, & m2, &m0);
SMenuItem m21(21, nullptr, & m2);// у этого нет предыдущего, но есть родитель
SMenuItem m22(22, & m21, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m23(23, & m22, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m24(24, & m23, & m2); // у этого есть и предыдущий, и родитель
SMenuItem m211(211, nullptr, & m21);
SMenuItem m212(212, & m211, & m21);
SMenuItem m213(213, & m212, & m21);
SMenuItem m221(221, nullptr, & m22);
SMenuItem m222(222, & m221, & m22);
SMenuItem m223(223, & m222, & m22);
SMenuItem m231(231, nullptr, & m23);
SMenuItem m232(232, & m231, & m23);
SMenuItem m233(233, & m232, & m23);
SMenuItem m241(241, nullptr, & m24);
SMenuItem m242(242, & m241, & m24);
SMenuItem m243(243, & m242, & m24);
//
// Ввод числа от 1 до 4
//
int prompt(void) {
int res = 0;
while (res < 1 || res > 4) {
Serial.println ("Введи число от 1 до 4: ");
Serial.println (" 1 - к предыдущему;");
Serial.println (" 2 - к следующему;");
Serial.println (" 3 - к родителю;");
Serial.println (" 4 - к ребёнку или (если нет детей, то выполнить)");
res = Serial.parseInt(); // Вводим число
while(Serial.available()) Serial.read(); // Вычитываем всё, что осталось
}
return res;
}
//
void setup(){ //
Serial.begin(115200); // Запускаем серийный порт
Screen_Intro();
delay (2000); // ждем 3 секунды
if (map(analogRead(A0), 0, 1023, 0, 255) <= 60) {
#ifdef DEBUG
Serial.println("Program mode = true");
#endif
programMode = true; // проверка нажата ли кнопка программирования, если да,
Screen_Program_intro(); // то переход в функцию программирования
}
} //
void loop(){ // Цикл программы
Xvol = map(analogRead(Xaxis), 0, 1023, 0, 255); // Считываем значения оси Х
Yvol = map(analogRead(Yaxis), 0, 1023, 0, 255); // Считываем значения оси У
if (programMode == true) { // Если в режиме программированивания
Joystic_Read_Programm(); // Проверяем что куда смотрит джойстик
static SMenuItem * activeElement = m1.activate(true);
m0.showChildren(1000); // Печатаем меню
// Обрабатываем комнду
// 1 - продвинуться по меню вверх, если есть куда (на том же уровне)
// 2 - продвинуться вниз, если есть куда (на том же уровне)
// 3 - перейти на уровень вверх (к родителю)
// 4 - если это не листик, то перейти на уровень вниз, а если листик, то выполнить этот пункт
switch (menuCommand) {
case 1: activeElement = activeElement->goPrev(); menuCommand = 0; break;
case 2: activeElement = activeElement->goNext(); menuCommand = 0; break;
case 3: activeElement = activeElement->goParent(); menuCommand = 0; break;
case 4: activeElement = activeElement->goChildOrRun(); menuCommand = 0; break;
}
}
} // loop
void Joystic_Read_Programm () { // Считывание показаний джойстика в режиме программирования
if (map(analogRead(A0), 0, 1023, 0, 255) > 60 && map(analogRead(A0), 0, 1023, 0, 255) < 210) {
bttnPressed = false;
}
if (map(analogRead(A1), 0, 1023, 0, 255) > 60 && map(analogRead(A1), 0, 1023, 0, 255) < 210) {
bttnPressed = false;
}
if (map(analogRead(A0), 0, 1023, 0, 255) <= 60) {
if (bttnPressed == false) {
menuCommand = 4; // нажата кнопка Enter,
bttnPressed = true;
menu_select();
#ifdef DEBUG
Serial.println("Enter");
#endif
}
}
if (map(analogRead(A0), 0, 1023, 0, 255) >= 210) {
if (bttnPressed == false) {
menuCommand = 3; // нажата кнопка Exit,
bttnPressed = true;
menu_select();
#ifdef DEBUG
Serial.println("Exit");
#endif
}
}
if (map(analogRead(A1), 0, 1023, 0, 255) <= 60) {
if (bttnPressed == false) {
menuCommand = 1; // нажата кнопка Up,
bttnPressed = true;
menu_select();
#ifdef DEBUG
Serial.println("Up");
#endif
}
}
if (map(analogRead(A1), 0, 1023, 0, 255) >= 210) {
if (bttnPressed == false) {
menuCommand = 2; // нажата кнопка Down,
bttnPressed = true;
menu_select();
#ifdef DEBUG
Serial.println("Down");
#endif
}
}
} // void Joystic_Read_Programm ()
void menu_select() {
switch (curMenu) {
//------------------------
case 1:
Screen_1();
break;
//------------------------
case 2:
Screen_2();
break;
//------------------------
case 3:
Screen_3();
break;
//------------------------
case 21:
Screen_21();
break;
//------------------------
case 22:
Screen_22();
break;
//------------------------
case 23:
Screen_23();
break;
//------------------------
case 24:
Screen_24();
break;
//------------------------
case 211:
Screen_211();
break;
//------------------------
case 212:
Screen_212();
break;
//------------------------
case 213:
Screen_213();
break;
//------------------------
case 221:
Screen_221();
break;
//------------------------
case 222:
Screen_222();
break;
//------------------------
case 223:
Screen_223();
break;
//------------------------
case 231:
Screen_231();
break;
//------------------------
case 232:
Screen_232();
break;
//------------------------
case 233:
Screen_233();
break;
//------------------------
case 241:
Screen_241();
break;
//------------------------
case 242:
Screen_242();
break;
//------------------------
case 243:
Screen_243();
break;
}
}
Отрисовку экранов меню вывел в отдельную вкладку
void Screen_Intro() {
myOLED.begin (); // Инициируем работу с дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.print ("Прюветь", OLED_C, 2); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.print ("Версия 2.0", OLED_C, 5); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
}
void Screen_Program_intro() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.print ("Режим", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.print ("настройки", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
delay (2000); // ждем 2 секунды
// Screen_1();
}
void Screen_1() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 1", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 2", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 3", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_2() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 1", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 2", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 3", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_3() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 1", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 2", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 3", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
}
void Screen_21() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 21", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 22", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 23", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_22() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 21", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 22", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 23", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_23() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 22", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 23", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 24", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_24() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 22", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 23", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 24", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
}
void Screen_211() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 211", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 212", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 213", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_212() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 211", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 212", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 213", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_213() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 211", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 212", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 213", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
// ---------==//==-------------
void Screen_221() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 221", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 222", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 223", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_222() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 221", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 222", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 223", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_223() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 221", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 222", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 223", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
// ---------==//==-------------
void Screen_231() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 231", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 232", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 233", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_232() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 231", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 232", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 233", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_233() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 231", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 232", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 233", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
// ---------==//==-------------
void Screen_241() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
myOLED.invText();
myOLED.print ("Меню 241", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 242", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 243", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_242() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 241", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 242", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 243", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
void Screen_243() {
myOLED.begin (); // Инициируем работу c дисплеем.
myOLED.setFont(MediumFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста на первом дисплее.
// myOLED.invText();
myOLED.print ("Меню 241", OLED_C, 1); // Выводим текст на первый дисплей по центру 3 строки (высота шрифта 2 строки, он займёт строки 2 и 3).
// myOLED.invText(false);
myOLED.invText();
myOLED.print ("Меню 242", OLED_C, 4); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
myOLED.invText(false);
// myOLED.invText();
myOLED.print ("Меню 243", OLED_C, 7); // Выводим текст на первый дисплей по центру 5 строки (высота шрифта 1 строка).
// myOLED.invText(false);
}
Понимаю, что с костылями, но работает. Почти.
Переход по пунктам меню при помощи джойстика, подключенного к выводам А0 и А1. Идея - считать командами перемещения по меню крайние положения стиков.
Никак не получается нормализовать перемещение по пунктам в соответствии с нажатиями джойстика.
У Вас, насколько я вижу, отводится по 6 переменных типа int/poiter на каждый элемент, включая листья, что соответствует 12 байт для AVR и 24 байта для SAM3/STM32. А, например, здесь: http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya на элемент меню приходится только по 6 байт при том, что для листьев память вообще не выделяется. Т.е. в реальном проекте на элемент, включая листья, может расходоваться вообще меньше одного байта памяти. Например, в проекте, над которым я сейчас работаю (и в котором меню уже не двухстрочное, а многострочное со скроллингом), при 948 байтах имеется 1070 листьев.
Тут я понимаю упрощение структуры. Или от switch все равно никуда не деться?
andriano пишет:
У Вас, насколько я вижу, отводится по 6 переменных типа int/poiter на каждый элемент, включая листья, что соответствует 12 байт для AVR и 24 байта для SAM3/STM32. А, например, здесь: http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya на элемент меню приходится только по 6 байт при том, что для листьев память вообще не выделяется. Т.е. в реальном проекте на элемент, включая листья, может расходоваться вообще меньше одного байта памяти. Например, в проекте, над которым я сейчас работаю (и в котором меню уже не двухстрочное, а многострочное со скроллингом), при 948 байтах имеется 1070 листьев.
Станно, но я вроде в меню не добавлял переменных... И перекидывать на другой проект... Ну это как-бы... А чего сразу туда не направили?
а что такого? Зачем тупо повторять за кем-то? чтобы понять общие принципы - полезно изучить 5-6 разных проектов
Вот знаете, я пытался изучить несколько разных проектов. Но 99% - это тупо swich и перелистываение на следующую страницу, ну, или обратно. По факту - один и тот-же проект с энкодером, который повторяется разными повторителями с разной степенью умения делать новое из чужого скеча... В данном проекте нашел то, что мне нужно и решил попытаться своим скудным умишком допилить его до нужного мне состояния. Уперся в стенку, получил направление в другой проект...
Но 99% - это тупо swich и перелистываение на следующую страницу, ну, или обратно.
Угу. Это как читатель комиксов решил начать перейти на книги и потом жалуется,как можно читать книги, ведь там нет картинок, одни буквы. И да меню это тупо swich, так же само как программы ветвления это тупо if.
Но 99% - это тупо swich и перелистываение на следующую страницу, ну, или обратно.
Угу. Это как читатель комиксов решил начать перейти на книги и потом жалуется,как можно читать книги, ведь там нет картинок, одни буквы. И да меню это тупо swich, так же само как программы ветвления это тупо if.
Ух как по тебе тут соскучились.
Да, я в какой-то степени читатель комиксов и прочих Веселых Картинок. (Журнал такой был в моем детстве). Но я прекрасно понимаю, что уровень знаний у всех разный. И форум, он как-бы подразумевает общение людей с разным уровнем знаний. И если более ...
Хотел написать очередной опус о помощи и поддержке знаниями, но понял, что мое мнение, в принципе, кроме меня никого не интересует, ничего писать не буду.
Идет Илья муромец по дороге, видит пещеру, подходит и говорит:
- Выходи Змей из пещеры, драться будем!
- Ну драться, так драться, а зачем мне в жопу орать!
vdm пишет:
Хотел написать очередной опус о помощи и поддержке знаниями, но понял, что мое мнение, в принципе, кроме меня никого не интересует, ничего писать не буду.
Скорее Вы как Илья Муромец приступили к задаче но не видите ее целиком. То что меню для Ардуино написать можно, вопросов нет. Но тем кому надо это меню тупо не имеют полного багажа знаний и даже не представляют объем этих знаний. И даже если вывалить рабочий код, то они в лесу не увидят леса. Несколько десятков деревьев да, а целиком нет. Вот и придется писать под компьютер графическую оболочку которое мышеклик переведет в код меню. Вот почему Nextoin нарисовала свою оболочку. Вот и вам надо такое же но под ваше железо. Но стоимость такой работы вы не потяните,
Во-во. Именно так. Мне не хочется разжевывать чем отличается программирование под МК от программированием под ПК. Так у вас 2 вариант. Да он работает. И все же этот вариант использовать под МК + флешка. Иначе нафиг такой паровоз.
Во-во. Именно так. Мне не хочется разжевывать чем отличается программирование под МК от программированием под ПК. Так у вас 2 вариант. Да он работает. И все же этот вариант использовать под МК + флешка. Иначе нафиг такой паровоз.
Но тем кому надо это меню тупо не имеют полного багажа знаний и даже не представляют объем этих знаний.
Поэтому им следует идти в пещеру, в которую Илья Муромец орал. Там светодиодиком мигать будут.
qwone пишет:
И даже если вывалить рабочий код, то они в лесу не увидят леса. Несколько десятков деревьев да, а целиком нет.
А если мышеклику разъяснить что это за странные палки в землю воткнуты, может он и догадается? Хотя нет. Это уже слишком! Путь идет учиться с азов и до самостоятельного написания меню. Только к тому времени меню то будет уже не актуально. Этот проект, к которому я пытаюсь меню прикрутить, у меня длится уже 2 года. Да, за это время можно выучить С++ до очень хорошего уровня, но у меня есть еще и другие цели и задачи в жизни.
Мда. Вот нафиг я на форум пришел. Очевидные вещи рассказывать.
Что такое программа. Это исполняемый код и некие данные. Ну define, константы, PROGMEMы, "данные" ну и так далее. В общем в исходник инкапсюлированы данные. Когда задачи не большие, то можно обойтись маленьким камнем и простым исполняемым кодом, в котором доля на объем данных минимальна. С ростом объема задач процентная доля данных увеличивается, увеличивается и в конце концов появляется новый язык данных. Конечно на языке данных можно удачно оперировать данными. Но как воткнуть в исходник заточенный под один язык данные форматированные под другой язык. Да andriano ваши структуры это зачаток нового языка данных заточных под меню. Удобно , если решите проблему инкасюляции этого языка.
Теперь про switch. Здесь все просто один case это одна страница и все что должно отображено на этой странице инкапсюлируется в это case.
ПС: Если кто ничего не понял, то тупо потрите мои посты и меня нет на форуме.
...С ростом объема задач процентная доля данных увеличивается, увеличивается и в конце концов появляется новый язык данных. Конечно на языке данных можно удачно оперировать данными. Но как воткнуть в исходник заточенный под один язык данные форматированные под другой язык. Да andriano ваши структуры это зачаток нового языка данных заточных под меню.
О чем Вы?
Это не только не другой язык (подразумевается, естественно, более высокоуровневый), это даже не Си++, это Ваш любимый "Си с классами". Тот, у которого одна инкапсуляция без полиморфизма и наследования (и который, как показывал Ворота, не уступает "чистому Си" по экономичности и компактности).
Насчет объема данных, как раз их у меня, минимум, в два, а на самом деле - в несколько раз меньше, чем у того варианта, который рассматривается в данной теме. Ну чем не вариант для МК? (кстати, для ПК я пишу по-другому)
Цитата:
Теперь про switch. Здесь все просто один case это одна страница и все что должно отображено на этой странице инкапсюлируется в это case.
ПС: Если кто ничего не понял, то тупо потрите мои посты и меня нет на форуме.
Лично я последней фразы (до ПС) не понял. В первую очередь потому, что слово "страница" многозначно, и совершенно непонятно, в каком именно значении Вы его здесь используете. Непонятно, какое отношение имеет страница к оператору case, непонятно, какое отношение имеет страница к меню, а потому совершенно нельзя понять, как Вы себе представляете взаимоотношение между case и меню (следовательно, почемe меню без case невозможно).
Нет, я не придираюсь, я действительно не могу понять.
Лично я последней фразы (до ПС) не понял. В первую очередь потому, что слово "страница" многозначно, и совершенно непонятно, в каком именно значении Вы его здесь используете. Непонятно, какое отношение имеет страница к оператору case, непонятно, какое отношение имеет страница к меню, а потому совершенно нельзя понять, как Вы себе представляете взаимоотношение между case и меню (следовательно, почемe меню без case невозможно).
Не поняли и ладно. Во многих знаниях есть свои печали. Вот только я не пойму, зачем мне это вам расжевывать. Мне что делать нечего и вам под вас учебник писать. Вы что за эту работу денег много отсыпите.
В общем-то правила хорошего тона рекомендуют: Сказал "А", говори "Б".
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
Читаю,читаю...Разве меню любое не следствие уже работающего скетча? То есть визуализация значений тех же переменных (или ещё чего там) в ходе исполнения программы. Я к тому, что легче сделать изделие работающее без экрана, потом его добавить, например смартфон через самодельное приложение и блютуз модуль от Ардуино (экран вроде бы есть, а вроде и нет).
Я к тому, что легче сделать изделие работающее без экрана, потом его добавить, например смартфон через самодельное приложение и блютуз модуль от Ардуино (экран вроде бы есть, а вроде и нет).
Блин. Ну что это за тупая мысль. Если два скетча работают, то если их объединить они тоже заработают.Сколько тем на этом форуме было.Есть мигающий светодиод, есть скетч с работающей кнопкой. А вот совмещая их выходит фигня. Конечно можно написать скетч где может работать и кнопка и светодиод одновременно, но тогда нет гарантии , что не появится заказ на дополнительную кнопку и светодиод. Вот и приходится так организовывать программу по особым правилам, что бы добавление чего-то, не отбрасывало эту работу в начало.
Цитата:"смартфон через самодельное приложение и блютуз модуль от Ардуино (экран вроде бы есть, а вроде и нет)." Это что. Делайте автомобили так, что бы любая блондинка любого пола могла сесть за руль. Ну сделали, ну продали. И в результате куча ДТП с травмами и убийствами.
Понятно я объясняю, что легче сделать и что в результате получить не одно и тоже.
Ну что за тупая мысль? Сначала мигают светодиодом, потом добавляют кнопку, потом светодиоды, кнопки, потом экран, потом меню на нём про светодиоды. Скетч один, а не два, три, четыре. Смысл меню - в блондинках или в экономии времени, не более. Впрочем я не настаиваю, глупость, так глупость :)
В общем-то правила хорошего тона рекомендуют: Сказал "А", говори "Б".
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
Речь в данном случае идет не о мифических секретных материалах, а в том, что Вы абсолютно голословно заявили, что написанный мною код, мол, не подходит для микроконтроллеров. На мой взгляд, я имею прав требовать, чтобы Вы либо объяснились, либо принесли свои извинения.
Речь в данном случае идет не о мифических секретных материалах, а в том, что Вы абсолютно голословно заявили, что написанный мною код, мол, не подходит для микроконтроллеров. На мой взгляд, я имею прав требовать, чтобы Вы либо объяснились, либо принесли свои извинения.
Мда. Вы со своими взглядами немного погодите. А то так можно дойти, что вы сейчас заявите:"что на ваш взгляд,мы здесь на форуме должны стреляться на дуели на смерть."
Я высказал на форуме свое мнение. Вы высказали свое. А теперь я должен по вашему мнению устраивать дуэли и извиняться. Похоже вас сударь занесло не туда. Обращайтесь к админам или модераторам.
В общем-то правила хорошего тона рекомендуют: Сказал "А", говори "Б".
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
таки, да...
японских шпионов полфорума.
у нас на вас по папке на каждого - чуть не в ту сторону пёрнете, за вами быро воронок в четыре утра прилетит.
Давайте Вы таки нарисуете совё дерево и приведёте код.
Код ковыряю Ваш. Но я скорее как первый человек на первом плоту...
Дерево у меня простое.
Для "Подменю 22-24" иерархия такая-же как и для "Подменю 21". Т.е. по 3 дитя у каждого.
Но все это планируется на экран 0,96", а на нем помещается только 4 строки.
Поэтому план такой:
На начальном экране 3 пункта меню. Это все дети m0, поэтому я и обозвал "дети активного уровня"
При выборе 2го пункта отображаются 4 подменю, дети m2.
При выборе Подменю-1 отображаются дети m21
В качестве костыля я уже собирался прописывать каждый экран по отдельности... Всего-то 19 экранов :)
Пока все сложновато для меня...
Никак не могу выудить ID активного пункта меню.
Обозначил переменную
Да, знаю, что это строковая переменная и жрет дохрена памяти, но по другому не получается поймать.
В show() добавил
Ну и в loop()
В этом случае удается выловить название активного меню. Но, блин, памяти жрет на 4% больше. Это при условии, что кроме данного скетча ничего больше нет.
И соответственно, в конструкторе
И при заполнении
Но это дает ошибку. Понимаю, что путаю мягкое с теплым, но мозх уже кипит. Сложновато на текущем уровне.
Вот знаете, уже ведь несколько раз говорил, выкладывайте скетч, а Вы каждый раз отвечаете "как у Вас ... как у Вас". У меня таких проблем не было. Если Вы выкладываете скетч (целиком, чтобы я мог его просто запустить у себя и увидеть Вашу проблемы), то я, возможно, запущу его и посмотрю. Если же Вы каждый раз мне что-то говорите, чего я не понимаю, то чем я могу Вам помочь?
Евгений, скетч ниже:
Костыльным методом начал получать номер активного пункта меню :)
А что такое активный пункт, напомните. Тот который курсором в данным момент выбран?
Да, выбранный.
На текущий момент получился вот такой монстр:
Отрисовку экранов меню вывел в отдельную вкладку
Понимаю, что с костылями, но работает. Почти.
Переход по пунктам меню при помощи джойстика, подключенного к выводам А0 и А1. Идея - считать командами перемещения по меню крайние положения стиков.
Никак не получается нормализовать перемещение по пунктам в соответствии с нажатиями джойстика.
Евгений, можете помочь с оптимизацией меню?
Я посмотрю, но через несколько дней, сейчас столько всего навалилось по работе.
Спасибо!
Евгений, можете помочь с оптимизацией меню?
У Вас, насколько я вижу, отводится по 6 переменных типа int/poiter на каждый элемент, включая листья, что соответствует 12 байт для AVR и 24 байта для SAM3/STM32. А, например, здесь: http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya на элемент меню приходится только по 6 байт при том, что для листьев память вообще не выделяется. Т.е. в реальном проекте на элемент, включая листья, может расходоваться вообще меньше одного байта памяти. Например, в проекте, над которым я сейчас работаю (и в котором меню уже не двухстрочное, а многострочное со скроллингом), при 948 байтах имеется 1070 листьев.
А что Вы подразумеваете под "оптимизацией"?
У Вас, насколько я вижу, отводится по 6 переменных типа int/poiter на каждый элемент, включая листья, что соответствует 12 байт для AVR и 24 байта для SAM3/STM32. А, например, здесь: http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya на элемент меню приходится только по 6 байт при том, что для листьев память вообще не выделяется. Т.е. в реальном проекте на элемент, включая листья, может расходоваться вообще меньше одного байта памяти. Например, в проекте, над которым я сейчас работаю (и в котором меню уже не двухстрочное, а многострочное со скроллингом), при 948 байтах имеется 1070 листьев.
перекидывать на другой проект... Ну это как-бы... А чего сразу туда не направили?
а что такого? Зачем тупо повторять за кем-то? чтобы понять общие принципы - полезно изучить 5-6 разных проектов
а что такого? Зачем тупо повторять за кем-то? чтобы понять общие принципы - полезно изучить 5-6 разных проектов
Вот знаете, я пытался изучить несколько разных проектов. Но 99% - это тупо swich и перелистываение на следующую страницу, ну, или обратно. По факту - один и тот-же проект с энкодером, который повторяется разными повторителями с разной степенью умения делать новое из чужого скеча... В данном проекте нашел то, что мне нужно и решил попытаться своим скудным умишком допилить его до нужного мне состояния. Уперся в стенку, получил направление в другой проект...
Квон! С возвращением!
qwone, welcome back!
Ух как по тебе тут соскучились.
Да, я в какой-то степени читатель комиксов и прочих Веселых Картинок. (Журнал такой был в моем детстве). Но я прекрасно понимаю, что уровень знаний у всех разный. И форум, он как-бы подразумевает общение людей с разным уровнем знаний. И если более ...
Хотел написать очередной опус о помощи и поддержке знаниями, но понял, что мое мнение, в принципе, кроме меня никого не интересует, ничего писать не буду.
Квон! я тоже рад тебя видеть!
Хуже нет пытаться разобраться в чужом коде, если половину не понимаешь.
Нужен исполнитель. Готов оплатить работу мозха. Писать на мой ник 161@mail.ru
И да меню это тупо swich
Специально посмотрел: том проекте, на который я ссылался выше, нет ни одного switch. Ни в одном из 16 файлов, включая "обвязку". (пост №161 этой темы)
Скорее Вы как Илья Муромец приступили к задаче но не видите ее целиком. То что меню для Ардуино написать можно, вопросов нет. Но тем кому надо это меню тупо не имеют полного багажа знаний и даже не представляют объем этих знаний. И даже если вывалить рабочий код, то они в лесу не увидят леса. Несколько десятков деревьев да, а целиком нет. Вот и придется писать под компьютер графическую оболочку которое мышеклик переведет в код меню. Вот почему Nextoin нарисовала свою оболочку. Вот и вам надо такое же но под ваше железо. Но стоимость такой работы вы не потяните,
Можно то же самое, только по-русски?
И при чем здесь флешка?
Но тем кому надо это меню тупо не имеют полного багажа знаний и даже не представляют объем этих знаний.
Поэтому им следует идти в пещеру, в которую Илья Муромец орал. Там светодиодиком мигать будут.
И даже если вывалить рабочий код, то они в лесу не увидят леса. Несколько десятков деревьев да, а целиком нет.
А если мышеклику разъяснить что это за странные палки в землю воткнуты, может он и догадается? Хотя нет. Это уже слишком! Путь идет учиться с азов и до самостоятельного написания меню. Только к тому времени меню то будет уже не актуально. Этот проект, к которому я пытаюсь меню прикрутить, у меня длится уже 2 года. Да, за это время можно выучить С++ до очень хорошего уровня, но у меня есть еще и другие цели и задачи в жизни.
И при чем здесь флешка?
Так надо!
Кому надо?
Кому надо?
Автору
Мда. Вот нафиг я на форум пришел. Очевидные вещи рассказывать.
Что такое программа. Это исполняемый код и некие данные. Ну define, константы, PROGMEMы, "данные" ну и так далее. В общем в исходник инкапсюлированы данные. Когда задачи не большие, то можно обойтись маленьким камнем и простым исполняемым кодом, в котором доля на объем данных минимальна. С ростом объема задач процентная доля данных увеличивается, увеличивается и в конце концов появляется новый язык данных. Конечно на языке данных можно удачно оперировать данными. Но как воткнуть в исходник заточенный под один язык данные форматированные под другой язык. Да andriano ваши структуры это зачаток нового языка данных заточных под меню. Удобно , если решите проблему инкасюляции этого языка.
Теперь про switch. Здесь все просто один case это одна страница и все что должно отображено на этой странице инкапсюлируется в это case.
ПС: Если кто ничего не понял, то тупо потрите мои посты и меня нет на форуме.
...С ростом объема задач процентная доля данных увеличивается, увеличивается и в конце концов появляется новый язык данных. Конечно на языке данных можно удачно оперировать данными. Но как воткнуть в исходник заточенный под один язык данные форматированные под другой язык. Да andriano ваши структуры это зачаток нового языка данных заточных под меню.
Это не только не другой язык (подразумевается, естественно, более высокоуровневый), это даже не Си++, это Ваш любимый "Си с классами". Тот, у которого одна инкапсуляция без полиморфизма и наследования (и который, как показывал Ворота, не уступает "чистому Си" по экономичности и компактности).
Насчет объема данных, как раз их у меня, минимум, в два, а на самом деле - в несколько раз меньше, чем у того варианта, который рассматривается в данной теме. Ну чем не вариант для МК? (кстати, для ПК я пишу по-другому)
Теперь про switch. Здесь все просто один case это одна страница и все что должно отображено на этой странице инкапсюлируется в это case.
ПС: Если кто ничего не понял, то тупо потрите мои посты и меня нет на форуме.
Нет, я не придираюсь, я действительно не могу понять.
Квон отшатнулся от неправославных лямбд и прильнул к посконному switch. Наверняка ещё отпустил бороду и на завалинке в ушанке посиживает.
Понятно.
В общем-то правила хорошего тона рекомендуют: Сказал "А", говори "Б".
А когда так: сказал "А", а когда потребовали объяснений, - сразу в кусты... Некрасиво это. За язык ведь никто не тянул.
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
Читаю,читаю...Разве меню любое не следствие уже работающего скетча? То есть визуализация значений тех же переменных (или ещё чего там) в ходе исполнения программы. Я к тому, что легче сделать изделие работающее без экрана, потом его добавить, например смартфон через самодельное приложение и блютуз модуль от Ардуино (экран вроде бы есть, а вроде и нет).
Цитата:"смартфон через самодельное приложение и блютуз модуль от Ардуино (экран вроде бы есть, а вроде и нет)." Это что. Делайте автомобили так, что бы любая блондинка любого пола могла сесть за руль. Ну сделали, ну продали. И в результате куча ДТП с травмами и убийствами.
Понятно я объясняю, что легче сделать и что в результате получить не одно и тоже.
Ну что за тупая мысль? Сначала мигают светодиодом, потом добавляют кнопку, потом светодиоды, кнопки, потом экран, потом меню на нём про светодиоды. Скетч один, а не два, три, четыре. Смысл меню - в блондинках или в экономии времени, не более. Впрочем я не настаиваю, глупость, так глупость :)
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
Я высказал на форуме свое мнение. Вы высказали свое. А теперь я должен по вашему мнению устраивать дуэли и извиняться. Похоже вас сударь занесло не туда. Обращайтесь к админам или модераторам.
Мне кажется, что "мнение" и "голословные обвинения" это не одно и то же.
Мнение может высказывать каждый, но если оно затрагивает чьи-то интересы, оно должно быть мотивированным.
Правило хорошего тона говорит что тон задает сам говорящий.В противном случае, он может A-разболтать лишнее,включая секретные материалы, Б- наболтать себе срок, B-и если правила хорошего тона навязывает тебе тот кто что-то от тебя услышал, то гляди в оба.
таки, да...
японских шпионов полфорума.
у нас на вас по папке на каждого - чуть не в ту сторону пёрнете, за вами быро воронок в четыре утра прилетит.
А теперь я должен по вашему мнению устраивать дуэли и извиняться. Похоже вас сударь занесло не туда. Обращайтесь к админам или модераторам.
администгация!
дуэльные пистолеты, секундантов, доктора и священника в тему!
Пух желает стреляться!
Вот так, пух - пух и нет Пуха.(