В предыдущей статье я рассмотрел схемотехнику домофонных трубок. Схема удаленного управления домофоном на EasyEDA. Разрабатывал как shield для ESP8266 Wemos D1 Mini. Схема включается в разрыв между линией и домофонной трубкой, т.е. подключение простое и не требуется подсоединяться к точкам на плате домофона, как в других реализациях энтузиастов. Оттестировано на домофоне Visit.
При разработке использовались следующие компоненты:
- ESP8266 Wemos D1 Mini.
- Реле Tyco IM03 или Omron G6K. Реле ПОЛЯРИЗОВАННОЕ, т.е. на обмотку нужно подать правильную полярность напряжения. Я об этом подзабыл. В результате пришлось напаивать перемычки на готовую плату. В схеме исправлено под IM03, так что новая плата разведена с учетом поляризации.
- Оптопара TLP291 для гальванической развязки входнйо линии и входов микроконтроллера.
- Миниатюрный 5V блок питания.
- Корпус — Коробка распаячная Tyco 43х80х35 IP54 о/п (67091). Поскольку Wemos D1 Mini с shield весьма компактные, возможно размещение непосредственно внутри трубки домофона.
- На разъем Line подключается линия.
- К разъему Phone подключается домофонная трубка.
- Питание +5V на отдельный разъем. К сожалению, при положенной трубке домофона питание на линию не подается. Приходится использовать внешний источник питания.
Схема тестировалась на домофоне Visit и она стабильно автоматически открывает дверь домофона. При желании можно добавить в схему переключатель для переключения в режим автоматического открывания двери.
При поступлении вызова с панели домофона срабатывает оптрон TLP291, который опускает вход D5 (GPIO15) на землю. Сопротивление R4 работает в качестве PULL-UP, подавая 1-цу на вход GPIO14. При поступлении вызова на входе логический 0.
Реле U1 нормально замкнутое и в обесточенном состоянии коммутирует Line напрямую на домофонную трубку. В выключенном состоянии платы домофон работает как обычно.
Как только микроконтроллер определяет сигнал вызова, он подает логическую 1-цу на выход D1 (GPIO5) и коммутирует линию на схему открывания двери, взятую из схематики домофонных трубок.
Далее микроконтроллер, подав логическую 1-цу на D2 (GPIO4), может открыть дверь.
Я завел выходы с реле U1 и U2 на D6 (GPIO12 ) и D7 (GPIO13), соотвественно. Это позволяет контролировать срабатывание контактов реле. Это избыточный контроль, но свободные контакты в реле и достаточное количество входов микроконтроллера есть. Например, при использовании протокола MQTT для управления домофоном, можно использовать эти входы для пересылки подтверждения о срабатывании контактов.
Код для управления домофоном с ESP8266
Учебный код для автоматического открывания домофона:
#include <PubSubClient.h> #include <ESP8266WiFi.h> #include <WiFiManager.h> #define LINE_SWITCH_PIN 5 //Switch line to domofon #define OPEN_DOOR_PIN 4 //Open the door #define DETECT_CALL_PIN 14 //Detect call long lastDebounce = 0; long debounceDelay = 500; // Ignore bounces less than 500 ms. void ICACHE_RAM_ATTR openDoor(); //Very important!!! Otherwise interrupt doesn't work properly void setup() { Serial.begin(115200); Serial.println("Domophone remote control is enabled."); pinMode(LINE_SWITCH_PIN, OUTPUT); pinMode(OPEN_DOOR_PIN, OUTPUT); initDetectCallPin(); attachInterrupt(digitalPinToInterrupt(DETECT_CALL_PIN), openDoor, FALLING); } //Reset DETECT_CALL_PIN to LOW, otherwise it initializes as HIGH void initDetectCallPin() { pinMode(DETECT_CALL_PIN, OUTPUT); digitalWrite(DETECT_CALL_PIN, LOW); pinMode(DETECT_CALL_PIN, INPUT); } void openDoor() { Serial.println("The call has detected and interrupt fired."); ESP.wdtDisable(); delay(1000*15); //Some delay to make domophone ring Serial.println("Switch line to remote control."); digitalWrite(LINE_SWITCH_PIN, HIGH); delay(500); Serial.println("Open the door."); digitalWrite(OPEN_DOOR_PIN, HIGH); delay(1000*30); Serial.println("Return LINE SWITCH relay back to domophone."); digitalWrite(LINE_SWITCH_PIN, LOW); delay(500); Serial.println("Return the DOOR OPEN relay back."); digitalWrite(OPEN_DOOR_PIN, LOW); delay(500); //detachInterrupt(DETECT_CALL_PIN); ESP.wdtEnable(1); //ESP.restart(); //attachInterrupt(DETECT_CALL_PIN, trigger, RISING); } void loop() { /*int currentMillis = millis(); if (currentMillis - lastMillis > 1000) { call = digitalRead(DETECT_CALL_PIN); String state = (call == HIGH ? "NOT DETECTED." : "DETECTED."); Serial.println("Waiting for the call. Call status is " + state); lastMillis = currentMillis; }*/ //initDetectCallPin(); //Init DETECT_CALL_PIN }
Вот эта строчка обязательна, иначе будет ошибка «ISR not in IRAM!»:
void ICACHE_RAM_ATTR openDoor();
При вызове прерываний, если в функции обработки прерывании есть значительная задержка, то срабатывает WatchDog. ESP.wdt_Disable() немного спасает, но проблема остается, поэтому нужно вынести задержку в loop(). Стабильно работающий код такой:
#define LINE_SWITCH_PIN 5 //Switch line to domofon #define OPEN_DOOR_PIN 4 //Open the door #define DETECT_DOOR_PIN 12 //Detect door relay status #define DETECT_LINE_PIN 13 //Detect line switch relay status #define DETECT_CALL_PIN 14 //Detect call const int openDoorDelay = 20*1000; //ms to open the door const int delayBeforeOpenDoor = 15*1000; //ms before open the door const int rebootInterval = 24*60*60*1000; //reboot ESP8266 once a day void ICACHE_RAM_ATTR openDoor(); //Very important!!! Otherwise will got the error "ISR not in IRAM!" void ICACHE_RAM_ATTR DoorStatus(); //Very important!!! Otherwise will got the error "ISR not in IRAM!" void ICACHE_RAM_ATTR LineStatus(); //Very important!!! Otherwise will got the error "ISR not in IRAM!" void setup() { Serial.begin(115200); Serial.println("Domophone remote control is enabled."); pinMode(LINE_SWITCH_PIN, OUTPUT); pinMode(OPEN_DOOR_PIN, OUTPUT); pinMode(DETECT_DOOR_PIN, INPUT_PULLUP); pinMode(DETECT_LINE_PIN, INPUT_PULLUP); initDetectCallPin(); attachInterrupt(digitalPinToInterrupt(DETECT_CALL_PIN), openDoor, FALLING); attachInterrupt(digitalPinToInterrupt(DETECT_DOOR_PIN), DoorStatus, FALLING); attachInterrupt(digitalPinToInterrupt(DETECT_LINE_PIN), LineStatus, FALLING); } //Reset DETECT_CALL_PIN to LOW, otherwise it initializes as HIGH void initDetectCallPin() { pinMode(DETECT_CALL_PIN, OUTPUT); digitalWrite(DETECT_CALL_PIN, LOW); pinMode(DETECT_CALL_PIN, INPUT); } bool doorStatus = false; void DoorStatus() { if (!doorStatus) { Serial.println("----------------------------"); Serial.println("The door has opened."); Serial.println("----------------------------"); doorStatus = true; } } bool lineStatus = false; void LineStatus() { if (!lineStatus) { Serial.println("----------------------------"); Serial.println("The line is connected to the Remote Control."); Serial.println("----------------------------"); lineStatus = true; } } long openDoorMillis = 0; bool callDetectorStatus = false; void openDoor() { if (!callDetectorStatus) { Serial.println("The call has detected and interrupt fired."); openDoorMillis = millis(); //Save time when call detector was fired callDetectorStatus = true; } } void loop() { if (callDetectorStatus && (millis() - openDoorMillis > delayBeforeOpenDoor)) //make delay for domophone ring { long currentMillis = millis(); Serial.println("Switch line to the Remote Control at " + String(currentMillis) + " ms."); digitalWrite(LINE_SWITCH_PIN, HIGH); delay(100); Serial.println("Open the door after " + String(millis() - currentMillis) + " ms."); digitalWrite(OPEN_DOOR_PIN, HIGH); delay(openDoorDelay); Serial.println("Return LINE SWITCH relay back to domophone after " + String(millis() - currentMillis) + " ms."); digitalWrite(LINE_SWITCH_PIN, LOW); delay(100); Serial.println("Return the DOOR OPEN relay back after " + String(millis() - currentMillis) + " ms."); digitalWrite(OPEN_DOOR_PIN, LOW); openDoorMillis = 0; callDetectorStatus = false; doorStatus = false; lineStatus = false; } if (millis() > rebootInterval) { ESP.restart(); } }
Чтобы убрать лишние сообщения из-за дребезга контактов в соотвествующие обработчики прерываний добавлены индикаторы срабатывания.
В код добавлен вызов функции ESP.restart() раз в сутки, на всякий случай, чтобы обеспечить стабильную работу кода при продолжительной эксплуатации.