Сложное меню для отображения на LCD
- Войдите на сайт для отправки комментариев
Пт, 23/01/2015 - 18:37
Помогите организовать меню с использованием lcd keyboard shield
За основу взял скетч из инте и начал переделывать под себя, но появилась масса вопросов
#include <LiquidCrystal.h>
LiquidCrystal lcd( 8, 9, 3, 5, 6, 7 );
int currentMenuItem = 0;
int lastState = 0;
int z = 0;
void setup() {
lcd.begin(16, 2);
clearPrintTitle();
}
void loop() {
mainMenu();
}
void mainMenu() {
int state = 0;
int x = analogRead (0);
lcd.setCursor(0,0);
if (x < 100) {
//Право;
state = 2;
}
else if (x < 200) {
//Вверх
}
else if (x < 400){
//Вниз
}
else if (x < 600){
//Лево
state = 1;
}
else if (x < 800){
//Селект
state = 3;
}
if (currentMenuItem < 0 || currentMenuItem > 4) {
currentMenuItem = 0;
}
if (state != lastState) {
if (state == 1) {
//Если нажимаем верх
currentMenuItem = currentMenuItem - 1;
displayMenu(currentMenuItem);
}
else if (state == 2) {
//Если нажимаем вниз
currentMenuItem = currentMenuItem + 1;
displayMenu(currentMenuItem);
}
else if (state == 3) {
//Если нажимаем селект
selectMenu(currentMenuItem);
}
//Последнее состояние
lastState = state;
}
delay(5);
}
void displayMenu(int x) {
switch (x) {
case 1:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Otop");
lcd.setCursor(0, 1);
lcd.print("Nagrev");
lcd.setCursor(10, 0);
lcd.print("Podval");
lcd.setCursor(10, 1);
lcd.print("Clock");
break;
case 2:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Status otoplenie");
lcd.setCursor(0, 1);
lcd.print("L");
lcd.setCursor(2, 1);
lcd.print("H-");
lcd.setCursor(4, 1);
lcd.print("24");
lcd.setCursor(7, 1);
lcd.print("L-");
lcd.setCursor(9, 1);
lcd.print("18");
lcd.setCursor(12, 1);
lcd.print("N-");
lcd.setCursor(14, 1);
lcd.print("40");
break;
case 3:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Status nagrevatel");
lcd.setCursor(6, 1);
lcd.print("OFF");
break;
case 4:
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Status podval");
lcd.setCursor(0, 1);
lcd.print("T-");
lcd.setCursor(2, 1);
lcd.print("10");
lcd.setCursor(5, 1);
lcd.print("H-");
lcd.setCursor(7, 1);
lcd.print("40");
lcd.setCursor(11, 1);
lcd.print("V-");
lcd.setCursor(13, 1);
lcd.print("OFF");
break;
}
}
void clearPrintTitle() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ulica");
lcd.setCursor(0, 1);
lcd.print("24,35");
lcd.setCursor(11, 0);
lcd.print("Home");
lcd.setCursor(11, 1);
lcd.print("18,55");
}
void selectMenu(int x) {
switch (x) {
case 1:
lcd.clear();
lcd.print ("Selected Opt 1");
break;
case 2:
lcd.clear();
lcd.print ("Selected Opt 2");
break;
case 3:
lcd.clear();
lcd.print ("Selected Opt 3");
break;
case 4:
lcd.clear();
//clearPrintTitle();
lcd.print ("Selected Opt 4");
break;
}
}
в case 1 выводится четыре значения в виде
Otop Podval
Nagrev Clock
Как сделать выбор одного из пунтов и войти в него
Как заставить моргать пункт мену который сейчас активный
Будет раздел с включением отопления и установкой температуры
Sett otoplenie
ON (OFF) - или обно или другое значение. Как сделать сохранение?
Установка температуры
HIGH LOW tNASOS
24 20 40
как сделать сохранение этих параметров?
#include <MyMenu.h> #include <LiquidCrystal.h> LiquidCrystal lcd(3, 5, 10, 11, 12, 13); #define NUM_ITEMS 32 //length of items array include submenu headers MItm items[NUM_ITEMS] = { MItm("SHOPPING LIST",0,0), //main header always 0,0 MItm("Fruits",1), //1 is target submenu index MItm("Veggies",2), MItm("Other",3), MItm("FRUITS LIST",1,0), //this is the submenu with index 1 (0 is previous menu index) MItm("Apple",50), // if there is no "50" submenu callback will run MItm("Orange",60), MItm("Banana",70), MItm("Pear",80), MItm(" <<BACK",0), //means "go to submenu with index 0" MItm("VEGGIES LIST",2,0), MItm("Tomato",90), MItm("Potato",100), MItm("Smth else",110), MItm(" <<BACK",0), MItm("Other",3,0), MItm("Meat",4), MItm("Fish",5), MItm(" <<BACK",0), MItm("MEAT",4,3), // notice that 3 in prev-index now not zero MItm("Chicken",6), MItm("Bacon",7), MItm("Beef",8), MItm("SHOW FISH",5), MItm(" <<BACK",3), MItm(" <<MAIN",0), MItm("FISH",5,3), MItm("Clownfish",9), MItm("Shark",10), MItm("SHOW MEAT",4), MItm(" <<BACK",3), MItm(" <<MAIN",0) }; Menu menu(items,NUM_ITEMS,&lcd,menuCallback); void setup() { //set button pins pinMode(42,INPUT); pinMode(44,INPUT); pinMode(46,INPUT); pinMode(48,INPUT); //initialize lcd lcd.begin(20,4); //show main menu on screen menu.goMain(); } void loop() { if(digitalRead(42) == HIGH){ menu.goBack(); delay(100); } if(digitalRead(44) == HIGH){ menu.goDown(); delay(100); } if(digitalRead(46) == HIGH){ menu.goUp(); delay(100); } if(digitalRead(48) == HIGH){ menu.goNext(); delay(100); } delay(100); } void menuCallback(int idx){ //do something according to index of item selected if(idx < 10){ lcd.clear(); lcd.setCursor(2,1); lcd.print("NO MEAT n FISH!"); delay(1000); }else if(idx == 10){ //selected "shark" int i=16; while(i >= 0){ lcd.clear(); lcd.setCursor(0,2); lcd.print("-^-^-^-^-^-^-^-^-^-^"); lcd.setCursor(i,1); lcd.print("/l"); delay(300); i--; } }else{ lcd.clear(); lcd.setCursor(2,2); lcd.print(String(idx)+" pressed"); delay(1000); } menu.goLast(); //return to last viewed menu }не мучайтесь, человек хороший уже это сделал....
вот библиотека https://yadi.sk/d/1P5V6ryreC6b8
Я для своих приложений делаю так.
составляю таблицу всех возможных событий и состояний.
Если при нажатии кнопки SET был главный экран, то переходим в настройки, если после этого нажать ^ то перейдем к настройке параметра №1 (вообщем на перекрестке события и состояния находиться ссылка на следующее состояние.
А чтобы код компактнее был вместо вот этого
080 case 2: 081 lcd.clear(); 082 lcd.setCursor(0, 0); 083 lcd.print("Status otoplenie"); 084 lcd.setCursor(0, 1); 085 lcd.print("L"); 086 lcd.setCursor(2, 1); 087 lcd.print("H-"); 088 lcd.setCursor(4, 1); 089 lcd.print("24"); 090 lcd.setCursor(7, 1); 091 lcd.print("L-"); 092 lcd.setCursor(9, 1); 093 lcd.print("18"); 094 lcd.setCursor(12, 1); 095 lcd.print("N-"); 096 lcd.setCursor(14, 1); 097 lcd.print("40"); 098 break;Создайте функцию в которю передавайте текст и координаты его на экране.
void PrTxt(текст,координата х, координата y){
lcd.setCursor(x,y);
lcd.print(text);
}
И вместо этого
lcd.setCursor(14,1);
lcd.print("40");
Будите писать
PrTxt("40",14,1); както так.
И вообще все пофторяющиеся действия передовайте в функции, так и ошибки легче найти.
Например Вам необходимо кнопками + и - изменять разные переменные. Вызываете функцию в которую будете передовать эту переменную, шаг её изменения и направление изменения, а она вернет новое значение переменной.
например так.
//param-переменная, maks-максимальное значение, mini-минимальное значение, st-шаг изменения, pm-0-уменьшать 1-увеличивать //вызов temp=changeP(temp,80,5,1,1) увеличить temp с шагом 1 и пределом 80 //эта функция вызываеться и для увеличения и для уменьшения параметра //и не надо для каждой переменной и кнопки прописывать byte changeP (byte param, byte maks, byte mini,byte st, boolean pm) {//изменение параметра if (pm == 1) { param=param+st; if (param > maks) { param = maks; } } if (pm == 0) { param=param-st; if (param < mini) { param = mini; } } return param; }код полегчает немного
По поводу выбора пункта:
в case 1 выводится четыре значения в виде
Otop Podval
Nagrev Clock
Как сделать выбор одного из пунтов и войти в него.
Ресуете табличку, как я выше предложил, по вертикали слева все Ваши "экраны", а по горизонтали кнопочки или события (внешнее прерывание например или таймер сработал), а на их перекрестке новое состояние или действие, Фантазируйте.
У меня допустем в одном проекте при включении доп вункции открываеться доступ к её настройкам, а пока выключена и настройки незачем показывать.
Надеюсь информация поможет.
помогите с меню вот такие ошибки выдает,
не соображу что делать...
menu_2_test:46: error: 'menuCallback' was not declared in this scope Menu menu(items,NUM_ITEMS,&lcd,menuCallback); ^ exit status 1 'menuCallback' was not declared in this scopeили поделитесь нормально рабочей библиотекой для меню с примером
немного разобрался вот библиотека https://cloud.mail.ru/public/KEXw/rAXWQj99V
ее получилось запусттить на новой версие IDE
кто может подсказать как выводить вот такие пункты меню сразу с показаниями датчиков к примеру так VAR1= 220
я пробовал так и много еще как но не в какую
1MItm("VAR1="+ varDell,4),но выдает пустую строку, и переменную не могу соединить со строкой
вот скетч
#include <MyMenu.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,16,2); //инициализируем 2х строчный дисплей #define NUM_ITEMS 17 //длина элементов массива включают в себя заголовки подменю int pinButton = A0 ; int varDell = 123; MItm items[NUM_ITEMS] = { MItm("GENERAL MENU",0,0), //основной заголовок всегда 0,0 MItm("Date Time",11), //1 Индекс подменю целевой 1 is target submenu index MItm("New Date Time",12), // MItm("Other",13), // MItm("Banana",14), MItm("Pear",1), MItm(" <<BACK",0), MItm("SUB MENU 1",1,0), //это подменю с индексом 1 (0 предыдущий индекс меню) // MItm("Banana",6), MItm("Pear",2), MItm(" <<BACK",0), MItm("SUB MENU 2",2,0), // MItm("GENERAL MENU 4",0), // MItm("Banana",8), MItm("Pear",3), MItm(" <<BACK",0), // MItm("Pear",80), MItm("SUB MENU 3",3,0), // заметить, что в 3-индекс предыд теперь не нулевой MItm("VAR1="+ varDell,4), MItm("VAR2=",5), MItm("VAR3=",6), MItm("VAR4=",7), MItm(" <<BACK",0) }; Menu menu(items,NUM_ITEMS,&lcd,menuCallback); void setup() { Serial.begin(9600); //initialize lcd lcd.init(); // инициализация LCD lcd.backlight(); // включаем подсветку //show main menu on screen menu.goMain(); } void loop() { int i = getPressedButton(); if(3 == i){ menu.goBack(); delay(100); } if(6 == i){ menu.goDown(); delay(100); } if(4 == i){ menu.goUp(); delay(100); } if(7 == i){ menu.goNext(); delay(100); } delay(100); } void menuCallback(int idx){ //сделать что-то в соответствии с индексом выбранного элемента ///// if(idx == 4){ //selected "shark" atoi int tt = nevValMenu(); lcd.clear(); lcd.setCursor(0,0); lcd.print(tt) ; delay(2000); //lcd.print("RET"+ nevValMenu()); } ///// if(idx < 5){ lcd.clear(); lcd.setCursor(2,0); lcd.print("NO MEAT n FISH!"); delay(1000); if(idx == 1){ Serial.println("id=1");} if(idx == 2){ Serial.println("id=2");} if(idx == 0){ Serial.println("id=0");} }else if(idx == 5){ //selected "shark" int i=16; while(i >= 0){ lcd.clear(); lcd.setCursor(0,2); //lcd.print(arrMenuChar[i]); lcd.setCursor(i,1); lcd.print("/l"); delay(300); i--; } } else{ lcd.clear(); lcd.setCursor(2,2); lcd.print(String(idx)+" pressed"); delay(1000); } menu.goLast(); //return to last viewed menu } int nevValMenu(){ // выведем значение на 2 строку 12+ клетку int i=1; int z = -1; int y=0; lcd.setCursor(11,1) ; lcd.print(i); while(y != 2){//выход y = getPressedButton(); if (y == 4)// вверх {i++; lcd.setCursor(11,1) ; lcd.print(i); } if (y == 6)// низ {i--; if (i<0){i=0;} lcd.setCursor(11,1) ; lcd.print(i);} if (y == 3)// лево {} if (y == 7) //право {} if (y == 5) //сохранить { z = i; } delay(100); if(i==9){lcd.setCursor(12,1); lcd.print(' ');} if(i==99){lcd.setCursor(13,1); lcd.print(' ');} if(i==999){lcd.setCursor(14,1); lcd.print(' ');} if(i==9999){lcd.setCursor(15,1); lcd.print(' ');} } return z ; } //процедура определения нажатой кнопки int getPressedButton(){ int val = analogRead(pinButton); if (val>100){ if (val < 472){Serial.println("val=1"); return 1; } if (val < 515){Serial.println("val=2"); return 2; } if (val < 567){Serial.println("val=3"); return 3; } if (val < 630){Serial.println("val=4"); return 4; } if (val < 710){Serial.println("val=5"); return 5; } if (val < 810){Serial.println("val=6"); return 6; } if (val < 1025){Serial.println("val=7"); return 7; } while(analogRead(pinButton)>100) ; } else return 0; }кто что знает подскажите пожалуйсто,
сильно не критикуйте счетчь тестовый пока основное пробую, будет все переделано
itoa(); sprintf();
все таки получилось сложить 2 кар строки
вот теперь идея такая для экономии памяти переменную "str1=" собирать из кусков допустим "str" + "1=" а для второго пункта меню менять только вторую переменную на "2=" и так далее, так вот задача, с помощью функции strcat можно сложить только 2 переменные а мне нужно еще значение этой переменной тоесть 3 переменные сложить вмести, есть идеи как это сделать правильно?
или же подскажите куда можно запихнуть все названия пунктов меню чтобы разгрузить немного память ардуинки? может на флешку можно это записатья?
Может кому пригодится подправленная билиотека MyMenu и мой пример. Отдельная благодарность разработчику библиотеки Denis Peshekhonov.
Подкорректированная библиотека
Мой пример меню:
#include <MyMenu.h> void menuCallback(int idx); LiquidCrystal_I2C lcd(0x27,20,4); // инициализируем 4х строчный дисплей #define NUM_ITEMS 27 // количество пунктов в меню включая все заголовки всех подменю (NUM_ITEMSNUM_ITEMS) boolean flEdit = false; // глобальная переменная ("флаг") отвечающая за режим редактирования параметров float Var; // глобальная переменная содержащая текущий редактируемый параметр static MItm items[NUM_ITEMS] = { MItm("SETTINGS",0,0), // основной заголовок меню всегда 0,0 MItm("Temperature",1), // Индекс меню 1. Индексы заголовков должны быть в диапазоне до 100. MItm("Date Time", 2), // после 100 идут индексы разделов подменю MItm("Motor time", 3), MItm("Moisture", 4), MItm("PID", 5), // всего основных 5 меню MItm("Temp",1,0), // подменю "Temperature"с индексом 1 тк у Temperature индекс 1 (0 предыдущий индекс меню) MItm("Temp.C", 100), // содержимое раздела "Temp" это "Temp.С" с инднксом 100. Индекс 100 также является адресом памяти хранения значения float MItm("OffSet", 104), // Индекс 104 индефикация миню и 104 также является адресом памяти хранения значения floatfloat (разница от соседних меню должна быть в 4 байта) MItm(" <<BACK",0), MItm("Date & Time",2,0), // MItm("Date",108), MItm("Time",112), MItm(" <<BACK",0), MItm("Motor time",3,0), // MItm("Cycl", 116), MItm("Long", 220), // тут непонятно но на ячейке 120 падает программ, скорее битая ячейка памяти MItm(" <<BACK",0), MItm("Moisture %",4,0), // MItm("On ", 124), MItm("Off", 128), MItm(" <<BACK",0), MItm("PID",5,0), // MItm("P", 132), MItm("I", 136), MItm("D", 140), MItm(" <<BACK",0) }; Menu menu(items,NUM_ITEMS,&lcd,menuCallback); //создаем меню void setup() { digitalWrite(4,HIGH); // кнопка "вниз" digitalWrite(5,HIGH); // кнопка "вверх" digitalWrite(6,HIGH); // кнопка "выбрать" lcd.init(); lcd.backlight(); menu.goMain(); //отображаем на экране главное меню } void loop() { keyBoard(); //опрос клавиатуры в основном цикле } void menuCallback(int idx){ //эта функция будет вызвана, если подменю с индексом idx нет //и нужно совершить действие, а не уходить на еще один уровень в меню if (idx >= 100) { lcd.setCursor(19,0); // устанавливаем курсор в правый верхний угол экрана if (!flEdit) {flEdit=true; lcd.print("*"); //digitalWrite(13, HIGH); // показываем звездочкой "*" режим редактирования } else { flEdit=false; lcd.print(" "); //digitalWrite(13, LOW); // убираем звездочку "*" при отсутствии действия редактирования menu.EEPROM_float_write(idx, Var); } // сохраняем параметр в памят EEprom Var = menu.EEPROM_float_read(idx); // читаем из памяти EEprom значение для редактирования } else { menu.goLast();} // возращаемся в главное меню } void showChangeDispVar(){ // функция вывода значения при редактировании его lcd.setCursor(15,menu.cur_sub_index+1); // всегда 15-я колонка, строка берется от положения курсора ">" lcd.print(String(Var)); // выводим значение } void keyBoard(){ // функция обработки нажатия кнопок (всего 3 шт.) if(digitalRead(4) == LOW){ // нажата кнопка "вниз" if (!flEdit) {menu.goDown();} // функция перемещает курсор на один пункт вниз если нет режима редактирования else {Var-=0.1; showChangeDispVar();} // если режим редактирование, то уменьшаем (-) значение на 0.1 } if(digitalRead(5) == LOW){ // нажата кнопка "вверх" if (!flEdit) {menu.goUp();} // функция перемещает курсор на один пункт вверх если нет режима редактирования else {Var+=0.1;showChangeDispVar();} // если режим редактирование, то увеличиваем (+) значение на 0.1 } if(digitalRead(6) == LOW){ // нажата кнопка "вперед" или "выбрать пункт" menu.goNext(); // функция переходит на новый уровень подменю или вызывает callback - функцию } delay(180); // задерка между срабатыванием кнопок (и) }