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