Обработка последовательности нажатия кнопок.

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
На форуме возник такой вопрос "Как поставить условие исходя из порядка нажатий кнопок и временного интервала между нажатиями"
Я предложил использовать некий буфер, куда закидывать последовательность нажатий кнопок, но задача решилась проще, что хорошо для автора вопроса, но не всегда подходит для других задач. Сама по себе задача мне показалось полезной и я решил воплотить ее в небольшой демонстрационной программке, что и выкладываю на всеобщее обозрение. Это всего лишь мое видение решения данного вопроса не претендующее на оптимально, но довольно универсальное. Ну а решений может быть куда больше. Алгоритм реализован линейное, без объектов, максимально просто для понятия.

Итак , имеем ардуино, к которой , в моем случае, подключено 4 кнопки. Кнопок может быть любое нужное количество.
Размер буфера я ограничил 4-мя нажатиями, строка 1

Немного пояснений по программе:
TIME_DELAY время тайм-аута, после которого происходит обработка буфера. В данном случае он равен 2000 мс, или 2 сек, как у автора вопроса. 2-ая строка программы.

Имеется глобальная переменная Buffer (далее буфер), которая представляет собой массив данных типа uint8_t. Для удобства работы с буфером были написаны несколько функций:
ClearBuffer() - очистка содержимого буфера, т.е. заполнение его нулями

bufSize() - функция возвращающая количество введенных нажатий, от 0 ( буфер пуст) до BUFER_SIZE (полностью заполнен)

addButton() - функция добавления номера нажатой кнопки в буфер. Если добавление успешно, то возвращается TRUE. Так же функция запоминает время добавления нажатия, что бы отработать последующий анализ последовательности. Еще одна особенность: if ((bs + 1) == BUFER_SIZE)buffer_Time = buffer_Time - TIME_DELAY; . В этой строке проверяется заполняет ли текущее нажатие буфер полностью, и , если заполняет, уменьшает время нажатия на время TIME_DELAY- время простоя, после которого в любом случае обрабатывается последовательность введенная в буфер. Сделано это для того, что бы если буфер полностью заполнен можно было бы с ним работать без задержки.

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

bufferTick() - функция встраиваемая в lopp. Если прошло определенное время после последнего нажатия и буфер не пустой, то она вызывает readCommandBuf и затем очищает содержимое буфера.

Почему я сделал размер буфера 4. Для простоты анализа последовательности. Имея переменную типа uint32_t можно легко преобразовать все 4 введенных нажатия в число и записать в эту переменную. Что то типа uint32_t perem=Buffer[0]+Buffer[1]<<8+Buffer[2]<<16+Buffer[3]<<24; , и уже сравнивать 2 переменные. Данный пример я не тестировал, возможно придется сделать явное приведение типов к uint32_t. Все это можно оформить в виде функции, и сравнение будет достаточно простое.

C++:
#define BUFER_SIZE 4
#define TIME_DELAY 2000
#define BTN_1_PIN 4
#define BTN_2_PIN 5
#define BTN_3_PIN 6
#define BTN_4_PIN 7


uint8_t Buffer[BUFER_SIZE];
uint32_t buffer_Time = 0;

#include "GyverButton.h"
GButton Btn_1(BTN_1_PIN , HIGH_PULL);
GButton Btn_2(BTN_2_PIN , HIGH_PULL);
GButton Btn_3(BTN_3_PIN , HIGH_PULL);
GButton Btn_4(BTN_4_PIN , HIGH_PULL);


void setup() {
  Serial.begin(115200);
  ClearBuffer();
}

void loop() {
  Btn_1.tick();
  Btn_2.tick();
  Btn_3.tick();
  Btn_4.tick();

  if (Btn_1.isClick()) {
    addButton(1);
  };
  if (Btn_2.isClick()) {
    addButton(2);
  };
  if (Btn_3.isClick()) {
    addButton(3);
  };
  if (Btn_4.isClick()) {
    addButton(4);
  };

  bufferTick();
}

void ClearBuffer()
{
  for (uint8_t i = 0; i < BUFER_SIZE; i++)
    Buffer[i] = 0;
}

uint8_t bufSize()
{
  for (uint8_t i = 0; i < BUFER_SIZE; i++)
    if (Buffer[i] == 0) return i;
  return BUFER_SIZE;
}

bool addButton(uint8_t n)
{
  uint8_t bs = bufSize();
  if (bs == BUFER_SIZE) return false;
  buffer_Time = millis();
  Buffer[bs] = n;
  if ((bs + 1) == BUFER_SIZE)buffer_Time = buffer_Time - TIME_DELAY;
return true;
}


void readCommandBuf()
{
  // в данном случае функция чтения команды просто выводит
  uint8_t bs = bufSize();
  for (uint8_t i = 0; i < bs; i++)
  {
    Serial.print(Buffer[i]);
  }
  Serial.println();
}

bool bufferTick()
{
  if (millis() - buffer_Time > TIME_DELAY)
  {
    uint8_t bs = bufSize();
    if (bs > 0)
    {
      readCommandBuf();
      ClearBuffer();
    }
  }
}
Ну и результаты работы:
14:57:59.784 -> 122
14:58:03.157 -> 1122
15:32:44.490 -> 2
15:32:48.914 -> 1234
15:32:54.542 -> 132
15:32:57.442 -> 4444
15:33:01.335 -> 222
 
Изменено: