Устранение дребезга контактов в счетчике воды

В предыдущей статье я рассмотрел пример схемы для снятия данных со счетчика воды. В ней для избавления от дребезга контактов использовался программный способ с использованием библиотеки bouncer2.

Программное устранение дребезга контактов

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

Код в статье плох использованием delay. Об использовании этой функции в программах для ESP8266/ESP32/Arduino лучше вообще забыть, поскольку она блокирует обработку событий другим блоками программы, не относящимся к коду debouncer. Нужно использовать millis() или библиотеку вроде bouncer2.

Вот простой пример использования millis(). В нем я не стал подключать к выходу микроконтроллера светодиод для индикации состояния геркона. Лишь сделал вывод информации о состоянии LED в Serial.

int pinButton = 14; //D5 - Пин кнопки
bool flag       = HIGH; //Индикатор текщего состояния, например, светодиода
bool lastButton = LOW;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
int debounceInterval = 20; //20 ms

void setup() 
{
  pinMode(pinButton, INPUT_PULLUP);
  Serial.begin(9600, SERIAL_8N1);
}

void loop() {
  bool currentButton = digitalRead(pinButton);
  currentMillis = millis();
  
  if (currentButton != lastButton)
  {
    previousMillis = currentMillis; //Сохраняем время получения первого импульса при срабатывании геркона
    lastButton = currentButton;
  }
  else
  { 
    if (currentMillis - previousMillis > debounceInterval)  //Если состояние кнопки не изменилось за debounceInterval
    { 
      if (flag != currentButton) //Сравниваем состояние и если оно отличается от состояния кнопки, меняем состояние LED
      {
        String state = currentButton ? "OFF" : "ON"; 
        Serial.println("Button is " + state); 
        flag = currentButton;
        lastButton = currentButton;  
      }
    }
  }
}

Отмечу, что в данном случае код для устранения дребезга контактов столь прост из-за того, что процессы очень медленные. Геркон находится в замкнутом состоянии много больше переходных процессов. Если бы время было сопоставимо, пришлось бы использовать более сложные алгоритмы детектирования дребезга контактов, чтобы не спутать с реальным изменением состояния геркона.

Аппаратное устранение дребезга

Для уппаратного устранения дребезга контактов используются различные подходы. Самый простой вариант — использовать RC цепочку, для сглаживания высокочастотный коротких всплесков во время переходных процессов. Рассмотрим схемы из статьи.

Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)
Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)
Имитация подавления дребезга контактов

Поиграться с работой схемы можно здесь. При разомкнутой кнопке емкость C1 заряжается через сопротивление R1. На входе инвертирущего триггера Шмитта +VCC. На выходе, соответственно 0.

Здесь RC цепочка работает главным образом при размыкании кнопки. При замыкании емкость фактически закорачивается, мгновенно разряжаясь до нуля. При этом через контакты кнопки/геркона по закону Ома I=U/R, при сопротивлении равном 0, будет протекать беконечный ток. Неправильно таким образом разряжать емкость. Правильнее все-же разряжать её через сопротивление, чтобы уменьшить ток разряда. Поэтому правильнее схема из статьи.

Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)

Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)
Имитация подавления дребезга контактов

Схема почти такая-же, но при замыкании кнопки емкость С2 разряжается на землю через сопротивление R4, поэтому ток протекающий через контакты уже безопасно небольшой для контактов: I = U/R = 5/1000 = 5 мА.

t — время разряда с уровня Vdc до Vc. Vc — напряжение при котором контроллер идентифицирует уровень напряжения как логический ноль. Я взял Vc = 0,5 V. В статье подробно описаны уровни для ESP8266. Я взял несколько более низкое напряжение, вместо 0,8 V для напряжения, ниже которого ESP8266 считает уровень логическим нулем.

t=-R2*C1*Ln(V0/Vdc) = 1000*10^-5*Ln(0.5/3.3) = 18,8 мс

Если емкость увеличить с 0,01 мкФ до 0,1 мкФ, то время разрядки конденсатора C1 до уровня V0 будет уже 180 мс, что довольно много. Однако, в данном случае процессы настолько медленные, что можно оставить и такую емкость. Её увеличение, а, соответственно, инерционности, лучше сглаживает короткие всплески, уменьшая вероятность ложных срабатываний.

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

ESP8266 и дребезг контактов

Для сборки триггера Шмитта на водосчетчике потребуются следующие компоненты (ссылки на Aliexpress):

Wemos D1 mini RT использует следующие контакты для RTC:

Wemos D1 mini
(ESP8266)
ОписаниеESP32
D1 (GPIO5) SCL GPIO 22 (SCL)
D2 (GPIO4) SDA GPIO 21 (SDA)
5V5V
GNDGND

SD card reader использует контакты:

Wemos D1 mini
(ESP8266)
ОписаниеESP32
VSPI
ESP32
HSPI
D5 (GPIO 14) CLK/SCK GPIO18 GPIO14
D6 (GPIO 12) DO/MISO GPIO19GPIO12
D7 (GPIO13) DI/MOSIGPIO23GPIO13
D8 (GPIO 15)CS/SS GPIO5GPIO15

Свободными осталось немного: D0 (GPIO16), D3 (GPIO0), D4 (GPIO3). И это все проблемные входы:

  • Вход D0 Wemos D1 mini не поддерживает обработку прерываний, соответственно, не подходит.
  • D3, D4 — входы на которых при рестарте/загрузке ESP8266 должен быть определенный уровень сигнала. Если он будет другим, то микропрограмма ESP8266 не загрузится. Т.е., при каждой перезагрузке нужно отключать выводы.
  • К D4 на Wemos D1 mini подсоединен LED.

В общем, получается, что одновременно подключить RTC с карт- ридером и модуль с триггером Шмитта не получится. Либо придется в схему добавлять чип, позволяющий расширить кол-во входов. Впрочем, особого смысла в RTC и карт-ридере нет, если воспользоваться примером кода для получения времени с NTP серверов.

Выводы триггера Шмитта SN74HC14
Выводы триггера Шмитта SN74HC14

Зачем аппаратное устранение дребезга?

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

Все бы ничего, когда питание схемы происходит от электросети. Но, подводить к водосчетчику 3,3/5 V от блока питания не всегда удобно. Нередко используется батарея вроде 18650, а емкость её не безгранична. Регулярно подзаряжать батарею — удовольствие не из приятных. Поэтому сокращение энергопотребления — очень важный момент при разработке подобных IoT устройств.

В ESP8266 есть несколько вариантов «спячки», при которой микроконтроллер потребляет существенно меньше электроэнергии, живя дольше на батарее.

Режимы спячки ESP8266
Режимы спячки ESP8266

Есть несколько подходов для снижения энергопотребления микроконтроллера ESP8266/ESP32:

  • Вместо программного опроса состояния входов в цикле loop использовать более красивый вариант с прерываниями. При использовании схемы устранения дребезга этот способ будет работать корректно. Пример кода с использованием прерываний описан в статье.
  • Загонять ESP8266/ESP32 в deep-sleep с указанием времени через которое он должен просыпаться для обработки состояния входов. В данном случае использовать аппаратное устранение дребезга не нужно. При выходе из спячки ESP8266 мог уже пропустить срабатывание геркона, поэтому нужно опросить текущее состояние входов, после чего опять уйти в спячку. Время спячки должно быть меньше минимальной продолжительности срабатывания геркона. Нужно убедится, что после срабатывания геркона, если сразу выключить воду, он останется в замкнутом состоянии. Если отключится, то нужно выбирать время deep sleep с учетом такого «форс-мажора».
  • Загонять ESP8266/ESP32 в deep-sleep и выводить его из этого состояния сняв данные с герконов через схему устранения дребезга контактов и подав импульс на вход EXT_RSTB. Для этого можно использовать свободные элементы триггера Шмитта. Схемотехнику для такого варианта опишу в другой статье.
  • Учитывая, что ESP8266/ESP32 стартуют мгновенно, можно вообще завязать цепь питания на геркон. Т.е. включать схему, при замыкании одного из герконов.

Схема водосчетчика на ESP8266 (Wemos D1 mini)

Схема и пример печатной платы в проекте EasyEDA. Оставил I2C входы незанятыми для подключения индикатора.

Схема водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Схему сделал на Wemos breadboard.

Монтажная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта
Монтажная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Пример печатной платы сформированный автотрассировкой в EasyEDA.

Печатная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Печатная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Программа водосчетчика для ESP8266

//int LED_PIN = 2; //D4
int COLD_WATER_PULSE_PIN = 12; //D6
int HOT_WATER_PULSE_PIN  = 13; //D7 4; //D2
int cold_water_counter = 0; 
int hot_water_counter = 0; 

void setup() 
{
  Serial.begin(9600);
  pinMode(COLD_WATER_PULSE_PIN, INPUT); //Pullup internal resister
  pinMode(HOT_WATER_PULSE_PIN,  INPUT); //Pullup internal resister
  //pinMode(LED_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(COLD_WATER_PULSE_PIN), triggerColdWater, RISING);
  attachInterrupt(digitalPinToInterrupt(HOT_WATER_PULSE_PIN), triggerHotWater, RISING);
}

void loop() {
  //int state = digitalRead(HOT_WATER_PULSE_PIN);
  //Serial.println("State: " + (String)(state));
  //delay(1000);
  //digitalWrite(LED_PIN, !state );
}

void triggerColdWater() 
{
  cold_water_counter++;
  Serial.println("Cold water consumption: " + (String)(cold_water_counter*10) + " l.");
}

void triggerHotWater() 
{
  hot_water_counter++;
  Serial.println("Hot water consumption: " + (String)(hot_water_counter*10) + " l.");
}

Полезные ссылки

Spread the love
Запись опубликована в рубрике IT рецепты с метками , . Добавьте в закладки постоянную ссылку.

Добавить комментарий