Millis, кнопки и меню

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

 

Всем добрый вечер.

Делаю скетч по выводу ардуиной меню на дисплей LCD 2004 I2C. Управляют всем 4 кнопки(5 пока не задействовал, в процессе).

Дисплей и упрощенный алгоритм работы скетча на рисунках. 


 

 

 

Вся сложность работы в том, что по нажатию на UP и  к обоим текущим числам + 1 , на DOWN  к обоим - 1. Надо бы чтобы к определенному. Решил для этих целей задействвать кнопку LEFT. Она будет отвечать за выбор числа. Думаю добавить мигание(чтобы как то обозначить текущее число?)  и разбить каждое число на сотни, десятки и еденицы и менять это кнопками, а то уж очень долго например если задано  200 а текущее 100 прибавлять по 1. Нужен ваш совет в реализации кода или доработке алгоритм. Заранее спасибо

Приложен код

// скетч меню 

#include <Wire.h> // библиотека для управления устройствами по I2C 
#include <LiquidCrystal_I2C.h> // подключаем библиотеку для LCD 1602

LiquidCrystal_I2C lcd(0x3f,20,4); // присваиваем имя lcd для дисплея 20х4

//Назначаем пины кнопок управления
#define BUTTON_OK 8
#define BUTTON_LEFT 9
#define BUTTON_RIGHT 12
#define BUTTON_UP 10
#define BUTTON_DOWN 11

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
byte t_bottom = 10; byte t_top = 90;
byte p_bottom = 20; byte p_top = 80; 
byte kp1 = 50;      byte kp2 = 50; 
byte ki1 =  0;      byte ki2 = 0; 
byte kd1 =  0;      byte kd2 = 0; 
byte aaa =  0;      byte AAA = 0;
byte bbb =  0;      byte BBB = 0;

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 7; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  byte *val1;
};

struct MENU_2{ //
char name2[13];
  byte *val2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   temp_t =", &t_bottom,
"  power_t =", &p_bottom,
"      kP1 =", &kp1,
"      kI1 =", &ki1,
"      kD1 =", &kd1,
"       aa =", &aaa,
"       bb =", &bbb
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_2 nMenu[CountMenu] = {
"   temp_b =", &t_top,
"  power_t =", &p_top,
"      kP2 =", &kp2,
"      kI2 =", &ki2,
"      kD2 =", &kd2,
"       AA =", &AAA,
"       BB =", &BBB
};
 
//функция выполнения меню
void menu(){
  
  //следующий пункт меню по кругу
  if (State_Right == LOW && (millis() - ms_button)>200) 
    {
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
    }
    
  //  bOk  выход из меню
  if (State_Ok == LOW && ( millis() - ms_button)>200)
    {
      ms_button =  millis();
      // выход из меню
      // переход к начальному меню
    }

  //  bUp  +1 значение
  if (State_Up == LOW && ( millis() - ms_button)>200)
    {
      ms_button =  millis();
      (*mMenu[curMenu].val1)++;
      (*nMenu[curMenu].val2)++;
    }


  //  bDown -1 значение
  if (State_Down == LOW && ( millis() - ms_button)>200)
    {
      ms_button =  millis();
      (*mMenu[curMenu].val1)--;
      (*nMenu[curMenu].val2)--;
    }

  //вывод использую буфер
  char ch1[16];
  char ch2[16]; 
  
  snprintf(ch1, 16, "%d         ", *mMenu[curMenu].val1); 
  snprintf(ch2, 16, "%d         ", *nMenu[curMenu].val2);
  
  lcd.setCursor(0,2);  lcd.print(mMenu[curMenu].name1);
  lcd.setCursor(12,2); lcd.print(ch1); 
   
  lcd.setCursor(0,3);  lcd.print(nMenu[curMenu].name2);
  lcd.setCursor(12,3); lcd.print(ch2);
}


void setup() {

  lcd.begin();
  lcd.backlight(); 

  lcd.setCursor(1, 1);
  lcd.print("ARDUINO REWORK v0.3");
  
  delay(1000); // wait for MAX chip to stabilize
  lcd.clear();
 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_LEFT, INPUT);  digitalWrite(BUTTON_LEFT, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_OK, INPUT);     digitalWrite(BUTTON_OK, HIGH); //подключаем подтягивающий резистор
  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Left = digitalRead(BUTTON_LEFT);
  State_Right = digitalRead(BUTTON_RIGHT);
  State_Ok = digitalRead(BUTTON_OK);
  //-------------------------------------/
  
  //вывод меню
  if (b_ShowmMenu) {
    menu();
  } else {
      //фиксируем момент нажатия кнопки "ОК" + защита от дребезга
      if (State_Ok==LOW && !button_state && ( millis() - ms_button)>100) 
      {
        ms_button =  millis();
        button_state = true;
        button_long_state = false;
      }
      //держим "ОК" 3секунды и заходим в меню настроек      
      if (State_Ok==LOW && !button_long_state && ( millis() - ms_button)>3000)
      {
        button_long_state = true;
        button_state = false;
        
        b_ShowmMenu = 1;
        lcd.clear(); 
      }
  }
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Сделано 7 подменю для каждого числа, по нажатию на BUTTON_RIGHT переход к следущему.  

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Конкретно вот в этом месте запутался

//  bUp  +1 значение
  if (State_Up == LOW && ( millis() - ms_button)>200)
    {
      ms_button =  millis();
      (*mMenu[curMenu].val1)++;
      (*nMenu[curMenu].val2)++;
    }


  //  bDown -1 значение
  if (State_Down == LOW && ( millis() - ms_button)>200)
    {
      ms_button =  millis();
      (*mMenu[curMenu].val1)--;
      (*nMenu[curMenu].val2)--;
    }

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Любой совет будет не лишним)

vvadim
Offline
Зарегистрирован: 23.05.2012

а вы не думали сначала научиться правильно работать с кнопками.
или какую то библиотеку для опроса кнопок прицепить...

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Вначале пробывал с Bounce, но не получилось сделать длинное нажатие, поэтому сделал как видите. Да обработка кнопок кривая, подскажите как улучшить?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

И похоже вы "потеряли" свою тему http://arduino.ru/forum/programmirovanie/lcd-i2c-displei-i-obrabotka-knopok#comment-332235

ПС: Я там в конце вариант скетча скидывал. #28   Нажатие в 3 сек - это что 33 утюга сидели на подоконике == "явка провалена"

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

 if (State_Down == LOW && ( millis() - ms_button)>200) // сам знаю что это не решение

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Да но я решил сделать по новому, qwone спасибо но буду уже этот код дорабатывать, а не писать заново

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Я  новичок в программировании , поэтому классы не осилил, буду дорабатывть уже созданный и отлаженный код

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

qwone пишет:

  Нажатие в 3 сек - это что 33 утюга сидели на подоконике == "явка провалена"

Мне так проще, код будет дополняться новыми режимами, поэтому и нажатие в 3 сек 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Любые идеи приветствуются, пишите

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018
  // это аналогично прежнему - тут есть защита от дребезга!!!
if (State_Up == LOW)
    {
     if ( millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].val1)++;
      (*nMenu[curMenu].val2)++;
     }
    }

за счет millis() устраняется ложное срабатывание

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018
 // может так
// изначально flag = 0
  if (State_Left == LOW) 
    {
      if ( millis() - ms_button > 200) {
      ms_button =  millis();
  
      // по нажатию флаг !flag 
      // функция мигания blink разрешена
      }
    }
  //  bUp  +1 значение
  if (State_Up == LOW)
    {
     if ( millis() - ms_button > 200) {
      ms_button =  millis();
      // if (flag == TRUE && ???)
      (*mMenu[curMenu].val1)++;
      // flag = 1
      // blink;
      /////////////////////////
      // (flag == LOW && ???)
      (*nMenu[curMenu].val2)++;
      // flag = 0
      // blink;
     }
    }

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

используйте вел Клапы, что зря чтоли человек трудился. 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

У меня такой вопрос. Допустим имеется две кнопки и число(int, byte или другого целого типа). Нужен простой алгоритм чтобы задействуя эти кнопки можно было увеличивать число на 1,10,100. Думаю одной кнопкой такое не возможно, поэтому предлагая на двух. 

Думаю организовать счетчик нажатий или чтото типа. Чтобы одна увеличивала число, а другая задавала предел(1,10...)

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Лучше всего это.  Да и кнопки должно быть 3 :+ ,-  и next

nik182
Offline
Зарегистрирован: 04.05.2015

Был  у меня такой кусочек. В часах время время и будильник устанавливает. Длинное нажатие на первую кнопку запускает цикл установки времени. Короткие нажатия - изменение цифры. Длинное нажатие - переход к следующей цифре. Последнее длинное нажатие - выход и сохранение результата. Если переменная ed или al отличны от нуля - в основном цикле моргает соответствующая позиция индикатора. Длинное нажатие на вторую кнопку вызывает значение будильника и меняет по тому же алгоритму. Было сделано на тиньке 25. Поэтому ноги используются и для индикации и для ввода - приходится переключать. Подавление дребезга присутствует,  длинное короткое нажатие различаются.

  
......
  tm=millis();
    if(tm-tio > 20)
    {
      tio=tm;
     
      DDRB &= ~(1 << dataPin); // обработка кнопки 1
      delayMicroseconds(100);
      if (k1cnt > 5){
        if ((PINB & dataPin)==0) k1cnt++; else {if (k1cnt > 40)  key=2; else key=1;} k1cnt=0;}
      else if ((PINB & dataPin)==0) k1cnt++;                
      DDRB|= 1 << dataPin ;    
    
      DDRB &= ~(1 << clockPin) ; // обработка кнопки 2
      delayMicroseconds(100);
      if (k2cnt > 5){
        if ((PINB & clockPin)==0) k2cnt++; else {if (k2cnt > 40)  key=4; else key=3;} k2cnt=0;}
      else if ((PINB & clockPin)==0) k2cnt++;                
      DDRB|= 1 << clockPin ;    
    }; 

   switch(key)
   {
    case 0: break;
    case 1: if (ed>0) switch (ed)
                      {
                         case 1: hour+=10;break;
                         case 2: hour++;break;
                         case 3: minute+=10;break;
                         case 4: minute++;break;
                       };
             if (minute>59) minute-=60; 
             if (hour>23) hour-=24; 
             break;
     case 2: ed++; if (ed>3) { ed=0; setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month,year);};break;          
     case 3: if(al>0) switch (al)
                       {
                         case 1: alm++;break;
                         case 2: alm+=10;break;
                         case 3: alh++;break;
                         case 4: alh+=10;break;
                       };
             if (alm>59) alm-=60; 
             if (alh>23) alh-=24; 
             break;
      case 4:  al++; if (al>3) al=0; break;
   };    
   key=0;
 }

 

sadman41
Offline
Зарегистрирован: 19.10.2016

BuonanotteMasha пишет:

У меня такой вопрос. Допустим имеется две кнопки и число(int, byte или другого целого типа). Нужен простой алгоритм чтобы задействуя эти кнопки можно было увеличивать число на 1,10,100. Думаю одной кнопкой такое не возможно, поэтому предлагая на двух.

Возможно, но будет неинтуитивно понятно: проверяете состояние кнопки каждые Xмс, если нажата - увеличиваете на единицу и считаете количество инкрементов. Если это кол-во > N, начинаете инкрементировать на десятку. Если > K - на сотню. Вместо прыжков по разрядам - можете уменьшать X (прибавляться будет быстрее). Как только на очередной итерации кнопка отжата - счетчик инкрементов на 0.

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

sadman41 пишет:

BuonanotteMasha пишет:

У меня такой вопрос. Допустим имеется две кнопки и число(int, byte или другого целого типа). Нужен простой алгоритм чтобы задействуя эти кнопки можно было увеличивать число на 1,10,100. Думаю одной кнопкой такое не возможно, поэтому предлагая на двух.

Возможно, но будет неинтуитивно понятно: проверяете состояние кнопки каждые Xмс, если нажата - увеличиваете на единицу и считаете количество инкрементов. Если это кол-во > N, начинаете инкрементировать на десятку. Если > K - на сотню. Вместо прыжков по разрядам - можете уменьшать X (прибавляться будет быстрее). Как только на очередной итерации кнопка отжата - счетчик инкрементов на 0.

 

Спасибо, буду пробовать. Позже прикреплю код

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Не совсем то но вот

#include <Wire.h> // библиотека для управления устройствами по I2C 
#include <LiquidCrystal_I2C.h> // подключаем библиотеку для LCD 1602

LiquidCrystal_I2C lcd(0x3f,20,4); // присваиваем имя lcd для дисплея 20х4

unsigned long time_Pressed;  // Переменные для работы со временем;
unsigned long timeNow;
byte i = 0; // мое число

enum bottonVariants { // Определения для работы с кнопкой;
        BOTTON_CLICK,							
	BOTTON_SHORT,
	BOTTON_LONG,
	BOTTON_NOT,
};
bottonVariants botton = BOTTON_NOT;

void setup() {
  lcd.begin();
  lcd.backlight(); 

  lcd.setCursor(1, 1);
  lcd.print("PROGRAM V1.0");
  
  delay(1000);
  lcd.clear();
  
  pinMode(9, INPUT);	 //пин для кнопки
  digitalWrite(9, HIGH); //подключаем подтягивающий резистор
  
}

void loop() {
  
    if (digitalRead(9) == 0) {	 // Стандартная проверка с антидребезгом;
	delay(50);		 // Ждем для устранения дребезга;
			
	if (digitalRead(9) == 0) {	// Проверяем еще раз;
	    time_Pressed = millis();	// Запоминаем время нажатия кнопки;
            
	    lcd.clear();
            lcd.print("+1");
            delay(200);		 
            botton = BOTTON_CLICK; // однократное нажатие
            
	    while (digitalRead(9) == 0) {		        // Ждем отпускания кнопки;
	       timeNow = millis();				// И засекаем время;
		    if (timeNow - time_Pressed > 1000 && timeNow - time_Pressed < 2000) {	
                    // Было долгое нажатие кнопки - больше 2 с.
                       lcd.clear();				 
		       lcd.print("+10");
		       delay(200);
                       botton = BOTTON_SHORT;   
	            }
	            else if (timeNow - time_Pressed >2000) {	
                    // Было короткое нажатие - менее 2 с но более 1 с.
                       lcd.clear();				
		       lcd.print("+100");
		       delay(200);
                       botton = BOTTON_LONG;  
		    }
	    }
	 }
     }
     
     switch (botton) {	// Здесь выбираем что менять;
			
        case BOTTON_CLICK:			// При одиночном нажатии,

        i++;
        lcd.setCursor(0, 1); lcd.print(i);
	botton = BOTTON_NOT;
	break;
			
	case BOTTON_SHORT:			// При коротком нажатии;
			
        i+=10;
        lcd.setCursor(0, 1); lcd.print(i);
	botton = BOTTON_NOT;
	break;

        case BOTTON_LONG:			// При долгом нажатии,

        i+=100;
        lcd.setCursor(0, 1); lcd.print(i);
	botton = BOTTON_NOT;
	break;
			
	case BOTTON_NOT:
	break;
    }
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Может кто нибудь сталкивался с подобным. Как сделать чтобы по нажатию кнопки на 8 пине в переменную chislo записывалось первое chislo_One, затем по повторному нажатию второе chislo_Two, затем снова chislo_One ...

byte chislo_One = 0; //  число 0-255
byte chislo_Two = 0; //  число 0-255
byte chislo; // буфер 

enum bottonVariants { // Определения для работы с кнопкой;
        BOTTON_CLICK,							
	BOTTON_NOT,
};
bottonVariants botton = BOTTON_NOT;

void setup() {

  pinMode(8, INPUT);	 //пин для кнопки выбора числа
  digitalWrite(9, HIGH); //подключаем подтягивающий резистор
  pinMode(9, INPUT);	 //пин для кнопки операций над числом
  digitalWrite(9, HIGH); //подключаем подтягивающий резистор
  
}

void loop() {
    
    
    if (digitalRead(8) == 0) {	 // Стандартная проверка с антидребезгом;
	delay(50);		 // Ждем для устранения дребезга;
    if (digitalRead(8) == 0) {	// Проверяем еще раз;
        // этой кнопкой нужно выбрать из chislo_One и chislo_Two
        // chislo = присвоить переменной  выбранное	 
	 }
     }
     
    if (digitalRead(9) == 0) {	 // Стандартная проверка с антидребезгом;
	delay(50);		 // Ждем для устранения дребезга;
			
	  if (digitalRead(9) == 0) {	// Проверяем еще раз
               botton = BOTTON_CLICK; // однократное нажатие
               // редактирование числа
          }

       }


     switch (botton) {  // Здесь выбираем что менять;
             
        case BOTTON_CLICK:          // При одиночном нажатии,
        chislo++;
        botton = BOTTON_NOT;
        break;
             
        case BOTTON_NOT:
        //тут код вывода на ЖК
        break;
     }
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

BuonanotteMasha пишет:

Может кто нибудь сталкивался с подобным. 

Ну сталкивался. Ну делал. Ну ничего особенного. Но при этом вам это банально не подойдет. Потому что мои программы организованы иначе ;)

b707
Offline
Зарегистрирован: 26.05.2017

BuonanotteMasha пишет:

Может кто нибудь сталкивался с подобным. Как сделать чтобы по нажатию кнопки на 8 пине в переменную chislo записывалось первое chislo_One, затем по повторному нажатию второе chislo_Two, затем снова chislo_One ...

А в чем проблема то? Способов это сделать - тыщи. Можно через массивы, можно через указатели, можно по-простому, через пару условий if...

Заводите, к примеру, переменную, со значениями 1 и -1. Если в переменной "1" - значит переменная chislo соответствует chislo_One, если там "-1" - значит chislo_Two. А по нажатию кнопки 8 просто умножаете значение этой переменной на -1.

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Все понятно, спасибо