В предыдущей статье я рассмотрел пример схемы для снятия данных со счетчика воды. В ней для избавления от дребезга контактов использовался программный способ с использованием библиотеки 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 цепочку, для сглаживания высокочастотный коротких всплесков во время переходных процессов. Рассмотрим схемы из статьи.

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

Подавление дребезга контактов для водосчетчика (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.
- Wemos D1 mini RTC shield. Напряжение питание чипа 5V, поэтому shield не пригоден для питания от батареи 3,7 V.
- Wemos breadboard.
- Триггер Шмитта SN74HC14N. Datasheet.
- Резисторы 1 кОм — 2 шт.
- Резисторы 10 кОм — 2 шт.
- Конденсаторы керамические 100 nF (0,1 mkF) — 2 шт.
- Батарея 18650.
- Кейс для батареи 18650.
Wemos D1 mini RT использует следующие контакты для RTC:
Wemos D1 mini (ESP8266) | Описание | ESP32 |
D1 (GPIO5) | SCL | GPIO 22 (SCL) |
D2 (GPIO4) | SDA | GPIO 21 (SDA) |
5V | 5V | |
GND | GND |
SD card reader использует контакты:
Wemos D1 mini (ESP8266) | Описание | ESP32 VSPI | ESP32 HSPI |
D5 (GPIO 14) | CLK/SCK | GPIO18 | GPIO14 |
D6 (GPIO 12) | DO/MISO | GPIO19 | GPIO12 |
D7 (GPIO13) | DI/MOSI | GPIO23 | GPIO13 |
D8 (GPIO 15) | CS/SS | GPIO5 | GPIO15 |
Свободными осталось немного: D0 (GPIO16), D3 (GPIO0), D4 (GPIO3). И это все проблемные входы:
- Вход D0 Wemos D1 mini не поддерживает обработку прерываний, соответственно, не подходит.
- D3, D4 — входы на которых при рестарте/загрузке ESP8266 должен быть определенный уровень сигнала. Если он будет другим, то микропрограмма ESP8266 не загрузится. Т.е., при каждой перезагрузке нужно отключать выводы.
- К D4 на Wemos D1 mini подсоединен LED.
В общем, получается, что одновременно подключить RTC с карт- ридером и модуль с триггером Шмитта не получится. Либо придется в схему добавлять чип, позволяющий расширить кол-во входов. Впрочем, особого смысла в RTC и карт-ридере нет, если воспользоваться примером кода для получения времени с NTP серверов.

Зачем аппаратное устранение дребезга?
При использовании программного устранения дребезга микроконтроллер постоянно находится в работе, поскольку ему приходится постоянно опрашивать состояния входов. Это полноценный режим работы при котором микроконтроллер расходует электроэнергию.
Все бы ничего, когда питание схемы происходит от электросети. Но, подводить к водосчетчику 3,3/5 V от блока питания не всегда удобно. Нередко используется батарея вроде 18650, а емкость её не безгранична. Регулярно подзаряжать батарею — удовольствие не из приятных. Поэтому сокращение энергопотребления — очень важный момент при разработке подобных IoT устройств.
В 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 breadboard.

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

Печатная плата 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."); }
Полезные ссылки
- EMBED WITH ELLIOT: DEBOUNCE YOUR NOISY BUTTONS, PART II
- Arduino: Дребезг — программное и аппаратное устранение
- Подключение кнопки к Arduino
- ESP8266EX GPIO High and Low Input Thresholds
- Datasheet триггер Шмитта.
- ESP8266 Low Power Solution (ENG). Документация от разработчика.
- Вывод ESP8266 из спячки (ENG).
- https://stackoverflow.com/questions/39481196/how-to-wake-esp8266-from-deep-sleep-without-continuous-resets