Длинное и короткое нажатие на Keypad.

Efremoff
Offline
Зарегистрирован: 11.02.2015

Всем привет. Есть програмка, которая в зависимости от того какая клавиша нажата на клавиатуре - запускает разные фунции. что-то вроде такого. Заголовок, сетап-часть и прочее - опускаю за ненадобностью. Импортится Keypad.h модуль. Клава заведена столбцы - на 5-6-7 сам столбец подключен на 8 ногу. Т.е. для выполнения кейса 'a' - нужно чтобы соеденились 5 и 8 ноги, для 'b' - 6 и 8 и тп....

void loop()
{
    char key = kpd.getKey();
  if(key)  // Check for a valid key.
  {
    switch (key)
      {
    case 'a': 
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход
      break;  //прекратить выполнение программы.
      }
      {
    case 'b': 
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход
      break;  //прекратить выполнение программы.
      }
      {
    case 'c': 
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход
      break;  //прекратить выполнение программы.
      }
  }
}

Воспрос - как мне заставить чтобы на длинное нажатие клавиши запускалась другая функция? т.е. чтобы при коротком соединении 5 и 8 ноги (case 'a') - я запускал одну функцию (включение/выключение ноги), а если выводы соеденены долго - то чтобы запускалась другая функция (запись в епром или еще что...)?

UPDATE:

был предложен вариант:

void loop()
{ 
  char key = kpd.getKey();
  static char savekey = 0;
  if(key) 
  {
    static uint32_t startmillis = 0;
    if(!startmillis)
    {
      startmillis = millis();
      savekey = key;
    }
    else if(millis()-startmillis > 500)
    {
      startmillis = 0;
      switch (key)
      {
      case 'a':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'b':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'c':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      }
    }
    key = 0;
  }
  else if(startmillis)
  {
    key = savekey;
    startmillis = 0;
  }


  if(key)  // Check for a valid key.
  {
    switch (key)
    { 
    case 'a':  
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'b':  
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'c':  
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
      break;  //прекратить выполнение программы.
    }
  }
}

но он не заработал. Конкретно в таком виде компилятор ругался на 46 строку, а когда я вынес переменную глобально - смог прошить ардуинку. Но -  на динное нажатие никакой реакции, на короткое - нужно было "попасть в цикл" - т.е. то реагировал на нажатие кнопки соединяющую 5 и 8 ноги - диод на 13 выводе включался-выключался произвольно,  а если  часто и быстро нажимать кнопку  - можно было добится циклического мигания светодиода с равными интервалами...

 

Пока застопорился с дальнейшими идеями. Кто-то может что-то подсказать? 

noxic
Offline
Зарегистрирован: 15.03.2013

если кнопка нажата

{

фиксируем время1;

пока кнопка нажата ждем;

фиксируем время2;

вычисляем время зажатия = верямя2-время1.

}

в зависимости от времени зажатия выполняем команды

Efremoff
Offline
Зарегистрирован: 11.02.2015

ну вот вроде выше так и сделано, но не работает. можно чуть в более ардуино-синтаксическом виде? )

bwn
Offline
Зарегистрирован: 25.08.2014

Примерно так, но для аналоговой клавиатуры, другой нет. Переменные и условия для своих клавиш переделайте. Срабатывает по отпусканию кнопки. Дребезгом не занимался.

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

unsigned long startmillis;
byte KEY;
byte savekey;


void setup()
{
  lcd.begin(16,2);
  lcd.clear();
}


byte key()
{ 
    int val = analogRead(0);
    if (val < 50) return 5;
    else if (val < 150) return 3;
    else if (val < 350) return 4;
    else if (val < 500) return 2;
    else if (val < 800) return 1;
    else return 0;  
}

void klav1()
{
  switch (KEY)
  {
    case 1:
    lcd.setCursor(0,0);
    lcd.print("1 dlin");
    break;
    
    case 2:
    lcd.setCursor(0,0);
    lcd.print("2 dlin");
    break;
    
    case 3:
    lcd.setCursor(0,0);
    lcd.print("3 dlin");
    break;
  
    case 4:
    lcd.setCursor(0,0);
    lcd.print("4 dlin");
    break;
    
    case 5:
    lcd.setCursor(0,0);
    lcd.print("5 dlin");
    break;
  }
}
  
  void klav2()
{
  switch (KEY)
  {
    case 1:
    lcd.setCursor(0,0);
    lcd.print("1 kor");
    break;
    
    case 2:
    lcd.setCursor(0,0);
    lcd.print("2 kor");
    break;
    
    case 3:
    lcd.setCursor(0,0);
    lcd.print("3 kor");
    break;
  
    case 4:
    lcd.setCursor(0,0);
    lcd.print("4 kor");
    break;
    
    case 5:
    lcd.setCursor(0,0);
    lcd.print("5 kor");
    break;
  }
}
    

void loop()
{
  KEY=key();
  if (savekey!=KEY && KEY!=0) {savekey=KEY; startmillis=millis();}
  if (KEY==0 && savekey!=0)
  {
    if (millis()-startmillis>1000) { KEY=savekey; savekey=0; klav1(); }
    else { KEY=savekey; savekey=0; klav2(); }
  }
}
  

 

bwn
Offline
Зарегистрирован: 25.08.2014
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

unsigned long startmillis;
char KEY;
char savekey;


void setup()
{
  lcd.begin(16,2);
  lcd.clear();
}


char key()
{ 
    int val = analogRead(0);
    if (val < 50) return 'e';
    else if (val < 150) return 'c';
    else if (val < 350) return 'd';
    else if (val < 500) return 'b';
    else if (val < 800) return 'a';
    else return ' ';  
}

void klav1()
{
  switch (KEY)
  {
    case 'a':
    lcd.setCursor(0,0);
    lcd.print("1 dlin");
    break;
    
    case 'b':
    lcd.setCursor(0,0);
    lcd.print("2 dlin");
    break;
    
    case 'c':
    lcd.setCursor(0,0);
    lcd.print("3 dlin");
    break;
  
    case 'd':
    lcd.setCursor(0,0);
    lcd.print("4 dlin");
    break;
    
    case 'e':
    lcd.setCursor(0,0);
    lcd.print("5 dlin");
    break;
  }
}
  
  void klav2()
{
  switch (KEY)
  {
    case 'a':
    lcd.setCursor(0,0);
    lcd.print("1 kor");
    break;
    
    case 'b':
    lcd.setCursor(0,0);
    lcd.print("2 kor");
    break;
    
    case 'c':
    lcd.setCursor(0,0);
    lcd.print("3 kor");
    break;
  
    case 'd':
    lcd.setCursor(0,0);
    lcd.print("4 kor");
    break;
    
    case 'e':
    lcd.setCursor(0,0);
    lcd.print("5 kor");
    break;
  }
}
    

void loop()
{
  KEY=key();
  if (savekey!=KEY && KEY!=' ') {savekey=KEY; startmillis=millis();}
  if (KEY==' ' && savekey!=' ')
  {
    if (millis()-startmillis>1000) { KEY=savekey; savekey=' '; klav1(); }
    else { KEY=savekey; savekey=' '; klav2(); }
  }
}
  

Вот так под ваши текстовые, только не знаю, что ваша функция возвращает, если не нажата ни одна клавиша. У себя в условие вбил пробел. У вас надо смотреть.

Efremoff
Offline
Зарегистрирован: 11.02.2015

 Так, если я правильно понимаю - то у вас получается что под разную длинну нажатий выводятся разные "клавиши". 

Keypad работает чуток иначе - каждая клавиша - это соединение двух каких-то ног. например case 'a' - это соединение 5 и 8 ноги, case 'b' - 6 и 8 и тд. 

Давайте для простоты попробуем в шапке все обозначить. 

#include <Keypad.h>
static char savekey = 0;
static uint32_t startmillis = 0;
const byte rows = 1;
const byte cols = 1;
char keys[rows][cols] = {
{'a'}, 
};
byte rowPins[rows] = {5};
byte colPins[cols] = {8};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);



void setup()
{
pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
}

Т.е. сведем все до 1 кнопки/кейса. 

Попробовал написать скетч, пользуясь вашим примером - 

#include <Keypad.h>
unsigned long startmillis;
char KEY;
char savekey;
const byte rows = 1;
const byte cols = 1;
char keys[rows][cols] = {
{'a'}, 
};
byte rowPins[rows] = {5};
byte colPins[cols] = {8};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
}

void klav1()
  {
      switch (KEY)
      {
      case 'a':
        digitalWrite(14, !digitalRead(14));     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      } 
    }

  
  void klav2()
  {
      switch (KEY)
      {
      case 'a':
        digitalWrite(13, !digitalRead(13));     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      } 
    }


    

void loop()
{
  char key = kpd.getKey();
  KEY=key;
      if(KEY){startmillis=millis();}  // Check for a valid key.
        {
          if (millis()-startmillis>1000) { klav1(); }
          else { klav2(); }
        }
}

Работают только короткие нажатия. Длинне никак не реагируют.

 

maksim
Offline
Зарегистрирован: 12.02.2012

Efremoff пишет:

Конкретно в таком виде компилятор ругался на 46 строку

         {
         case 'a':  
           ...     // Делаем, то что надо
           break;  //прекратить выполнение программы.

         case 'b':  
           ...     // Делаем, то что надо

В 46 строке компилятору не на что ругается.

Efremoff пишет:

он не заработал

Словосочитание "не работает" ооочень растяжимое, а в предыдущих постах вы не сооизволили сообщить, что пример просто не компилируется.

void loop()
{ 
  char key = kpd.getKey();
  static char savekey = 0;
  static uint32_t startmillis = 0;
  if(key) 
  {
    if(!startmillis)
    {
      startmillis = millis();
      savekey = key;
    }
    else if(millis()-startmillis > 500)
    {
      startmillis = 0;
      switch (key)
      {
      case 'a':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'b':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'c':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      }
    }
    key = 0;
  }
  else if(startmillis)
  {
    key = savekey;
    startmillis = 0;
  }


  if(key)  // Check for a valid key.
  {
    switch (key)
    { 
    case 'a':  
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'b':  
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'c':  
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
      break;  //прекратить выполнение программы.
    }
  }
}

Ну а если и сейчас не заработает длительное нажатие, то это означает, что с данной библиотекой в текущем варианте это невозможно.

 

maksim
Offline
Зарегистрирован: 12.02.2012

С библиотекой Keypad.h должно работать так:

void loop()
{ 
  char key = kpd.getKey();
  static char savekey = 0;
  static uint32_t startmillis = 0;
  if(kpd.getState() == PRESSED) 
  {
    if(!startmillis)
    {
      startmillis = millis();
      savekey = key;
    }
    else if(millis()-startmillis > 500)
    {
      startmillis = 0;
      switch (savekey)
      {
      case 'a':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'b':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.

      case 'c':  
        ...     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      }
    }
    key = 0;
  }
  else if(kpd.getState() == HOLD && startmillis)
  {
    key = savekey;
    startmillis = 0;
  }


  if(key)  // Check for a valid key.
  {
    switch (key)
    { 
    case 'a':  
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'b':  
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'c':  
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
      break;  //прекратить выполнение программы.
    }
  }
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

И замените в 33 строке RELEASED на HOLD, иначе нажатия будут срабатывать только через полсекунды.

else if(kpd.getState() == HOLD && startmillis)

 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Огромное спасибо!!! да, так работает. Извиняюсь за некоторую путанницу с кодами и прочим - я достаточно начинающий, поэтому бывают явные "глупости". 

bwn
Offline
Зарегистрирован: 25.08.2014

Efremoff пишет:

 Так, если я правильно понимаю - то у вас получается что под разную длинну нажатий выводятся разные "клавиши". 

Keypad работает чуток иначе - каждая клавиша - это соединение двух каких-то ног. например case 'a' - это соединение 5 и 8 ноги, case 'b' - 6 и 8 и тд. 

Давайте для простоты попробуем в шапке все обозначить. 

#include <Keypad.h>
static char savekey = 0;
static uint32_t startmillis = 0;
const byte rows = 1;
const byte cols = 1;
char keys[rows][cols] = {
{'a'}, 
};
byte rowPins[rows] = {5};
byte colPins[cols] = {8};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);



void setup()
{
pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
}

Т.е. сведем все до 1 кнопки/кейса. 

Попробовал написать скетч, пользуясь вашим примером - 

#include <Keypad.h>
unsigned long startmillis;
char KEY;
char savekey;
const byte rows = 1;
const byte cols = 1;
char keys[rows][cols] = {
{'a'}, 
};
byte rowPins[rows] = {5};
byte colPins[cols] = {8};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
}

void klav1()
  {
      switch (KEY)
      {
      case 'a':
        digitalWrite(14, !digitalRead(14));     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      } 
    }

  
  void klav2()
  {
      switch (KEY)
      {
      case 'a':
        digitalWrite(13, !digitalRead(13));     // Делаем, то что надо
        break;  //прекратить выполнение программы.
      } 
    }


    

void loop()
{
  char key = kpd.getKey();
  KEY=key;
      if(KEY){startmillis=millis();}  // Check for a valid key.
        {
          if (millis()-startmillis>1000) { klav1(); }
          else { klav2(); }
        }
}

Работают только короткие нажатия. Длинне никак не реагируют.

 

Они у вас и не будут реагировать. Вы убрали защелку нажата-ненажата. Дальше логика уже не работает. Для корректной работы вам надо определить, что возвращает функция при ненажатой клавише. Пришел код - нет нажатых клавиш, начали отрабатывать условия. Возможно с этой библиотекой так и не выйдет.

Efremoff
Offline
Зарегистрирован: 11.02.2015

Не, работает с Released. C HOLD - работает не корректно - длинные нажатия не отрабатывают, короткие отрабатывают через раз. 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Всем спасибо. Финальный код выглядит так: 

#include <Keypad.h>

const byte rows = 1;
const byte cols = 1;
char keys[rows][cols] = {
{'a'}, 
};
byte rowPins[rows] = {5};
byte colPins[cols] = {8};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
}

void loop()
{ 
  char key = kpd.getKey();
  static char savekey = 0;
  static uint32_t startmillis = 0;
  if(kpd.getState() == PRESSED) 
  {
    if(!startmillis)
    {
      startmillis = millis();
      savekey = key;
    }
    else if(millis()-startmillis > 500)
    {
      startmillis = 0;
      switch (savekey)
      {
      case 'a':  
        digitalWrite(14, !digitalRead(14));
        break;  //прекратить выполнение программы.
      }
    }
    key = 0;
  }
  else if(kpd.getState() == RELEASED && startmillis)
    {
    key = savekey;
    startmillis = 0;
  }


  if(key)  // Check for a valid key.
  {
    switch (key)
    { 
    case 'a':  
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
      break;  //прекратить выполнение программы.
    }
  }
}

Все работает как надо. 

maksim
Offline
Зарегистрирован: 12.02.2012

Так работает или не работает с RELEASED ?

Efremoff
Offline
Зарегистрирован: 11.02.2015

Работает имено с RELEASED. C HOLD - не работает. 

maksim
Offline
Зарегистрирован: 12.02.2012

Ну и более оптимально будет так

void loop()
{  
  char key = kpd.getKey();
  static char savekey = 0;
  static uint32_t startmillis = 0;
  if(kpd.getState() == PRESSED) 
  {
    if(!startmillis)
    {
      startmillis = millis();
      savekey = key;
    }
    else if(millis()-startmillis > 500)
    {
      startmillis = 0;
      Long(savekey);
    }
  }
  //else if(kpd.getState() == HOLD && startmillis)        // или с HOLD
  else if(kpd.getState() == RELEASED && startmillis)    // или с RELEASED
  {
    startmillis = 0;
    Shot(savekey);
  }
}



void Shot(char _key)
{
  switch (_key)
  { 
  case 'a':  
    digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
    break;  //прекратить выполнение программы.

  case 'b':  
    digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
    break;  //прекратить выполнение программы.

  case 'c':  
    digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
    break;  //прекратить выполнение программы.
  }
}


void Long(char _key)
{
  switch (_key)
  {
  case 'a':  
    ...     // Делаем, то что надо
    break;  //прекратить выполнение программы.

  case 'b':  
    ...     // Делаем, то что надо
    break;  //прекратить выполнение программы.

  case 'c':  
    ...     // Делаем, то что надо
    break;  //прекратить выполнение программы.
  }
}

 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Кстати, а почему я не могу тут 

else if(millis()-startmillis > 500) 

указать больше чем 500? например если мне задержка нужна в 2500-3000? Если пробую указать даже 600 - то ничего не происходит по долгому нажатию. 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Причем меньше чем 500 - установить могу, отрабатывает корректно. больше - просто перестают работать "долгие" нажатия. 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Потому что setHoldTime по-умолчанию 500 после этого state меняется на hold а Вы ловите Pressed и Released.

В чём вообще прикол, используя библиотеку самому считать время, когда это уже реализовано в библиотеке?

bwn
Offline
Зарегистрирован: 25.08.2014

Слушайте, а в чем фишка этой библиотеки? Может она и мне нужна?

Efremoff
Offline
Зарегистрирован: 11.02.2015

Точно. Прописал в setup части - kbp.setHoldTime = 2500; и стало считать большие значения. 

Я бы с радостью использовал все, что есть в библиотеке - но знаний пока не хватает. 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Она по сути кейпад делает. 

bwn
Offline
Зарегистрирован: 25.08.2014

А 8 пинов на 16 клавиш, это не слишком щедро? Есть ведь варианты на 1 или 2

http://arduino.ru/forum/programmirovanie/rabota-s-knopkami-v-pomoshch-novichku?page=4#comment-86325

http://arduino.ru/forum/programmirovanie/podklyuchenie-matrichnoi-klaviatury-po-i2c

Сам правда не проверял, мне больше трех пока было не надо.

maksim
Offline
Зарегистрирован: 12.02.2012

Penni пишет:

Потому что setHoldTime по-умолчанию 500 после этого state меняется на hold а Вы ловите Pressed и Released.

В чём вообще прикол, используя библиотеку самому считать время, когда это уже реализовано в библиотеке?

Все верно, просто нужно внимательнее читать описание библиотек.

void loop()
{  
  char key = kpd.getKey();
  if(key && kpd.getState() == PRESSED) 
  {
    switch (key)
    { 
    case 'a':  
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'b':  
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
      break;  //прекратить выполнение программы.

    case 'c':  
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
      break;  //прекратить выполнение программы.
    }
  }
  
  if(key && kpd.getState() == HOLD) 
  {
    switch (key)
    {
    case 'a':  
      ...     // Делаем, то что надо
      break;  //прекратить выполнение программы.

    case 'b':  
      ...     // Делаем, то что надо
      break;  //прекратить выполнение программы.

    case 'c':  
      ...     // Делаем, то что надо
      break;  //прекратить выполнение программы.
    }
  }
}

 

Efremoff
Offline
Зарегистрирован: 11.02.2015

Как-то у меня последний пример не заработал. короткие нажатия работают нормально, длинные нет. Может я что-то в сетап-части упустил?

Efremoff
Offline
Зарегистрирован: 11.02.2015

Так. поскольку чуток в голове уже инфы появилось, перечитал еще раз документацию к библиотеке, и посмотрел в очередной раз примеры. И оказалось, что пример "EventKeypad" очень похож на то, что мне нужно (наконец-то я это смог увидеть). 


void setup()
{

pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
pinMode(15, OUTPUT); 

kpd.setHoldTime(1000);
kpd.addEventListener(keypadEvent);
}

void loop()
{  
  char key = kpd.getKey();
}
void keypadEvent(KeypadEvent key){
    switch (kpd.getState()){
    case PRESSED:    // a to e = select wich relay is HIGH/LOW
    if (key == 'a') {
      digitalWrite(13, !digitalRead(13)); //Инвертировать выход 
       //прекратить выполнение программы.
      }
    if (key == 'b') { 
      digitalWrite(14, !digitalRead(14)); //Инвертировать выход 
       //прекратить выполнение программы.
     }
    if (key == 'c) {
      digitalWrite(15, !digitalRead(15)); //Инвертировать выход 
      //прекратить выполнение программы.
    }
    break; 

    case HOLD:
    if (key == 'a'){
      ...     // Делаем, то что надо
      }

     if (key == 'b'){ 
      ...     // Делаем, то что надо
      }

     if (key == 'c'){  
      ...     // Делаем, то что надо
      }
    break;
  }
}

Работает.