Очередное подавление дребезга на ESP32

Попались мне крайне дешевые китайские кнопки. Взял на замену емкостным, т.к. у последних стали появляться ложные срабатывания.

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

Я не претендую на оригинальность, скорее всего подобный код есть и много лучше, однако поделюсь. Не судите строго.

Смысл в том, что мы не фиксируем нажатие и не считаем его длительность. Мы стробируем время и в заданные промежутки проверяем состояние линии за прошедший период.

//*********************************************
void IRAM_ATTR button_interr_i(){ 
  if( !ft_i ){
    ft_i = 1;
    buttonTime_i = millis();
  } 
  millis_prev_i = millis();
}

Выше код обработчика нажатия на кнопку. Мы его подключаем в setup().

  attachInterrupt(digitalPinToInterrupt(PIN_BUTTON_I), button_interr_i, CHANGE);

Также в setup() мы подключаем обработчик прерывания таймера:

hw_timer_t *My_timer = NULL;  //указатель на таймер
 . . .
void setup() {
. . .
  My_timer = timerBegin(0, 80, true);
  timerAttachInterrupt(My_timer, &ft_interr, true);
  timerAlarmWrite(My_timer, ALATM_DT * 1000, true);
  timerAlarmEnable(My_timer);
. . .
}

Ниже код обработчика прерываний таймера:

//*********************************************
void IRAM_ATTR ft_interr(){ //обработка прерывания по таймеру
  if( ft_i && ( millis_prev_i == 0 ) ) {
    if( ft_i < EMPTY_LOOP ){
      ft_i += !digitalRead(PIN_BUTTON_I);
    } else {
      buttonStatus_i = 1;
    }
  } else {
    millis_prev_i = 0;
    if(ft_i > 1){
      ft_i = 1;
    }
  }
}

Немного подробнее: при первом нажатии на кнопку мы устанавливаем значение переменной ft_i в 1 и фиксируем время события в переменной buttonTime_i. При последующих прерываниях от кнопки (дребезга) мы фиксируем время данного события в переменной millis_prev_i.

Обработчик таймера проверяет состояние millis_prev_i и ft_i. Если нажатие состоялось, то при каждом вызове таймера мы проверяем, а не закончилось ли оно сбрасывая переменную millis_prev_i в 0 при каждом вызове. Т.е. если кнопу отпустили, то прерываний от таймера не будет и переменная millis_prev_i останется равной 0. Дешевым кнопкам доверять нельзя. Мы несколько раз (EMPTY_LOOP) убеждаемся в том, что импульсов от кнопки больше нет и устанавливаем признак buttonStatus_i в 1, который обрабатывается уже в loop().

void loop() {
  //.................................
  if(buttonStatus_i) {
    unsigned long bT_i = millis() - buttonTime_i - EMPTY_LOOP * ALATM_DT;
      if(bT_i > LONG_WAIT) //какие-то действия
        light_2.toggleMax();
      else
        light_2.trigger();
      ft_i = 0;
      buttonStatus_i = 0;
      buttonTime_i = 0;
  }
. . . 
}