Сложное меню для отображения на LCD

ramzes.ru
Offline
Зарегистрирован: 19.11.2014


Помогите организовать меню с использованием 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

как сделать сохранение этих параметров?

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012
#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
}
#include <MyMenu.h> !!!!!!!!!!!!!!!!!!!!!!!!!!!!
SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

не мучайтесь, человек хороший уже это сделал....

вот библиотека https://yadi.sk/d/1P5V6ryreC6b8

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Я для своих приложений делаю так.

составляю таблицу всех возможных событий и состояний.

Если при нажатии кнопки 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

Как сделать выбор одного из пунтов и войти в него.

Ресуете табличку, как я выше предложил, по вертикали слева все Ваши "экраны", а по горизонтали кнопочки или события (внешнее прерывание например или таймер сработал), а на их перекрестке новое состояние или действие, Фантазируйте.

У меня допустем в одном проекте при включении доп вункции открываеться доступ к её настройкам, а пока выключена и настройки незачем показывать.

Надеюсь информация поможет.

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

помогите с меню вот такие ошибки выдает,

не соображу что делать...

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

или поделитесь нормально рабочей библиотекой для меню с примером

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

немного разобрался вот библиотека https://cloud.mail.ru/public/KEXw/rAXWQj99V

ее получилось запусттить на новой версие IDE 

кто может подсказать как выводить вот такие пункты меню сразу с показаниями датчиков к примеру так VAR1= 220

я пробовал так и много еще как но не в какую

1 MItm("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;
}

кто что знает подскажите пожалуйсто,

сильно не критикуйте счетчь тестовый пока основное пробую, будет все переделано

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

itoa(); sprintf();

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

все таки получилось сложить 2 кар строки

char dd[16]="str1=";
char cc[6]="220";

 lcd.print(strcat(dd,cc))   ; 

вот теперь идея такая для экономии памяти переменную "str1=" собирать из кусков допустим "str" + "1=" а для второго пункта меню менять только вторую переменную на "2=" и так далее, так вот задача, с помощью функции strcat можно сложить только 2 переменные а мне нужно еще значение этой переменной тоесть 3 переменные сложить вмести, есть идеи как это сделать правильно?

или же подскажите куда можно запихнуть все названия пунктов меню чтобы разгрузить немного память ардуинки? может на флешку можно это записатья?

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015
strcat(aa,strcat(bb,cc));

 

lock95
Offline
Зарегистрирован: 15.02.2016

Может кому пригодится подправленная билиотека 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);                               // задерка между срабатыванием кнопок (и)
}