Автоматическое открывание домофона на ESP8266. Часть 2.

В предыдущей статье я рассмотрел схемотехнику домофонных трубок. Схема удаленного управления домофоном на 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 на отдельный разъем. К сожалению, при положенной трубке домофона питание на линию не подается. Приходится использовать внешний источник питания.
Печатная плата для удаленного управления домофоном (увеличено примерно на 40%)
Шилд управления домофоном на Wemos D1 Mini

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

Продолжение следует…

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

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

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