Categories
ESP32 Home Assistant

Smarte Türklingel optimiert

Vor einiger Zeit habe ich meine Türklingel smart gemacht, hier der Link.

Relativ schnell hat sich herausgestellt, dass öfter ein Klingeln detektiert wird, wenn man das Licht an- bzw. ausschaltet. Da das Kabel, mit dem die Klingel angesteuert wird nicht geschirmt ist, ist das nicht weiter verwunderlich.

Wir erinnern uns: Der ESP32 reagiert auf das Abfallen des Signals mit einem Interrupt und detektiert ein Klingeln.

Also habe ich mir die Störung auf dem Oszilloskop genauer angeschaut.

Oszilloskop, Störung am ESP32 Eingang

Die Störung ist zirka 6ms Sekunden lang und beginnt sehr “unsauber”.

Danach habe ich mir ein echtes so kurz wie mögliches Klingeln angeschaut.

Oszilloskop, Klingeln am ESP32 Eingang

Was sofort ins Auge fällt ist, dass ein “echtes” Klingeln deutlich länger als die Störung ist. Betrachtet man die Zeit Skala des Oszilloskop Bildes, fällt auf, dass wir nicht mehr 1ms/Einheit sondern 20ms/Einheit haben. Eigentlich müsste man also nur messen, wie lang das Signal “low” ist, um dann entscheiden zu können, ob man eine Klingelbenachrichtigung auslöst. – Zumindest theoretisch. – Am ESP32 ist das nicht so einfach, da man im “Interrupt” nicht so einfach die Möglichkeit hat, ab zu fragen, wie lang der Eingang low/high ist. – Man könnte den Interrupt auch auf “on-low”, sowie auf “on-high” registrieren und messen, wie lang der Abstand zwischen diesen beiden Interrupts ist. Das könnte funktionieren.

Dann habe ich mir das Klingelsignal etwas genauer angeschaut, sowohl den Anfang, wie auch etwa in der Mitte.

Oszilloskop, Klingeln am ESP32 Eingang, Zoom: Anfang
Oszilloskop, Klingeln am ESP32 Eingang, Zoom: Mitte

Dabei habe ich festgestellt, dass das einfache Messen der Differenz zwischen “on-low” und “on-high” nicht zielführend ist. Wenn man allerdings die Differenz zwischen dem ersten “on-low” und dem letzten “on-high” misst, diese mindestens 40ms betragen muss, sodass ein Klingeln ausgelöst wird, kann man die durch Störungen ausgelösten Klingelbenachrichtigungen bis auf wenige Ausnahmen komplett eliminieren. Der Detektorzeitraum wird nach vier Sekunden zurückgesetzt, damit nicht zwei Störungen zu einer Benachrichtigung führen. – Warum werden nur “fast” alle falschen Klingelbenachrichtigungen eliminiert? – Es kann sein man schaltet das Licht an und innerhalb von vier Sekunden wieder aus und es kommt der Umstand dazu, dass bei beiden Schaltvorgängen Störungen ausgelöst werden, dann erhält man dennoch eine Klingelbenachrichtigung. Das kommt bei mir innerhalb eines Monats maximal einmal vor.

Software

Der ESP32 reagiert jetzt auf “on-low” sowie “on-high” und misst die Differenz zwischen dem ersten “on-low” und dem letzten “on-high”. Beträgt dieser mindestens 40ms, wird klingeln ausgelöst. Die beiden Zeitstempel werden nach vier Sekunden zurückgesetzt, um eine Fehlauslösung durch Störungen zu verhindern.

#include "WiFi.h"
#include <WebServer.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>

#include <WiFiClientSecure.h>

#include <TaskScheduler.h>

#include <NTPClient.h>
#include <WiFiUdp.h>

// --------- WIFI -----------

[...]

unsigned long previousMillis = 0;
unsigned long interval = 30000;

// --------- END WIFI -------

// --------- INITS -------

const char* ssid = STASSID;
const char* password = STAPSK;
const char* deviceName = DEVICENAME;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "de.pool.ntp.org", 0, 0);
WebServer server(80);
StaticJsonDocument<512> jsonDocument;
char jsonBuffer[512];

const String HomeAssistantBearerName = "Authorization";
const String HomeAssistantBearerContent = "Bearer HomeAssistantBearer";

const String SendApiIotUrl = "HOME-Assistant-URL";

// --------- END INITS -------

// --------- SCHEDULER BEGIN -------

void resetRing();
Task scheduleResetRing(120*1000, TASK_FOREVER, &resetRing);

void checkFreeRam();
Task scheduleCheckFreeRam(60*1000, TASK_FOREVER, &checkFreeRam);

Scheduler runner;

// --------- SCHEDULER END ---------

// ------- DEFINITIONS ----------
static String linkColorNormal = "#2321B0";
static String linkColorVisited = "#2321B0";
static String activeMarkerBegin = "<b>&raquo;";
static String activeMarkerEnd = "&laquo;</b>";
// ------- END DEFINITIONS ----------

// --------- Pins -----------

static int morsePin = 2;
const int detectLed = 4; // D4
const int testButton = 18; // D18
const int dorbellPin = 34; // D34

// --------- END Pins ----------

// --------- Variables ---------

volatile bool ringActive = false;
volatile bool sentActive = false;
volatile bool sentInactive = false;
volatile unsigned long ringMillis = -1;
volatile unsigned long rangMillis = -1;

int freeHeap = 0;

String logText = "";

// --------- END Variables ---------

// --------- Interrupt Functions -----------

void IRAM_ATTR eventDorbell()
{

  int bellVal = digitalRead(dorbellPin);
  if (bellVal == 1)
  {
    rang();
  }
  else
  {
    ring();
  }
}

// --------- END Interrupt Functions -----------

void setup() 
{
  initSerial();

  delay(2000);

  initWifi();
  
  initPinModes();

  initSchedules();

  initTimeClient();

  initServer();
}

void initSerial()
{
  Serial.begin(115200); 
  while(!Serial){} // Waiting for serial connection
  Serial.println();
}

void initWifi()
{
  Serial.println("WiFi init");
  Serial.print("Wifi: ");
  Serial.println(ssid);

  Serial.println("turn wifi off...");
  WiFi.mode(WIFI_OFF);
  delay(10);

  delay(200);

  WiFi.mode(WIFI_STA);
  delay(250);
  
  WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);    
  delay(200); 

  Serial.println("setting hostname");
  WiFi.setHostname(deviceName);
  delay(200); 
  Serial.println("Connecting to WiFi..");
  WiFi.begin(ssid, password);
  delay(200); 

  int iCounter = 0;
  int iMax = 30;
  while (WiFi.status() != WL_CONNECTED && iCounter < iMax)
  {
    digitalWrite(morsePin, LOW);
    delay(500);
    digitalWrite(morsePin, HIGH);
    delay(500);
    Serial.print(".");
    iCounter++;
  }

  digitalWrite(morsePin, LOW);

  Serial.println(WiFi.localIP()); 
}

void initPinModes()
{
  Serial.println("PIN inits");
  
  pinMode(morsePin, OUTPUT);
  pinMode(detectLed, OUTPUT);
  pinMode(testButton, INPUT);
  pinMode(dorbellPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(dorbellPin), eventDorbell, CHANGE);
}

void initSchedules()
{
  Serial.println("SCHEDULES init");
  
  runner.init();

  runner.addTask(scheduleCheckFreeRam);
  scheduleCheckFreeRam.enable();

  runner.addTask(scheduleResetRing);
  scheduleResetRing.enable();
}

void initTimeClient()
{
  timeClient.begin();
  timeClient.update();
}

void initServer()
{
  server.on("/", handleConnect);
  server.on("/jsondoaction", jsonDoAct);
  server.onNotFound(handleConnect);
  server.begin();
  Serial.println("HTTP server started");
}

void checkFreeRam()
{
  freeHeap = ESP.getFreeHeap();

  if (ESP.getFreeHeap() < 60000)
  {
    ESP.restart();
  }
}

void resetRing()
{
  sendRingStatus();
}

void loop() 
{
  wifiReconnectCheck();

  handleButtons();

  checkRing();

  handleRing();

  server.handleClient();
  runner.execute();

}

void checkRing()
{
  if (ringMillis != -1 && 
      rangMillis != -1)
  {

    if (rangMillis < ringMillis)
    {
      return;
    }
    if ((rangMillis - ringMillis) < 40)
    {
      return;
    }

    if ((rangMillis - ringMillis) >= 40)
    {
      ringActive = true;
      digitalWrite(detectLed, HIGH);
    }

    ringMillis = -1;
    rangMillis = -1;
  }

  // if too long
  if (ringMillis != -1)
  {
    if (millis() < ringMillis ||
        (millis() - ringMillis) > 4000)
    {
      ringActive = false;
      digitalWrite(detectLed, LOW);
      ringMillis = -1;
    }
  }

  if (rangMillis != -1)
  {
    if (millis() < rangMillis ||
        (millis() - rangMillis) > 4000)
    {
      ringActive = false;
      digitalWrite(detectLed, LOW);
      rangMillis = -1;
    }
  }
}

void wifiReconnectCheck()
{
  unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval))
  {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    previousMillis = currentMillis;
  }
}

void handleButtons()
{
  int testBtn = digitalRead(testButton);

  if (testBtn == 1)
  {
    digitalWrite(morsePin, HIGH);
    delay(100);
    testBtn = digitalRead(testButton);
    digitalWrite(morsePin, LOW);
    if (testBtn == 1)
    {
      ringActive = true;
      digitalWrite(detectLed, HIGH);
    }
    delay(2000);
  }
}

void handleRing()
{
  if (ringActive == true && sentActive == false)
  {
    sendRingStatus();
  }
  else if (ringActive == false && sentInactive == false)
  {
    sendRingStatus();
  }
  else if (ringActive == true && sentInactive == true)
  {
    sendRingStatus();
  }
}

void ring()
{
  if (ringMillis == -1)
  {
    ringMillis = millis();
  }
}

void rang()
{
  rangMillis = millis();
}

void unring()
{
  ringActive = false;
  digitalWrite(detectLed, LOW);
}

String sendRingStatus()
{
  String retStr;

  HTTPClient *https = new HTTPClient();
  https->setReuse(false);

  Serial.print("[HTTP] begin...\n");
  Serial.println("[HTTP] URL: " + SendApiIotUrl);
  
  if (https->begin(SendApiIotUrl))
  {
    Serial.print("[HTTP] POST...\n");
    https->setTimeout(30000);
    // start connection and send HTTP header
    https->addHeader("Content-Type", "application/json; charset=UTF-8");
    https->addHeader(HomeAssistantBearerName, HomeAssistantBearerContent); // auth

    // status Json
    bool initialRingActive = ringActive;
    String sendContent = "{\"state\": \"off\" }";
    if (initialRingActive == true)
    {
      sendContent = "{\"state\": \"on\" }";
    }

    Serial.println("Content: " + sendContent);
    
    int httpCode = https->POST(sendContent);

    // httpCode will be negative on error
    if (httpCode > 0)
    {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] POST... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
      {
        retStr = https->getString();

        if (initialRingActive == true)
        {
          sentActive = true;
          sentInactive = false;
          delay(3000);
          unring();
        }
        else if (initialRingActive == false)
        {
          sentActive = false;
          sentInactive = true;
        }
        
      }
    }
    else
    {
      Serial.printf("[HTTP] GET... failed, error: %s\n", https->errorToString(httpCode).c_str());
      retStr = "error: ";
      retStr += https->errorToString(httpCode).c_str();
    }

    https->end();
  }
  else
  {
    Serial.printf("[HTTP] Unable to connect\n");
    retStr = "ConErr";
  }

  delete https;
  https = NULL;

  delay(2000);

  return retStr;
}

// HTTP Server

void handleConnect()
{
  Serial.println("Connect");
  server.send(200, "text/html", SendHTML("")); 
}

String SendHTML(String context)
{
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>ESP32 Doorbell</title>\n";
  ptr +="<style>html { font-family: Arial; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +="a, a:active, { color: " + linkColorNormal + "; text-decoration: underline; }\n";
  ptr +="a:visited { color: " + linkColorVisited + "; text-decoration: underline; }\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP32 Doorbell</h1>\n";

  String lineBreak = "<br><br>";

  ptr += logText;

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

void jsonDoAct()
{
  Serial.println("JSON Act");

  String status = "OK";
  
  if (server.hasArg("plain") == false) 
  {
    //handle error here
  }
  
  String body = server.arg("plain");
  Serial.println(body);
  deserializeJson(jsonDocument, body);

  // continue
  status = "OK";

  createStatusJson(status);

  server.send(200, "application/json", jsonBuffer);
}

// -------------------- JSON HELPER --------------------------

void createStatusJson(String statusIn) 
{
  if (statusIn == "")
  {
    statusIn = "OK";
  }
  
  jsonDocument.clear();  
  jsonDocument["state"] = statusIn;
  jsonDocument["millis"] = millis();
  jsonDocument["ringActive"] = ringActive;
  jsonDocument["sentActive"] = sentActive;
  jsonDocument["sentInactive"] = sentInactive;

  jsonDocument["freeram"] = freeHeap;

  serializeJson(jsonDocument, jsonBuffer);
}

// logger

void putlog(String strIn)
{
  //logText += strIn + "<br>";
}

Hinweis

Dieser Artikel dokumentiert lediglich meinen Aufbau. Für den Nachbau, die Nutzung einzelner Komponenten, die Platinen und den gesamten Inhalt wird die Haftung in jeglicher Form ausgeschlossen.

Categories
ESP32 Home Assistant

Klassische Türklingel smart gemacht

Einige haben sie noch, viele kennen sie noch, manche haben sie schon ersetzt. Die Rede ist von der klassischen Türklingel mit Wechselstrom, die entweder schrillt oder metallern “ding-dong” von sich gibt, wenn an der Tür jemand den Taster mit der Glocke betätigt.

Das Problem ist, wenn man sich gerade im Keller oder draußen befindet, hört man die Klingel nicht immer.

Klassische Klingel, außen Ansicht
Klassische Klingel, innen Ansicht

Hier wird durch das “Klingeln” der Bolzen in der Mitte nach links geschlagen, das ist das “ding”, lässt man den Taster los, schlängt der Bolzen wieder nach rechts, das ist das “dong”. An den beiden Kontakten liegt beim Klingeln Wechselspannung an, meist ~12V.

Ich habe mir überlegt, dass man den zum Teil kurzen Wechselspannungsimpuls durch einen ESP32 detektieren müsste, um ihn dann dem Home Assistant für eine Benachrichtigung zu nutzen.

Da es sich um Wechselspannung handelt, könnte es durchaus sein, dass der ESP32 aufgrund des schnellen Wechsels nichts detektiert oder mehrfach detektiert. Ein ESP32 bietet die Funktion auf den Wechsel eines Eingangs von Hi nach Lo, sowie umgekehrt zu reagieren, unabhängig ob der Eingang gerade im Code abgefragt wird, dazu später.

Schaltplan

Aber eins nach dem anderen. Zuerst werden die ~12V mit Dioden gleichgerichtet. Richtet man Wechselspannung gleich, haben wir viele “kleine Berge”. Für das oben genannte “Dektieren” wäre das nicht von Vorteil, entweder ist der ESP32 zu langsam, oder zu schnell und es wird bei langem klingeln, mehrfach klingeln gemeldet. Was wir zum Gleichrichter also noch benötigen ist etwas, was die Spannung nach dem Gleichrichter stabilisiert, dazu verwende ich einen 22µF Kondensator. Um zu verhindern, dass sich hier ungewollt eine Spannung aufbaut, sowie der Kondensator nach dem Klingeln wieder zügig entlädt, habe ich zwischen dem Ausgang des Gleichrichters und Masse einen Widerstand mit 1kO verbaut.

Nachdem das Signal gleichgerichtet ist, müssen wir dafür sorgen, dass der Eingang des ESP32 maximal 3,3V erhält und keine Spannung darüber, dazu verwende ich einen BC547C Transistor als Schalter. Der Kollektor wird hierbei über einen Widerstand auf Hi gezogen, während am Kollektor der Eingang des ESP32 angeschlossen ist. Wird jetzt die Klingel aktiviert, liegt an der Basis des Transistors eine Spannung an, der Transistor wird leitend und der Eingang des ESP32 bzw. der Kollektor wird gegen Masse “gezogen”.

Schaltplan – ~1 sowie ~2 sind die Anschlüsse für den Klingeldraht – Schaltplan erstellt mit sPlan

Platine

Hier die Platine dazu (Afillate):  https://aisler.net/p/RGOCICIL

Bauteile:

  • C1 : 22 µF
  • C2, C3, C4 : 10nF
  • D1, D2, D3, D4 : Diode Typ 1N 4148
  • IC1 : RECOM R-78E50-05
  • L1 : 10 µH
  • LED1 : Standard LED
  • R1 : 2.2kO
  • R2, R3 : 5.6kO
  • R4 : 22kO
  • R5 : 1kO
  • T1 : BC547C
  • Test : Dip-Taster

Hardware

Nach einigen Test mit dem Oszilloskop auf dem Breadboard und zusammenlöten der Platine, habe ich ein Gehäuse für den Aufbau erstellt.

Gehäuse für die Platine – Löcher für Kabel müssen gebohrt werden
Deckel für das Gehäuse mit Lüftungsschlitzen

Die erste Montage sieht wie folgt aus:

Montiert, ohne Deckel

Nach positivem Verlauf der Tests, habe ich die mittlerweile vergilbte Klingelabdeckung angeschliffen und mit Sprühlack lackiert, inklusive Klarlack.

Frisch lackiertes Klingelgehäuse
Fertig montierte Klingel mit Klingel Detector

Home Assistant

Wichtig ist, dass die Klingel proaktiv dem Home Assistant meldet, dass geklingelt wurde. Dafür muss im Home Assistant ein Template Sensor angelegt werden.

binary_sensor:
  - platform: template
    sensors:
      door_bell:
        friendly_name: "Türklingel"
        value_template: "{{ state_attr('binary_sensor.eg_front_door_bell', 'ring') }}"

Auf die Statusänderung des Binary Sensor lässt sich dann ein Automatismus registrieren, der z.B. per Benachrichtigung an mobile Geräte das Klingeln meldet.

Software

Sobald die Türklingel gedrückt wurde und der GPIO Port des ESP32 auf Low geschaltet wird, soll dies an Home Assistant zurückgemeldet werden. Somit muss ein Handler auf das “to-low” bzw FALLING des GPIO Ports registriert werden, ein sogenannter Interrupt. Im Anschluss muss dann der Klingelstatus an den Home Assistant übermittelt werden, genauso wie einige Zeit später das Klingel Signal durch den ESP32 wieder resettet werden muss. Der Bearer ist ein Langzeittoken, dass als Admin erstellt werden muss.

#include <ArduinoJson.h>
#include <HTTPClient.h>

#include <WiFiClientSecure.h>

#include <TaskScheduler.h>

#include <NTPClient.h>
#include <WiFiUdp.h>

// --------- WIFI -----------

[...]

unsigned long previousMillis = 0;
unsigned long interval = 30000;

// --------- END WIFI -------

// --------- INITS -------

const char* ssid = STASSID;
const char* password = STAPSK;
const char* deviceName = DEVICENAME;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "de.pool.ntp.org", 0, 6 * 3600 * 1000);
StaticJsonDocument<2048> jsonDocument;

const String HomeAssistantBearerName = "Authorization";
const String HomeAssistantBearerContent = "Bearer XXX";

const String SendApiIotUrl = "http://homeAssistantIp:8123/api/states/binary_sensor.door_bell";

// --------- END INITS -------

// --------- SCHEDULER BEGIN -------

void resetRing();
Task scheduleResetRing(120*1000, TASK_FOREVER, &resetRing);

void checkFreeRam();
Task scheduleCheckFreeRam(60*1000, TASK_FOREVER, &checkFreeRam);

Scheduler runner;

// --------- SCHEDULER END ---------

// --------- Pins -----------

static int morsePin = 2;
const int detectLed = 4; // D4
const int testButton = 18; // D18
const int dorbellPin = 34; // D34

// --------- END Pins ----------

// --------- Variables ---------

bool ringActive = false;
bool sentActive = false;
bool sentInactive = false;
unsigned long ringMillis = -1;

// --------- END Variables ---------

// --------- Interrupt Functions -----------

void IRAM_ATTR eventDorbell()
{
  detachInterrupt(dorbellPin);

  ring();
}

// --------- END Interrupt Functions -----------


void setup() 
{
  initSerial();

  delay(2000);

  initWifi();
  
  initPinModes();

  initSchedules();

  initTimeClient();
}

void initSerial()
{
  Serial.begin(115200); 
  while(!Serial){} // Waiting for serial connection
  Serial.println();
}

void initWifi()
{ 
  [...]
}

void initPinModes()
{
  Serial.println("PIN inits");
  
  pinMode(morsePin, OUTPUT);
  pinMode(detectLed, OUTPUT);
  pinMode(testButton, INPUT);
  pinMode(dorbellPin, INPUT);
  attachInterrupt(dorbellPin, eventDorbell, FALLING);
}

void initSchedules()
{
  Serial.println("SCHEDULES init");
  
  runner.init();

  runner.addTask(scheduleCheckFreeRam);
  scheduleCheckFreeRam.enable();

  runner.addTask(scheduleResetRing);
  scheduleResetRing.enable();
}

void initTimeClient()
{
  timeClient.begin();
  timeClient.update();
}

void checkFreeRam()
{
  if (ESP.getFreeHeap() < 60000)
  {
    ESP.restart();
  }

  //Serial.println("Free RAM: " + String(ESP.getFreeHeap()));
}

void resetRing()
{
  sendRingStatus();
}

void loop() 
{
  wifiReconnectCheck();

  handleButtons();

  bool beforeRingVal = ringActive;

  handleRing();

  runner.execute();

}

void ringResetter()
{
  if (ringMillis != -1)
  {
    unsigned long currentMillis = millis();

    if (ringMillis > currentMillis)
    {
      ringMillis = currentMillis;
    }
    
    if (currentMillis - ringMillis >= 30000)
    {
      bool doorbellState = digitalRead(dorbellPin);
      if (doorbellState == true)
      {
        unring();
      }
    }
  }
}

void wifiReconnectCheck()
{
  unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval))
  {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    previousMillis = currentMillis;
  }
}

void handleButtons()
{
  int testBtn = digitalRead(testButton);

  if (testBtn == 1)
  {
    digitalWrite(morsePin, HIGH);
    delay(100);
    testBtn = digitalRead(testButton);
    digitalWrite(morsePin, LOW);
    if (testBtn == 1)
    {
      ring();
    }
    delay(2000);
  }
}

void handleRing()
{
  if (ringActive == true && sentActive == false)
  {
    sendRingStatus();
  }
  else if (ringActive == false && sentInactive == false)
  {
    sendRingStatus();
  }
  else if (ringActive == true && sentInactive == true)
  {
    sendRingStatus();
  }
}

void ring()
{
  ringActive = true;
  ringMillis = millis();
  digitalWrite(detectLed, HIGH);
}

void unring()
{
  ringActive = false;
  digitalWrite(detectLed, LOW);
  attachInterrupt(dorbellPin, eventDorbell, FALLING);
}

String sendRingStatus()
{
  String retStr;

  HTTPClient *https = new HTTPClient();
  https->setReuse(false);

  Serial.print("[HTTP] begin...\n");
  Serial.println("[HTTP] URL: " + SendApiIotUrl);
  
  if (https->begin(SendApiIotUrl))
  {
    Serial.print("[HTTP] POST...\n");
    https->setTimeout(30000);
    // start connection and send HTTP header
    https->addHeader("Content-Type", "application/json; charset=UTF-8");
    https->addHeader(HomeAssistantBearerName, HomeAssistantBearerContent); // auth

    // status Json
    bool initialRingActive = ringActive;
    String sendContent = "{\"state\": \"off\" }";
    if (initialRingActive == true)
    {
      sendContent = "{\"state\": \"on\" }";
    }

    Serial.println("Content: " + sendContent);
    
    int httpCode = https->POST(sendContent);

    // httpCode will be negative on error
    if (httpCode > 0)
    {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] POST... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
      {
        //String payload = https->getString();
        //Serial.println(payload);
        retStr = https->getString();

        if (initialRingActive == true)
        {
          sentActive = true;
          sentInactive = false;
          delay(3000);
          unring();
        }
        else if (initialRingActive == false)
        {
          sentActive = false;
          sentInactive = true;
        }
        
      }

      //String payload = https->getString();
      //Serial.println(payload);
    }
    else
    {
      Serial.printf("[HTTP] GET... failed, error: %s\n", https->errorToString(httpCode).c_str());
      retStr = "error: ";
      retStr += https->errorToString(httpCode).c_str();
    }

    https->end();
  }
  else
  {
    Serial.printf("[HTTP] Unable to connect\n");
    retStr = "ConErr";
  }

  delete https;
  https = NULL;

  delay(2000);

  return retStr;
}

Probleme

Klingeldrähte sind meist ohne Abschirmung verlegt, das heißt wenn ein Elektromagnetischer Impuls durch z.B. das Abschalten einer LED Lampe oder schalten eines Relais in der Nähe der Klingelleitung vollzogen wird, meldet die Klingel ab und an (wenige Male pro Monat) fälschlicherweise klingeln. Wahrscheinlich würde sich das Problem durch das Ersetzen der Klingelleitung durch eine abgeschirmte Leitung, beispielsweise CAT7 lösen lassen.

Noch ein Hinweis: Wenn die Klingel gedrückt ist, der Trafo im Schaltschrank z.B. ~12V auf der Sekundärseite nominal aufweist, kann es sein, dass bei angeschlossener Klingel weniger als 12V direkt vor der Klingel messbar sind, das ist normal. Das kommt dadurch, weil die Klingel Strom braucht und der Trafo oft nur eine Begrenze menge Strom liefern kann.

Hinweis

Dieser Artikel dokumentiert lediglich meinen Aufbau. Für den Nachbau, die Nutzung einzelner Komponenten, die Platinen und den gesamten Inhalt wird die Haftung in jeglicher Form ausgeschlossen.