Нажатие двух кнопок одновременно. Как?

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

Всем привет! В реализации своего первого проекта мне понадобилась функциональная особенность: хочу делать перезагрузку Ардуинки по удерживанию двух кнопок (зажатых одновременно). Есть ли у кого-то идеи, как это реализовать?

В данный момент использую обычную обработку кнопок с помощью флажков и millis() для устранения дребезга контактов.

Вот часть кода, отвечающего за это:

boolean flag1 = 0;
boolean flag2 = 0;
boolean butt1;
boolean butt2;
unsigned long last_press;

void setup() {
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  butt1 = !digitalRead(A1);
  butt2 = !digitalRead(A2);

if (butt1 == 1 && flag1 == 0 && millis() - last_press > 150) {
  // первое действие
}

if (butt2 == 1 && flag2 == 0 && millis() - last_press > 150) {
  // второе действие
}

if (butt1 == 0 && flag1 == 1) { flag1 = 0; }
if (butt2 == 0 && flag2 == 1) { flag2 = 0; }

 

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

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

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

Я дичайше извиняюсь, что ввёл вас в заблуждение. Хотел как лучше — очистить код от ненужностей, и оставить только основу. И сам того не желая, удалил из циклов важные строки кода, которые лишили код понимания.

boolean flag1 = 0;
boolean flag2 = 0;
boolean butt1;
boolean butt2;
unsigned long last_press;

void setup() {
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  butt1 = !digitalRead(A1);
  butt2 = !digitalRead(A2);

if (butt1 == 1 && flag1 == 0 && millis() - last_press > 150) {
  flag1 = 1;
  last_press = millis();
  // первое действие
}

if (butt2 == 1 && flag2 == 0 && millis() - last_press > 150) {
  flag2 = 1;
  last_press = millis();
  // второе действие
}

if (butt1 == 0 && flag1 == 1) { flag1 = 0; }
if (butt2 == 0 && flag2 == 1) { flag2 = 0; }

Вот как-то так получается. Единственное, я полагаю, что и переменных last_press две должно быть (last_press1 и last_press2). Но во время тестирования никакого дребезжания не было замечено вроде как.

Возвращаясь к теме, хочу спросить вас b707, есть ли у вас мысли, как обработку одновременного нажатия с удержанием сделать? Заранее благодарю.

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

puhloschiok пишет:

Я дичайше извиняюсь, что ввёл вас в заблуждение. Хотел как лучше — очистить код от ненужностей, и оставить только основу.

ситуация не изменилась - все равно код неверный. Например, в условии строки 17 кнопка 1 сработает сразу, а не после антидребезга в 150мс. Да и вообще, код обрывается на середине и компилироваться не будет.

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

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

puhloschiok пишет:
хочу делать перезагрузку Ардуинки по удерживанию двух кнопок (зажатых одновременно). Есть ли у кого-то идеи, как это реализовать?
Идея есть. Но шансов вам понять эту идею нет.

1- Если программа будет работать так как вы хотите, то нажатие кнопок будет вызывать вечный перегруз.

2- А вот если кнопка  1 только что нажата, а кнопка 2 уже нажата то да можно уйти в перегруз, как и ситуация как кнопка 2 только что нажата.....

ВН
Offline
Зарегистрирован: 25.02.2016

qwone пишет:

1- Если программа будет работать так как вы хотите, то нажатие кнопок будет вызывать вечный перегруз.

скорее кто-то кого-то не понял

 

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

b707 совершенно не думаю, что вы придираетесь. Я всегда прислушаюсь к советам людей, которые разбираются в той или иной области лучше меня. Так хотя бы чему-то учишься, а не повторяешь вслепую непонятные схемы. А в том, что нет смысла модифицировать корявый код, я с вами полностью согласен. К тому же, заметил, что в ходе тестирования, одна из кнопок всё таки "залипла" пару раз. Т.е. действительно дребезг в моём коде не устраняется. К сожалению было слишком много работы в последнюю неделю и не было времени заниматься по вечерам этим мини-проектом. На днях пересмотрю код и урок, на основании которого я писал этот код, и попробую найти причину.

qwone возможно мы друг друга не поняли, но в пункте 2 своего сообщения вы натолкнули меня на верную мысль. Действительно, нужен алгоритм, который будет проверять состояние другой кнопки, при текущем нажатии первой. И в случае соответствия условию будет выполнятся то, что необходимо. Я даже примерно понимаю, как это должно выглядеть в коде. Спасибо.

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

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

Разобравшись, что там к чему, и немного модифицировав его скетч, получил вот это:

const char btn_pin1 = 2;
const char btn_pin2 = 4;
bool btn1; // значение на кнопке без дребезга
bool btn2;
bool btn1_old; // старое значение на кнопке без дребезга
bool btn2_old;
bool flag1 = 0; // антидребезговый флаг
bool flag2 = 0;
bool press1 = 0; // флаг «кнопка нажата и удерживается»
bool press2 = 0;
bool dbl = 0; // флаг «другая кнопка не была нажата»

void setup() {
  Serial.begin(9600);
  pinMode(btn_pin1, INPUT_PULLUP);
  pinMode(btn_pin2, INPUT_PULLUP);
  btn1 = digitalRead(btn_pin1);
  btn2 = digitalRead(btn_pin2);
}

void loop() {
  static uint32_t last_press1 = 0 ;
  static uint32_t last_press2 = 0 ;
  
  if (!flag1 && btn1 != digitalRead(btn_pin1)) {
    flag1 = 1;                                  
    last_press1 = millis();                     
  }
  else if ( flag1 && millis() - last_press1 >= 40 ) {
    flag1 = 0;
    btn1_old = btn1 ;
    btn1 = digitalRead(btn_pin1);
    if (btn1_old && !btn1) {
      Serial.print("Нажато: ");Serial.println(last_press1);
      press1 = 1;
      if (press1 > 0 && press1 == press2 && dbl == 0) {
        dbl = 1;
        delay(3000);
        Serial.println("Нажаты обе");
      }
    }
    if (!btn1_old && btn1) {
      Serial.print("Отпущено: ");Serial.println(last_press1);
      press1 = 0;
      dbl = 0;
    }
  }

  if (!flag2 && btn2 != digitalRead(btn_pin2)) {
    flag2 = 1;
    last_press2 = millis();
  }
  else if ( flag2 && millis() - last_press2 >= 40 ) {
    flag2 = 0;    // то снять флаг
    btn2_old = btn2 ;
    btn2 = digitalRead(btn_pin2);
    if (btn2_old && !btn2) {
      Serial.print("Нажато: ");Serial.println(last_press1);
      press2 = 1;
      if (press2 > 0 && press1 == press2 && dbl == 0) {
        dbl = 1;
        delay(3000);
        Serial.println("Нажаты обе");
      }
    }
    if (!btn2_old && btn2) {
      Serial.print("Отпущено: ");Serial.println(last_press2);
      press2 = 0;
      dbl = 0;
    }
  }
}

Добавил три флажка: два из них фиксируют нажатие и удержание одной из кнопок, а третий отвечает за проверку состояния второй кнопки в момент нажатия текущей (чтобы одно и то же действие не выполнилось дважды).

Вобщем-то всё работает идеально. Спасибо всем, кто помогал и отдельное спасибо qwone за его скетч.

P.S.: Для выполнением функции после трёхсекундного удерджания я использовал delay() который, как я понимаю, лучше не использовать в серьёзных проектах, т.к. он полностью останавливает выполнение loop(). По задумке, это действие должно приводить к перезагрузке Ардуины, так что полная приостановка цикла — вполне нормальное явление в данном случае. Но если у кого-то есть мысли, как сделать это с помощью millis(), буду признателен. Я, к сожалению, ещё не настолько продвинулся в изучении программирования.

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

Много воды утекло с последнего сообщения в этой теме. Тем не менее, код, описанный в нём, верой и правдой служил всё это время. И вот понадобилось мне расширить немного функционал. А именно: добавить функционал «удержания» (или длительного нажатия, если позволите) к одной из кнопок. Программирование не является моей основной деятельностью, и, изрядно поломав голову, я так и не смог придумать, как это сделать.

Прошу помощи в данном вопросе. Возможно qwone увидит это сообщение и подтолкнёт к нужному решению. Всё-таки мой код большей часть основан на его скетче.

Всем заранее спасибо!

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

puhloschiok пишет:

Много воды утекло с последнего сообщения в этой теме. Тем не менее, код, описанный в нём, верой и правдой служил всё это время. И вот понадобилось мне расширить немного функционал. А именно: добавить функционал «удержания» (или длительного нажатия, если позволите) к одной из кнопок. Программирование не является моей основной деятельностью, и, изрядно поломав голову, я так и не смог придумать, как это сделать.

Прошу помощи в данном вопросе. Возможно qwone увидит это сообщение и подтолкнёт к нужному решению. Всё-таки мой код большей часть основан на его скетче.

Всем заранее спасибо!

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

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

vvadim пишет:
скачайте библиотеку OneButton.
кнопки с внутренней подтяжкой.
там и короткий клик, и двойной, и длинный, и удержание кнопки.
проще с ней разобраться и пользоваться на здоровье...

За совет спасибо. Библиотеку эту я знаю.
Тем не менее, хочется самому разобраться. Я готовый код не прошу. Прошу лишь подсказать.

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

хотите сам - посмотрите как в библиотеке работа с кнопкой реализована и повторяйте ...

а я пользуюсь уже давно, мне нравится и удобно.

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

Хочу ещё добавить, что фиксировать долгое нажатие ПОСЛЕ отпускания кнопки, у меня получилось сразу.
Вот код:

const char btn_pin1 = 2;
bool btn1; 
bool btn1_old;
bool flag1 = 0;
bool press1 = 0;

void setup() {
  Serial.begin(9600);
  pinMode(btn_pin1, INPUT_PULLUP);
  btn1 = digitalRead(btn_pin1);
}

void loop() {
  static uint32_t last_press1 = 0 ;
  static uint32_t press_time = 0 ;
  
  if (!flag1 && btn1 != digitalRead(btn_pin1)) {
    flag1 = 1;                                  
    last_press1 = millis();            
  }
  else if (flag1 && millis() - last_press1 >= 40) {
    flag1 = 0;
    btn1_old = btn1 ;
    btn1 = digitalRead(btn_pin1);
    if (btn1_old && !btn1) {
      press1 = 1;
      press_time = last_press1;
    } if (!btn1_old && btn1) {
        if (last_press1 - press_time >= 600) {
          Serial.print("Долгое нажатие! ");Serial.println(last_press1 - press_time);
        } else {
          Serial.print("Простой клик: ");Serial.println(last_press1);
        }
      press1 = 0;
    }
  }
}

Но вот с отстчётом времени в момент удержания (даже без отпускания кнопки) ничего придумать не получается :(

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

puhloschiok, прошу выложить мой код, на который вы опирались. Так как я уже много кода на форум писал.

/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//------Cl_BtnLong----------------------
// класс кнопка
class Cl_BtnLong {
  protected:
    const byte pin;
    pDo Do1, Do2; //обработчик короткий,длиный
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
    const uint32_t time = 500 ;
    bool flag = 0;
    uint32_t past_flag = 0 ;
  public:
    /*конструктор*/
    Cl_BtnLong(byte pin_, pDo Do1_, pDo Do2_)
      : pin(pin_), Do1(Do1_), Do2(Do2_)  {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        if (!btn && oldBtn) {
          flag = 1;
          past_flag = mill;
        }
        if (!oldBtn && btn && flag && mill - past_flag < time ) {
          flag = 0;
          Do1();// короткое нажатие
        }
      }
      if (flag && mill - past_flag >= time ) {
        flag = 0;
        Do2();//длиное нажатие
      }
    }
};
//-----Компоновка----------------------
void DoBtn1() {
  Serial.println("Do_Btn1");
}
void DoBtn2() {
  Serial.println("DoLong_Btn1");
}
Cl_BtnLong Btn1(/*пин*/2,/*обработчик короткого*/DoBtn1,/*обработчик длиного*/DoBtn2);
//-----main----------------------
void setup() {
  Serial.begin(9600);
  Btn1.init();
}
void loop() {
  mill = millis();
  Btn1.run();
}

 

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

qwone, большое спасибо, что отозвались!
Я попробовал найти тот ваш старый код, но, скорее всего, сообщение, в котором он был, в последствии было отредактирвоано вами в связи с оптимизацией вами кода. http://arduino.ru/forum/programmirovanie/podavlenie-drebezga-bez-delai-i... — вот тут вы описываете свой код антидребезга. Думаю раньше на этом месте и был ныне устаревший вариант... По крайней мере Гугл указывает именно на это сообщение.

То, что вы привели мне в качестве примера в последнем сообщении — это уже довольно прогрессивный и сложный для меня код. Тем не менее, благодарю! Буду разбираться, как он работает.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/**/
const byte pin=/*пин*/2;
bool flag1 = 0;
bool btn = 1, oldBtn;
unsigned long past;
const uint32_t time = 500 ;
bool flag = 0;
uint32_t past_flag = 0 ;
//
void setup() {
  Serial.begin(9600);
  pinMode(pin, INPUT_PULLUP);
}

void loop() {
  bool newBtn = digitalRead(pin);
  if (!flag1 && newBtn != btn) {
    flag1 = 1;
    past = millis();
  }
  if (flag1 && millis() - past >= 10) {
    flag1 = 0 ;
    oldBtn = btn;
    btn = newBtn;
    if (!btn && oldBtn) {
      flag = 1;
      past_flag = millis();
    }
    if (!oldBtn && btn && flag && millis() - past_flag < time ) {
      flag = 0;
      Serial.println("short! ");
    }
  }
  if (flag && millis() - past_flag >= time ) {
    flag = 0;
    Serial.println("long! ");
  }
}

 

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

qwone, невероятно просто! Словами не выразить мою благодарность. Построчно буду сравнивать со старым кодом и разбираться, как же вам удалось это провернуть! =)

puhloschiok
puhloschiok аватар
Offline
Зарегистрирован: 02.11.2017

qwone, разобрался в вашем коде.
Оказывается, одна из моих неудачных конструкций, была очень похожей. Я не додумался ввести ещё один флаг. Из-за этого программа работала не корректно, и каждый раз я получал предыдущее значение нажатия. Ну и время удержания я записывал просто числом, не константой. Хотя разницы особой нет, вопрос удобства и оптимизации.

По ходу изучения вашего кода у меня возник вопрос по объявлению переменных.
Вот в четвёртой строке вы сделали запись:

bool btn = 1, oldBtn;

Т.е. это означает, что обе переменные типа bool. И такая запись является более оптимальной, чем объявлять один и тот же тип для каждой переменной с новой строки. Даже с точки зрения восприятия скетча, не так много строк (особенно, когда переменных много).

Выходит и другие переменные типа bool этого скетча можно было объявить в этой же строке?

bool flag1 = 0, btn = 1, oldBtn, flag = 0;

Или я не прав?

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

Видишь тут такое дело. Обычно считается что программисты пишут программы, но это в 100% не верно. Они пишут исходник, а дальше компиляторы линкеры и прочие механизмы собирают как кто хочет.  Так что мой ответ будет такой. https://www.youtube.com/watch?v=7TsQLGimB2I

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Нет, программы все же пишут программисты. А все эти компиляторы, линкеры и прочие механизмы - лишь инструменты, которыми программист пользуется.

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

puhloschiok пишет:

Выходит и другие переменные типа bool этого скетча можно было объявить в этой же строке?

bool flag1 = 0, btn = 1, oldBtn, flag = 0;

Или я не прав?

Раз уж взялись за булеаны, то и значения надо использовать - true/false, мне так кажеся.

ua6em
ua6em аватар
Онлайн
Зарегистрирован: 17.08.2016

qwone пишет:

Видишь тут такое дело. Обычно считается что программисты пишут программы, но это в 100% не верно.

вот вот, согласен )))