Benutzer-Werkzeuge

Webseiten-Werkzeuge


sonstiges:tuerklingel

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
sonstiges:tuerklingel [2023-09-29 21:58] – [Empfänger mit Netzteil versorgen] schuballaasonstiges:tuerklingel [2026-05-07 02:21] (aktuell) – schuballaa
Zeile 1: Zeile 1:
-====== Türklingel ======+====== Klingeldetektor ======
  
 +<note>Zurzeit ist die Funkbrücke in die E-Werkstatt und den Kreativraum außer Betrieb! Auch wird die Torklingel zurzeit überarbeitet!</note>
  
-===== Übersicht =====+Es wird unsere Telegram Gruppe gepostet sobald geklingelt wurde.
  
-Im Hackerspace ist am Eingang Bornstr. 14-15 eine Türklingel mit Sprechanlage (Türsprechstelle) installiert. Das Haustelefon befindet sich im Workshopraum.+{{:sonstiges:photo_2026-04-20_02-48-38.jpg?400|}}
  
-Es wurde eine Möglichkeit gefunden, das Klingelsignal abzugreifen und in die Räume Bornstr. 16-17 zu verlängern (E- und Kreativwerkstatt).+[[https://schuballaa.hackerspace-bremen.de/telegram.php|HSHB Telegram Gruppe]]
  
-**Optional:**+{{:sonstiges:photo_2026-04-19_21-24-52.jpg?400|}}
  
-Es könnte zu jeder zeit an sender und oder Empfängern eine Li-ion Akku (via JST) angeschlossen werden und somit auch mobil betrieben werden z.b bei Veranstaltungen die im Hinnenhof stattfinden um kein Klingeln zu verpassen.+Der Klingeldetektor erkennt, ob an der Tür oder am Tor geklingelt wurde. 
 +===== Erkennungswege =====
  
-**Achtung bei Powerbanks!**+  * **Tür:** Auswertung des Datenbusses des hauseigenen Klingelsystems (**QwikBus**) 
 +  * **Tor:** Erkennung über **LDR (Lichtsensor)** am Empfänger der Torklingel
  
-Da sich die meisten Powerbanks bei geringer Last ausschalten, könnte es sein, dass der Empfänger bei mobilen betrieb mit Powerbanks die Funktion nach kurzer zeit Einstellen ([[https://apfelhirn.de/automatische-abschaltung-von-powerbanks-als-arduino-stromversorgung-bei-geringer-grundlast-verhindern/|https://apfelhirn.de/automatische-abschaltung-von-powerbanks-als-arduino-stromversorgung-bei-geringer-grundlast-verhindern/]]).+===== Bei einem Klingelereignis =====
  
 +  * LED-Visualisierung Ort (**Tor / Tür**)
 +  * Akustische und visuelle Anzeige im Hauptraum
 +  * Klingelsignal weitergabe an **E-Werkstatt** und **Kreativwerkstatt** (über eine **Funkbrücke**)
 +  * Meldung an Telegram ([[https://t.me/joinchat/OwmGe1WZmRs1ZDJi|Gruppe]])
 +  * Meldung per **MQTT** im Hackerspace-System (für zukünftige Erweiterungen)
  
-===== Sprechanlage =====+===== Verhalten im Standby =====
  
-Die Sprechanlage nutzt ein 2-Draht-Bussystem vom Typ [[http://www.str-elektronik.de/|STR]] QwikBus, welches die Türsprechstelle mit den Haustelefonen verbindet. Daher ist es nicht möglich, das Klingelsignal direkt abzugreifen, da es digital über den Bus übertragen wird.+  * Warten auf ein Klingelereignis 
 +  * LEDs werden ein- und ausgeblendet, um Standby und Funktionsbereitschaft zu visualisieren
  
-Das Haustelefon ist vom Typ STR HT 3033.  
  
-Das Steuergerät ist ein SP 333 ({{:sonstiges:einstellungen_sp333_version_2.pdf|Doku}}), welches im Hauskeller im Sicherungsschrank neben dem Netzteil NH 333 installiert ist. +===== Umgesetzte Extras =====
-===== Pinout =====+
  
-Öffnet man das Haustelefon über die einzig vorhandene Gehäuseschraube (die obere Gehäusehälfte lässt sich dann herunterklappen), so kommen innen zwei Steckverbinder zum Vorschein.+  * MQTT-Integration 
 +  * Akustische und visuelle Anzeige im Hauptraum 
 +  * Funkverteilung in die anderen Räume 
 +  * LED-Visualisierung (Tor / Bornstraße)
  
-Die Belegung lässt sich durch ein Foto einer Relaisbox {{:sonstiges:aps658.pdf|AM333}}, die man hinzukaufen kann, erahnen. Ein Multimeter half bei der weiteren Analyse. Auf dem zweiten Pin von oben liegt Masse, auf dem zweiten Pin von unten wird das Klingelsignal ausgegeben (5 V, wenn ich mich recht erinnere).+===== Danksagungen: =====
  
-{{:sonstiges:tuersprechstelle.png?200| }} +  * **Marco** – Tips bei der Entwicklung der Hardware 
-{{:sonstiges:pinout.png?200| }}+  * **Delpiño** – Unterstützung beim Auswertendes Datenbusses 
 +  * **Markus** – Idee zur LED-Visualisierung (Tor / Bornstraße) 
 +===== Technische Erklärung =====
  
-Mechanisch konnten die passenden Steckerweibchen nicht ermittelt werden. Daher greifen wir das Signal über zwei IC-Beinchen-Einsätze gedrehter IC-Sockel ab, die in den Steckverbindern verlässlich halten.+{{:sonstiges:photo_2026-04-04_01-50-41.jpg?400|}} 
 +==== Tür – QwikBus-Auswertung ====
  
-===== Funk-Türklingel =====+Das hauseigene Klingelsystem (QwikBus) überträgt Klingelereignisse als zeitkodierte elektrische Signale.
  
-Da uns keine Leitung zwischen den Räumen Bornstr. 14-15 sowie 16-17 zur Verfügung steht, mussten wir auf eine [[http://www.amazon.de/dp/B00ISMW1DG|Funk-Türklingel]] mit zwei Empfängern zurückgreifen. Der Sender sowie die 2 Empfänger werden mit einem Standard 5 Volt USB Netzteil versorgt.+**Signalcharakteristik:** 
 +  * Ruhespannung: ca. ~20 V 
 +  * Aktivsignal: ca. ~18 V 
 +  * Information in **zeitlichen Pulsfolgen**
  
-Wenn geklingelt wird, zieht ein n channel mosfet den Pin 4 des [[https://www.amazon.de/AZDelivery-Lolin-ESP32-Parent/dp/B086V1P4BL?th=1|esp32]] auf ground. Wenn dieses > 1000 ms auf Ground bleibt, wurde geklingelt und ein ESPNOW signal zu den Empfängern geschickt.+**Hardware:** 
 +  * 1 nF um nur die Busänderungen zu dedektieren 
 +  * Spannungsteiler 
 +  * Schmitt-Trigger 
 +  * Mikrocontroller (ESP32)
  
 +**Verarbeitung:**
 +  * Flankenerkennung (Interrupt)
 +  * Zeitmessung im µs-Bereich
 +  * Bildung von Sequenzen:
 +  * Quantisierung zur Reduktion von Rauschen
 +  * Mustervergleich (Pattern Matching) zur Klassifikation
  
-===== Sender in Haustelefon einbauen =====+**Ergebnis:** 
 +  * Zuordnung des Signals zu einem **Türklingel-Ereignis**
  
-Der Sender ist ein ESP32 Lolin Lite welcher auf ein Trigger auf Pin 4 wartet (als sensor wird ein n chanel mosfet benutzt. (Bild: Patine mitte links)+==== Tor – LDR-Auswertung ====
  
-{{https://wiki.hackerspace-bremen.de/_media/sonstiges/klingel_sender.jpg?nolink&600x800|klingel_sender.jpg}}+Das Torklingeln wird über das optische Signal des Empfängers erkannt.
  
 +**Signalquelle:**
 +  * Blinkende LED am Empfänger der Torklingel
  
-===== Sourcecode =====+**Hardware:** 
 +  * LDR (Lichtsensor) 
 +  * Widerstand als Spannungsteiler 
 +  * ADC-Eingang am Mikrocontroller
  
-Sender:+**Verarbeitung:** 
 +  * Kontinuierliche Helligkeitsmessung 
 +  * Schwellwertbasierte Erkennung (Hell/Dunkel) 
 +  * Zeitliche Analyse des Blinkmusters 
 +  * Entprellung zur Vermeidung von Fehltriggern
  
-#include <WiFi.h>\\ +**Ergebnis:** 
-#include <esp_wifi.h>\\ +  * Zuordnung des Signals zu einem **Torklingel-Ereignis**
-#include <esp_now.h>+
  
-// Set your new MAC Address\\+---- 
 + 
 +==== Ereignisverarbeitung ==== 
 + 
 +Nach der Erkennung erfolgt die weitere Verarbeitung: 
 + 
 +  * Klassifikation: **Tür** oder **Tor** 
 +  * Generierung eines Events 
 +  * Veröffentlichung per **MQTT** im Hackerspace-System 
 +  * Ansteuerung der **LED-Visualisierung (Tor / Bornstraße)** 
 +  * Weitergabe der Information: 
 +    * Akustisch und visuell im **Hauptraum** 
 +    * Über **Funkbrücke** an **E-Werkstatt** und **Kreativwerkstatt** 
 +{{:sonstiges:pxl_20260415_1832213091.jpg?400|}} 
 +{{:sonstiges:mqtt.png?400|}} 
 +{{:sonstiges:torsensor.jpg?400|}} 
 +{{:sonstiges:photo_2026-04-04_01-50-57.jpg?400|}} 
 + 
 +==== Code ==== 
 + 
 +<code> 
 + 
 +#include <WiFi.h> 
 +#include <esp_wifi.h> 
 +#include <PubSubClient.h> 
 +#include <time.h> 
 +#include <Preferences.h> 
 +#include <WebServer.h> 
 +#include <HTTPUpdateServer.h> 
 +#include <ArduinoOTA.h> 
 +#include <ESPmDNS.h> 
 +#include <Arduino.h> 
 +#include <cstring> 
 +#include "esp32-hal-rmt.h" 
 +#include <math.h> 
 + 
 +// ===================================================== 
 +// WLAN / MQTT 
 +// ===================================================== 
 +const char* WIFI_SSID     = "Hackerspace"; 
 +const char* WIFI_PASSWORD = "****"; 
 + 
 +const char* MQTT_HOST = "daniel-stingl.de"; 
 +const int   MQTT_PORT = ****; 
 +const char* MQTT_USER = "***"; 
 +const char* MQTT_PASS = "***"; 
 + 
 +const char* MQTT_TOPIC_TUER      = "/hshb/status/klingel/tuer"; 
 +const char* MQTT_TOPIC_TOR       = "/hshb/status/klingel/tor"; 
 +const char* MQTT_TOPIC_STATUS    = "/hshb/status/klingel/status"; 
 +const char* MQTT_TOPIC_TUER_TEST = "/hshb/status/klingel/tuertest"; 
 +const char* MQTT_TOPIC_TOR_TEST  = "/hshb/status/klingel/tortest"; 
 + 
 +// ===================================================== 
 +// OTA / Web 
 +// ===================================================== 
 +const char* OTA_HOSTNAME = "hshb-klingel"; 
 +const char* OTA_USER     = "****"; 
 +const char* OTA_PASS     = "****"; 
 + 
 +// ===================================================== 
 +// NTP 
 +// ===================================================== 
 +const char* NTP_SERVER = "pool.ntp.org"; 
 +const long  GMT_OFFSET_SEC = 3600; 
 +const int   DAYLIGHT_OFFSET_SEC = 3600; 
 + 
 +// ===================================================== 
 +// MAC 
 +// =====================================================
 uint8_t newMACAddress[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63}; uint8_t newMACAddress[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63};
  
-// REPLACE WITH YOUR ESP RECEIVER'S MAC ADDRESS\\ +// ===================================================== 
-uint8_t broadcastAddress1[] = {0x30, 0xAF, 0xA1, 0x05, 0x1D, 0x63};\\ +// Pins 
-uint8_t broadcastAddress2[] = {0x30, 0xAF, 0xA2, 0x05, 0x1D, 0x63};+// ===================================================== 
 +constexpr int bellPin    = 5;   // Trigger-Ausgang 
 +constexpr int busPin     = 32;  // RMT RX 
 +const int adcTorPin      = 33;  // ADC 
 +const int testBellPin    = 26;  // Taster 
 + 
 +// ===================================================== 
 +// RMT / Nachrichten 
 +// ===================================================== 
 +constexpr uint8_t hackerspaceBell[]  = {0x25, 0x2E, 0xC4, 0x00, 0xE8}; 
 +constexpr uint8_t hackerspaceBell2[] = {0x25, 0x2E, 0xE2, 0x00, 0xCA}; 
 + 
 +constexpr size_t bufferSize = 16; 
 +constexpr size_t maxSyms = RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; 
 + 
 +rmt_data_t symbols[maxSyms]; 
 +size_t symbolCount = maxSyms; 
 + 
 +enum { P_BAD = -1, P_ZERO = 0, P_ONE = 1, P_START = 2, P_STOP = 3 }; 
 + 
 +// ===================================================== 
 +// Zeiten / Zustände 
 +// ===================================================== 
 +unsigned long lastBellTriggered       = 0; 
 +unsigned long lastTorTriggerMillis    = 0; 
 +unsigned long lastButtonTriggerMillis = 0; 
 +unsigned long lastDebugMillis         = 0; 
 +unsigned long lastStatusMillis        = 0; 
 +unsigned long bellPulseStartMillis    = 0; 
 +unsigned long lastWifiReconnectMs     = 0; 
 +unsigned long lastMqttReconnectMs     = 0; 
 +unsigned long lastNtpSyncMs           = 0; 
 + 
 +const unsigned long BellDebounceMs     = 300; 
 +const unsigned long TorDebounceMs      = 11000; 
 +const unsigned long ButtonDebounceMs   = 300; 
 +const unsigned long Debuginterval      = 1000; 
 +const unsigned long StatusMs           = 5000; 
 +const unsigned long BellPulseMs        = 10; 
 +const unsigned long WifiRetryMs        = 10000; 
 +const unsigned long MqttRetryMs        = 5000; 
 +const unsigned long NtpResyncMs        = 6UL * 60UL * 60UL * 1000UL; 
 +const unsigned long LedEventDurationMs = 60000;  // 60 Sekunden 
 + 
 +const int adcTorThreshold = 1500; 
 + 
 +bool bellPulseActive = false; 
 +bool timeValid = false; 
 +bool lastButtonState = HIGH; 
 +bool lastTorState = false; 
 + 
 +String lastState = "IDLE"; 
 +String lastSource = "-"; 
 +String lastDecodedHex = "-"; 
 +uint32_t lastDecodedLen = 0; 
 +int adcTorValue = 0; 
 + 
 +// ========================= 
 +// LEDs 
 +// ========================= 
 +const int ledTuerPin = 23; 
 +const int ledTorPin  = 19; 
 + 
 +const int ledFreq = 5000; 
 +const int ledResolution = 8; 
 + 
 +enum LedMode { 
 +  LED_MODE_STANDBY, 
 +  LED_MODE_SHOW_TUER, 
 +  LED_MODE_SHOW_TOR 
 +}; 
 + 
 +LedMode ledMode = LED_MODE_STANDBY; 
 +unsigned long ledModeUntilMs = 0; 
 +unsigned long lastLedUpdateMs = 0; 
 +unsigned long lastBlinkToggleMs = 0; 
 +bool ledBlinkState = false; 
 + 
 +// ===================================================== 
 +// Objekte 
 +// ===================================================== 
 +WiFiClient wifiClient; 
 +PubSubClient mqttClient(wifiClient); 
 +Preferences preferences; 
 +WebServer httpServer(80); 
 +HTTPUpdateServer httpUpdater; 
 + 
 +// ===================================================== 
 +// Vorwärtsdeklarationen 
 +// ===================================================== 
 +bool connectWiFi(); 
 +bool ensureMqttConnected(); 
 +bool syncTime(); 
 +bool ensureTimeValid(); 
 +bool publishUnixTimeToTopic(const char* topic); 
 +bool publishStatus(); 
 +void markMqttPendingTuer(); 
 +void markMqttPendingTor(); 
 +bool isMqttPendingTuer(); 
 +bool isMqttPendingTor(); 
 +void clearMqttPendingTuer(); 
 +void clearMqttPendingTor(); 
 +void processPendingMqtt(); 
 +void setupHttpRoutes(); 
 +void setupArduinoOta(); 
 +void triggerBell(bool fromButton); 
 +void triggerTorEvent(const char* source); 
 +void mqttCallback(char* topic, byte* payload, unsigned int length); 
 +String buildDebugJson(); 
 + 
 +int pulseType(uint16_t us); 
 +size_t collectLows(const rmt_data_t *symbols, size_t symbolCount, uint16_t *lows); 
 +int decodeByte(const uint16_t *pulses); 
 +bool decodeMessage(const rmt_data_t *symbols, size_t symbolCount, uint8_t *msg, size_t &len); 
 +bool messageContains(const uint8_t *msg, size_t len, const uint8_t *what, size_t whatLen); 
 + 
 +// ===================================================== 
 +// WLAN / Zeit / MQTT 
 +// ===================================================== 
 +bool connectWiFi() { 
 +  if (WiFi.status() == WL_CONNECTED) return true; 
 + 
 +  unsigned long now = millis(); 
 +  if (now - lastWifiReconnectMs < WifiRetryMs) return false; 
 +  lastWifiReconnectMs = now;
  
-typedef struct bell_struct {\\ +  Serial.println("Verbinde mit WLAN..."); 
-uint64_t trigger;\\ +  WiFi.mode(WIFI_STA); 
-} bell_struct;+  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  
-bell_struct bell;+  unsigned long startAttemptTime = millis(); 
 +  while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 10000) { 
 +    delay(250); 
 +    Serial.print("."); 
 +  } 
 +  Serial.println();
  
-esp_now_peer_info_t peerInfo;+  if (WiFi.status() == WL_CONNECTED) { 
 +    Serial.println("WLAN verbunden"); 
 +    Serial.print("IP: "); 
 +    Serial.println(WiFi.localIP()); 
 +    return true; 
 +  }
  
-// callback when data is sent\\ +  Serial.println("WLAN Verbindung fehlgeschlagen"); 
-void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {\\ +  return false;
-char macStr[18];\\ +
-Serial.print("Packet to: ");\\ +
-// Copies the sender mac address to a string\\ +
-snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",\\ +
-         mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);\\ +
-Serial.print(macStr);\\ +
-Serial.print(" send status:\t");\\ +
-Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");\\+
 } }
  
-void setup() {\\ +bool syncTime() { 
-Serial.begin(115200);+  if (WiFi.status() != WL_CONNECTED) return false;
  
-WiFi.mode(WIFI_STA);+  Serial.println("Synchronisiere Zeit per NTP..."); 
 +  configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);
  
-// Set the ESP32 Board MAC Address.\\ +  time_t now = time(nullptr); 
-esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);+  int retry = 0;
  
-// Print the new MAC Address.\\ +  while (now < 100000 && retry < 20) { 
-Serial.print("[NEW] ESP32 Board MAC Address:  ");\\ +    delay(500); 
-Serial.println(WiFi.macAddress());+    Serial.print("."); 
 +    now = time(nullptr); 
 +    retry++; 
 +  } 
 +  Serial.println();
  
-if (esp_now_init() != ESP_OK) {\\ +  if (now < 100000) { 
-  Serial.println("Error initializing ESP-NOW");\\ +    Serial.println("NTP Zeit konnte nicht geholt werden"); 
-  ESP.restart();\\+    timeValid = false; 
 +    return false; 
 +  } 
 + 
 +  Serial.print("Aktuelle Unix-Zeit: "); 
 +  Serial.println((unsigned long)now); 
 +  lastNtpSyncMs = millis(); 
 +  timeValid = true; 
 +  return true;
 } }
  
-esp_now_register_send_cb(OnDataSent);+bool ensureTimeValid() { 
 +  time_t now = time(nullptr); 
 +  if (now > 100000 && timeValid) { 
 +    if (millis() - lastNtpSyncMs < NtpResyncMs) { 
 +      return true; 
 +    } 
 +  } 
 +  return syncTime(); 
 +}
  
-// register peer\\ +bool ensureMqttConnected() { 
-peerInfo.channel = 0;\\ +  mqttClient.setServer(MQTT_HOST, MQTT_PORT); 
-peerInfo.encrypt = false;\\ + 
-// register first peer\\ +  if (mqttClient.connected()) return true; 
-memcpy(peerInfo.peer_addr, broadcastAddress1, 6);\\ +  if (WiFi.status() != WL_CONNECTED) return false; 
-if (esp_now_add_peer(&peerInfo) != ESP_OK) {\\ + 
-  Serial.println("Failed to add peer");\\ +  unsigned long now = millis(); 
-  return;\\+  if (now - lastMqttReconnectMs < MqttRetryMs) return false; 
 +  lastMqttReconnectMs = now; 
 + 
 +  String clientId = "esp32-klingel-"; 
 +  clientId += String((uint32_t)ESP.getEfuseMac(), HEX); 
 + 
 +  Serial.println("Verbinde MQTT..."); 
 +  if (mqttClient.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) { 
 +    Serial.println("MQTT verbunden"); 
 + 
 +    mqttClient.subscribe(MQTT_TOPIC_TUER_TEST); 
 +    mqttClient.subscribe(MQTT_TOPIC_TOR_TEST); 
 +    Serial.println("MQTT Test-Topics subscribed"); 
 + 
 +    return true; 
 +  } 
 + 
 +  Serial.print("MQTT Verbindung fehlgeschlagen, rc="); 
 +  Serial.println(mqttClient.state()); 
 +  return false;
 } }
  
-// register second peer\\ +bool publishUnixTimeToTopic(const char* topic) { 
-memcpy(peerInfo.peer_addr, broadcastAddress2, 6);\\ +  if (!connectWiFi()) return false; 
-if (esp_now_add_peer(&peerInfo) != ESP_OK) {\\ +  if (!ensureTimeValid()) return false; 
-  Serial.println("Failed to add peer");\\ +  if (!ensureMqttConnected()) return false; 
-  return;\\+ 
 +  time_t now = time(nullptr); 
 +  uint64_t nowMs = ((uint64_t)now) * 1000ULL + (millis() % 1000ULL); 
 + 
 +  char payload[32]; 
 +  snprintf(payload, sizeof(payload), "%llu", (unsigned long long)nowMs); 
 + 
 +  bool ok = mqttClient.publish(topic, payload, true); 
 + 
 +  if (ok) { 
 +    Serial.print("MQTT gesendet an "); 
 +    Serial.print(topic); 
 +    Serial.print(" mit Payload: "); 
 +    Serial.println(payload); 
 +  } else { 
 +    Serial.print("MQTT publish fehlgeschlagen für "); 
 +    Serial.println(topic); 
 +  } 
 + 
 +  mqttClient.loop(); 
 +  return ok;
 } }
  
- // Set pin 4 as an interrupt\\ +void updateLeds() { 
-pinMode(4, INPUT_PULLUP);\\ +  unsigned long now = millis(); 
-attachInterrupt(digitalPinToInterrupt(4), triggerbell, FALLING);\\+ 
 +  if (ledMode != LED_MODE_STANDBY && (long)(now - ledModeUntilMs) >= 0) { 
 +    ledMode = LED_MODE_STANDBY; 
 +  } 
 + 
 +  // ===== Langsames sinusförmiges Fade im Standby ===== 
 +  if (ledMode == LED_MODE_STANDBY) { 
 +    if (now - lastLedUpdateMs >= 25) { 
 +      lastLedUpdateMs = now; 
 + 
 +      static float phase = 0.0f; 
 + 
 +      // kleiner = langsamer 
 +      phase += 0.02f; 
 + 
 +      if (phase > 2.0f * PI) { 
 +        phase -= 2.0f * PI; 
 +      } 
 + 
 +      int brightness = (int)((sin(phase) * 0.5f + 0.5f) * 255.0f); 
 + 
 +      ledcWrite(ledTuerPin, brightness); 
 +      ledcWrite(ledTorPin, brightness); 
 +    } 
 +    return; 
 +  } 
 + 
 +  // ===== Event-Blinken ===== 
 +  if (now - lastBlinkToggleMs >= 100) { 
 +    lastBlinkToggleMs = now; 
 +    ledBlinkState = !ledBlinkState; 
 +  } 
 + 
 +  if (ledMode == LED_MODE_SHOW_TUER) { 
 +    ledcWrite(ledTuerPin, ledBlinkState ? 255 : 0); 
 +    ledcWrite(ledTorPin, 255); 
 +    return; 
 +  } 
 + 
 +  if (ledMode == LED_MODE_SHOW_TOR) { 
 +    ledcWrite(ledTuerPin, 255); 
 +    ledcWrite(ledTorPin, ledBlinkState ? 255 : 0); 
 +    return; 
 +  }
 } }
  
-void triggerbell() {\\ +bool publishStatus() { 
-bell.trigger = 8562190430;+  if (!connectWiFi()) return false; 
 +  if (!ensureMqttConnected()) return false;
  
-esp_err_t result = esp_now_send(0, (uint8_t *) &bell, sizeof(bell_struct));+  String json = buildDebugJson(); 
 +  bool ok = mqttClient.publish(MQTT_TOPIC_STATUS, json.c_str(), true);
  
-if (result == ESP_OK) {\\ +  if (ok) { 
-  Serial.println("Sent with success");\\ +    Serial.println("MQTT Status (JSON) gesendet"); 
-} else {\\ +  } else { 
-  Serial.println("Error sending the data");\\+    Serial.println("MQTT Status publish fehlgeschlagen"); 
 +  } 
 + 
 +  mqttClient.loop(); 
 +  return ok;
 } }
  
-Serial.print("[NEW] ESP32 Board MAC Address:  ");\\ +// ===================================================== 
-Serial.println(WiFi.macAddress());+// Pending Flags 
 +// ===================================================== 
 +bool isMqttPendingTuer() { 
 +  preferences.begin("klingel", true); 
 +  bool pending = preferences.getBool("mqttTuer", false); 
 +  preferences.end(); 
 +  return pending; 
 +}
  
 +bool isMqttPendingTor() {
 +  preferences.begin("klingel", true);
 +  bool pending = preferences.getBool("mqttTor", false);
 +  preferences.end();
 +  return pending;
 } }
  
-void loop() {+void clearMqttPendingTuer() { 
 +  preferences.begin("klingel", false); 
 +  preferences.putBool("mqttTuer", false); 
 +  preferences.end(); 
 +}
  
 +void clearMqttPendingTor() {
 +  preferences.begin("klingel", false);
 +  preferences.putBool("mqttTor", false);
 +  preferences.end();
 } }
  
-Empfänger1+void markMqttPendingTuer() { 
 +  preferences.begin("klingel", false); 
 +  preferences.putBool("mqttTuer", true); 
 +  preferences.end(); 
 +}
  
-#include <WiFi.h>\\ +void markMqttPendingTor() { 
-#include <esp_wifi.h>\\ +  preferences.begin("klingel", false); 
-#include <esp_now.h>+  preferences.putBool("mqttTor", true); 
 +  preferences.end(); 
 +}
  
-// Set MAC Address this device (bell receiver 1)\\ +void processPendingMqtt() { 
-uint8_t newMACAddress[] = {0x30, 0xAF, 0xA1, 0x05, 0x1D, 0x63};+  if (isMqttPendingTuer()) { 
 +    if (publishUnixTimeToTopic(MQTT_TOPIC_TUER)) { 
 +      clearMqttPendingTuer(); 
 +    } 
 +  }
  
-uint8_t addr[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63};+  if (isMqttPendingTor()) { 
 +    if (publishUnixTimeToTopic(MQTT_TOPIC_TOR)) { 
 +      clearMqttPendingTor(); 
 +    } 
 +  } 
 +}
  
-unsigned long lastPrintoutTime = 0;\\ +// ===================================================== 
-bool isTriggered = false;\\ +// Web / Debug / OTA 
-unsigned long lastTriggeredTime = 0;+// ===================================================== 
 +String buildDebugJson() { 
 +  String json; 
 +  json.reserve(1700);
  
-typedef struct bell_struct {\\ +  json += "{"; 
-uint64_t trigger;\\ +  json += "\"wifi\":\""; 
-} bell_struct;+  json += (WiFi.status() == WL_CONNECTED ? "connected" : "disconnected"); 
 +  json += "\","; 
 +  json += "\"ip\":\""; 
 +  json += WiFi.localIP().toString(); 
 +  json += "\","; 
 +  json += "\"rssi\":"; 
 +  json += WiFi.RSSI(); 
 +  json += ","; 
 +  json += "\"uptime\":"; 
 +  json += millis() / 1000UL; 
 +  json += ","; 
 +  json += "\"free_heap\":"; 
 +  json += ESP.getFreeHeap(); 
 +  json += ","; 
 +  json += "\"state\":\""; 
 +  json += lastState; 
 +  json += "\","; 
 +  json += "\"source\":\""; 
 +  json += lastSource; 
 +  json += "\","; 
 +  json += "\"adcTorValue\":"; 
 +  json += adcTorValue; 
 +  json += ","; 
 +  json += "\"adcTorThreshold\":"; 
 +  json += adcTorThreshold; 
 +  json += ","; 
 +  json += "\"lastTorState\":"; 
 +  json += (lastTorState ? "true" : "false"); 
 +  json += ","; 
 +  json += "\"lastDecodedLen\":"; 
 +  json += lastDecodedLen; 
 +  json += ","; 
 +  json += "\"lastDecodedHex\":\""; 
 +  json += lastDecodedHex; 
 +  json += "\","; 
 +  json += "\"mqttPendingTuer\":"; 
 +  json += (isMqttPendingTuer() ? "true" : "false"); 
 +  json += ","; 
 +  json += "\"mqttPendingTor\":"; 
 +  json += (isMqttPendingTor() ? "true" : "false"); 
 +  json += ","; 
 +  json += "\"bellPulseActive\":"; 
 +  json += (bellPulseActive ? "true" : "false"); 
 +  json += ","; 
 +  json += "\"hostname\":\""; 
 +  json += OTA_HOSTNAME; 
 +  json += "\","; 
 +  json += "\"mac\":\""; 
 +  json += WiFi.macAddress(); 
 +  json += "\""; 
 +  json += "}";
  
-//Create a struct_message called myData\\ +  return json; 
-bell_struct myData;+}
  
-void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {\\ +void setupHttpRoutes() { 
-memcpy(&myData, incomingData, sizeof(myData));\\ +  httpServer.on("/", HTTP_GET, []() { 
-Serial.print("Bytes received: ");\\ +    String html; 
-Serial.println(len);\\ +    html.reserve(3200); 
-Serial.print("received command:");\\ + 
-Serial.println(myData.trigger);\\ +    html += "<!doctype html><html><head><meta charset='utf-8'>"; 
-if (memcmp(mac, addr, 6) == 0) { // Check if expected Mac Addr\\ +    html += "<meta name='viewport' content='width=device-width,initial-scale=1'>"; 
-  Serial.println("klingel device received!");\\ +    html += "<title>HSHB Klingel</title></head><body>"; 
-  if (myData.trigger == 8562190430) {\\ +    html += "<h1>HSHB Klingel</h1>"; 
-    Serial.println("valid Trigger code received!");\\ +    html += "<p><b>IP:</b> " + WiFi.localIP().toString() + "</p>"; 
-    triggerbell();\\ +    html += "<p><b>RSSI:</b> " + String(WiFi.RSSI()) + "</p>"; 
-  }\\ +    html += "<p><b>Uptime:</b> " + String(millis() / 1000UL) + " s</p>"; 
-}\\+    html += "<p><b>State:</b> " + lastState + "</p>"; 
 +    html += "<p><b>Source:</b> " + lastSource + "</p>"; 
 +    html += "<p><b>ADC Tor:</b> " + String(adcTorValue) + "</p>"; 
 +    html += "<p><b>ADC Threshold:</b> " + String(adcTorThreshold) + "</p>"; 
 +    html += "<p><b>Last Tor State:</b> " + String(lastTorState ? "true" : "false") + "</p>"; 
 +    html += "<p><b>Decoded Len:</b> " + String(lastDecodedLen) + "</p>"; 
 +    html += "<p><b>Decoded Hex:</b> " + lastDecodedHex + "</p>"; 
 +    html += "<p><b>MQTT pending Tür:</b> " + String(isMqttPendingTuer() ? "true" : "false") + "</p>"; 
 +    html += "<p><b>MQTT pending Tor:</b> " + String(isMqttPendingTor() ? "true" : "false") + "</p>"; 
 +    html += "<p><b>Bell Pulse:</b> " + String(bellPulseActive ? "true" : "false") + "</p>"; 
 +    html += "<p><b>MAC:</b> " + WiFi.macAddress() + "</p>"; 
 +    html += "<p><a href='/debug'>Debug JSON</a></p>"; 
 +    html += "<p><a href='/update'>Firmware Update</a></p>"; 
 +    html += "</body></html>"; 
 + 
 +    httpServer.send(200, "text/html", html); 
 +  }); 
 + 
 +  httpServer.on("/debug", HTTP_GET, []() { 
 +    httpServer.send(200, "application/json", buildDebugJson()); 
 +  }); 
 + 
 +  httpUpdater.setup(&httpServer, "/update", OTA_USER, OTA_PASS); 
 +  httpServer.begin();
 } }
  
-void setup() {+void setupArduinoOta() { 
 +  ArduinoOTA.setHostname(OTA_HOSTNAME); 
 +  ArduinoOTA.setPassword(OTA_PASS); 
 +  ArduinoOTA.begin(); 
 +}
  
-Serial.begin(115200);+// ===================================================== 
 +// MQTT Callback 
 +// ===================================================== 
 +void mqttCallback(char* topic, byte* payload, unsigned int length) { 
 +  String topicStr = String(topic); 
 +  String payloadStr;
  
-Serial.println();+  for (unsigned int i = 0; i < length; i++) { 
 +    payloadStr += (char)payload[i]; 
 +  }
  
-WiFi.mode(WIFI_STA);+  payloadStr.trim();
  
-Serial.print("[OLD] ESP32 Board MAC Address:  ");\\ +  Serial.print("MQTT empfangen | topic="); 
-Serial.println(WiFi.macAddress());+  Serial.print(topicStr); 
 +  Serial.print(" | payload="); 
 +  Serial.println(payloadStr);
  
-// ESP32 Board add-on before version < 1.0.5\\ +  if (payloadStr != "test") return;
-//esp_wifi_set_mac(ESP_IF_WIFI_STA, &newMACAddress[0]);+
  
-// ESP32 Board add-on after version > 1.0.5\\ +  if (topicStr == MQTT_TOPIC_TUER_TEST) { 
-esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);+    Serial.println("MQTT TEST -> TUER"); 
 +    triggerBell(true); 
 +  } else if (topicStr == MQTT_TOPIC_TOR_TEST) { 
 +    Serial.println("MQTT TEST -> TOR"); 
 +    triggerTorEvent("mqtt-test"); 
 +  } 
 +}
  
-Serial.print("[NEW] ESP32 Board MAC Address:  ");\\ +// ===================================================== 
-Serial.println(WiFi.macAddress());+// Klingeltrigger 
 +// ===================================================== 
 +void triggerBell(bool fromButton) { 
 +  unsigned long now = millis(); 
 +  if (now - lastBellTriggered < BellDebounceMs) return;
  
-pinMode(LED_BUILTIN, OUTPUT);\\ +  Serial.println("Bell Triggered!");
-pinMode(4, OUTPUT);+
  
-digitalWrite(LED_BUILTIN, LOW);\\ +  ledMode = LED_MODE_SHOW_TUER; 
-digitalWrite(4, HIGH);   // trigger bell\\ +  ledModeUntilMs = now + LedEventDurationMs;
-delay(100);\\ +
-digitalWrite(4, LOW);\\ +
-digitalWrite(LED_BUILTIN, HIGH);\\ +
-delay(100);+
  
-//Set device as a Wi-Fi Station\\ +  lastBellTriggered = now;
-WiFi.mode(WIFI_STA);+
  
-//Init ESP-NOW\\ +  digitalWrite(bellPin, HIGH); 
-if (esp_now_init() != ESP_OK) {\\ +  bellPulseStartMillis = now; 
-  Serial.println("Error initializing ESP-NOW");\\ +  bellPulseActive = true; 
-  ESP.restart();\\+ 
 +  lastSource = fromButton ? "button" : "bus"; 
 +  lastState  = "BELL"; 
 + 
 +  markMqttPendingTuer(); 
 +  processPendingMqtt(); 
 +  publishStatus();
 } }
  
-// Once ESPNow is successfully Init, we will register for recv CB to\\ +void triggerTorEvent(const char* source) { 
-// get recv packer info\\ +  unsigned long now = millis(); 
-esp_now_register_recv_cb(OnDataRecv);\\+ 
 +  if (now - lastTorTriggerMillis < TorDebounceMs) { 
 +    Serial.println("TOR blockiert: 11s Sperre aktiv"); 
 +    return; 
 +  } 
 + 
 +  lastTorTriggerMillis = now; 
 + 
 +  ledMode = LED_MODE_SHOW_TOR; 
 +  ledModeUntilMs = now + LedEventDurationMs; 
 +  lastState = "TOR"; 
 +  lastSource = source; 
 + 
 +  Serial.print("TOR TRIGGER! source="); 
 +  Serial.println(source); 
 + 
 +  markMqttPendingTor(); 
 +  processPendingMqtt(); 
 +  publishStatus();
 } }
  
-\\ +// ===================================================== 
-void triggerbell() {\\ +// RMT Hilfsfunktionen 
-if (!isTriggered || (millis() - lastTriggeredTime > 10000)) {\\ +// ===================================================== 
-  isTriggered = true;\\ +int pulseType(uint16_t us) { 
-  lastTriggeredTime = millis();+  if (us >= 32  && us <= 52)  return P_ONE; 
 +  if (us >= 103 && us <= 123) return P_ZERO; 
 +  if (us >= 223 && us <= 243) return P_START; 
 +  if (us >= 76  && us <= 96)  return P_STOP; 
 +  return P_BAD; 
 +} 
 + 
 +size_t collectLows(const rmt_data_t *symbols, size_t symbolCount, uint16_t *lows) { 
 +  size_t n = 0; 
 + 
 +  for (size_t i = 0; i < symbolCount && n < maxSyms; ++i) { 
 +    if (symbols[i].level0 && !symbols[i].level1 && 
 +        symbols[i].duration0 >= 17 && symbols[i].duration0 <= 33 && 
 +        symbols[i].duration1) { 
 +      lows[n++] = symbols[i].duration1; 
 +    } else if (!symbols[i].level0 && symbols[i].level1 && 
 +               symbols[i].duration1 >= 17 && symbols[i].duration1 <= 33 && 
 +               symbols[i].duration0) { 
 +      lows[n++] = symbols[i].duration0; 
 +    } else if (!symbols[i].level0 && pulseType(symbols[i].duration0) != P_BAD) { 
 +      lows[n++] = symbols[i].duration0; 
 +    } else if (!symbols[i].level1 && pulseType(symbols[i].duration1) != P_BAD) { 
 +      lows[n++] = symbols[i].duration1; 
 +    } 
 +  }
  
-  digitalWrite(LED_BUILTIN, LOW);\\ +  return n;
-  digitalWrite(4, HIGH);   // trigger bell\\ +
-  delay(100);\\ +
-  digitalWrite(4, LOW);\\ +
-  digitalWrite(LED_BUILTIN, HIGH);\\ +
-  Serial.println("bell triggered");\\ +
-}\\+
 } }
  
-void loop() {\\ +int decodeByte(const uint16_t *pulses) { 
-// Print out the `isTriggered` variable every second\\ +  if (pulseType(pulses[0]) != P_START) return -1; 
-if (millis() - lastPrintoutTime > 1000) {\\ + 
-  Serial.print("isTriggered: ");\\ +  uint8_t value = 0; 
-  Serial.println(isTriggered);\\ +  for (int i = 0; i < 8; ++i) { 
-  lastPrintoutTime = millis();\\ +    int bit = pulseType(pulses[i + 1]); 
-}\\ +    if (bit < 0 || bit > 1) return -1; 
-\\ +    value |= bit << i; 
-// Reset the `isTriggered` variable after 5 seconds\\ +  } 
-if (millis() - lastTriggeredTime > 5000) {\\ +  return value;
-  isTriggered = false;\\ +
-}\\ +
-\\+
 } }
  
-Empfänger2+bool decodeMessage(const rmt_data_t *symbols, size_t symbolCount, uint8_t *msg, size_t &len) { 
 +  uint16_t lows[maxSyms]; 
 +  size_t lowCount = collectLows(symbols, symbolCount, lows);
  
-#include <WiFi.h>\\ +  len = 0; 
-#include <esp_wifi.h>\\ +  for (size_t i = 0; i + 9 < lowCount && len < bufferSize;) { 
-#include <esp_now.h>+    if (pulseType(lows[i]) != P_START) { 
 +      ++i; 
 +      continue; 
 +    }
  
-// Set MAC Address this device (bell receiver 2)\\ +    int value = decodeByte(lows + i); 
-uint8_t newMACAddress[] = {0x30, 0xAF, 0xA2, 0x05, 0x1D, 0x63};+    if (value < 0) { 
 +      ++i; 
 +      continue; 
 +    }
  
-uint8_t addr[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63};+    msg[len++] = value; 
 +    i += 10;
  
-unsigned long lastPrintoutTime = 0;\\ +    if (i < lowCount && lows[i] >= 76) { 
-bool isTriggered = false;\\ +      ++i; 
-unsigned long lastTriggeredTime = 0;+    } 
 +  }
  
-typedef struct bell_struct {\\ +  return len > 0; 
-uint64_t trigger;\\ +}
-} bell_struct;+
  
-//Create a struct_message called myData\\ +bool messageContains(const uint8_t *msg, size_t len, const uint8_t *what, size_t whatLen) { 
-bell_struct myData;+  if (whatLen > len) return false;
  
-void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {\\ +  for (size_t i = 0; i + whatLen <= len; ++i) { 
-memcpy(&myData, incomingData, sizeof(myData));\\ +    if (!memcmp(msg + i, what, whatLen)) return true; 
-Serial.print("Bytes received: ");\\ +  } 
-Serial.println(len);\\ +  return false;
-Serial.print("received command:");\\ +
-Serial.println(myData.trigger);\\ +
-if (memcmp(mac, addr, 6) == 0) { // Check if expected Mac Addr\\ +
-  Serial.println("klingel device received!");\\ +
-  if (myData.trigger == 8562190430) {\\ +
-    Serial.println("valid Trigger code received!");\\ +
-    triggerbell();\\ +
-  }\\ +
-}\\+
 } }
  
 +// =====================================================
 +// Setup
 +// =====================================================
 void setup() { void setup() {
 +  Serial.begin(115200);
 +  delay(500);
  
-Serial.begin(115200);+  ledcAttach(ledTuerPin, ledFreq, ledResolution); 
 +  ledcAttach(ledTorPin, ledFreq, ledResolution);
  
-Serial.println();+  ledcWrite(ledTuerPin, 0); 
 +  ledcWrite(ledTorPin, 0);
  
-WiFi.mode(WIFI_STA);+  pinMode(bellPin, OUTPUT); 
 +  digitalWrite(bellPin, LOW);
  
-Serial.print("[OLD] ESP32 Board MAC Address:  ");\\ +  pinMode(testBellPin, INPUT_PULLUP); 
-Serial.println(WiFi.macAddress());+  pinMode(adcTorPin, INPUT);
  
-// ESP32 Board add-on before version < 1.0.5\\ +  analogReadResolution(12); 
-//esp_wifi_set_mac(ESP_IF_WIFI_STA, &newMACAddress[0]);+  analogSetPinAttenuation(adcTorPin, ADC_11db);
  
-// ESP32 Board add-on after version > 1.0.5\\ +  WiFi.mode(WIFI_STA); 
-esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);+  esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);
  
-Serial.print("[NEW] ESP32 Board MAC Address:  ");\\ +  mqttClient.setServer(MQTT_HOST, MQTT_PORT); 
-Serial.println(WiFi.macAddress());+  mqttClient.setCallback(mqttCallback); 
 +  mqttClient.setBufferSize(2048); 
 +  mqttClient.setKeepAlive(30); 
 +  mqttClient.setSocketTimeout(2);
  
-pinMode(LED_BUILTIN, OUTPUT);\\ +  connectWiFi(); 
-pinMode(4, OUTPUT);+  ensureTimeValid(); 
 +  ensureMqttConnected();
  
-digitalWrite(LED_BUILTIN, LOW);\\ +  if (MDNS.begin(OTA_HOSTNAME)) { 
-digitalWrite(4, HIGH);   // trigger bell\\ +    MDNS.addService("http", "tcp", 80); 
-delay(100);\\ +  }
-digitalWrite(4, LOW);\\ +
-digitalWrite(LED_BUILTIN, HIGH);\\ +
-delay(100);+
  
-//Set device as a Wi-Fi Station\\ +  setupHttpRoutes(); 
-WiFi.mode(WIFI_STA);+  setupArduinoOta();
  
-//Init ESP-NOW\\ +  rmtInit(busPin, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_2, 1000000); 
-if (esp_now_init() != ESP_OK) {\\ +  rmtSetRxMinThreshold(busPin, 3); 
-  Serial.println("Error initializing ESP-NOW");\\ +  rmtSetRxMaxThreshold(busPin, 1000); 
-  ESP.restart();\\ +  rmtReadAsync(busPin, symbols, &symbolCount);
-}+
  
-// Once ESPNow is successfully Init, we will register for recv CB to\\ +  publishStatus();
-// get recv packer info\\ +
-esp_now_register_recv_cb(OnDataRecv);\\+
 } }
  
-\\ +// ===================================================== 
-void triggerbell() {\\ +// Loop 
-if (!isTriggered || (millis() - lastTriggeredTime > 10000)) {\\ +// ===================================================== 
-  isTriggered = true;\\ +void loop() { 
-  lastTriggeredTime = millis();+  unsigned long now = millis();
  
-  digitalWrite(LED_BUILTIN, LOW);\\ +  updateLeds(); 
-  digitalWrite(4, HIGH);   // trigger bell\\ +  connectWiFi(); 
-  delay(100);\\ +  ensureMqttConnected(); 
-  digitalWrite(4, LOW);\\ +  mqttClient.loop();
-  digitalWrite(LED_BUILTIN, HIGH);\\ +
-  Serial.println("bell triggered");\\ +
-}\\ +
-}+
  
-void loop() {\\ +  httpServer.handleClient(); 
-// Print out the `isTriggered` variable every second\\ +  ArduinoOTA.handle(); 
-if (millis() - lastPrintoutTime > 1000) {\\ + 
-  Serial.print("isTriggered: ");\\ +  adcTorValue = analogRead(adcTorPin); 
-  Serial.println(isTriggered);\\ + 
-  lastPrintoutTime = millis();\\ +  bool currentTorState = (adcTorValue > adcTorThreshold); 
-}\\ + 
-\\ +  // nur steigende Flanke 
-// Reset the `isTriggered` variable after 5 seconds\\ +  if (!lastTorState && currentTorState) { 
-if (millis() - lastTriggeredTime > 5000) {\\ +    Serial.print("TOR Flanke erkannt, ADC="); 
-  isTriggered = false;\\ +    Serial.println(adcTorValue); 
-}\\ +    triggerTorEvent("adc"); 
-\\+  } 
 + 
 +  lastTorState = currentTorState; 
 + 
 +  bool currentButtonState = digitalRead(testBellPin); 
 +  if (lastButtonState == HIGH && currentButtonState == LOW && 
 +      (now - lastButtonTriggerMillis > ButtonDebounceMs)) { 
 +    lastButtonTriggerMillis = now; 
 +    triggerBell(true); 
 +  } 
 +  lastButtonState = currentButtonState; 
 + 
 +  if (rmtReceiveCompleted(busPin)) { 
 +    uint8_t msg[bufferSize]; 
 +    size_t len = 0; 
 + 
 +    decodeMessage(symbols, symbolCount, msg, len); 
 + 
 +    lastDecodedLen = len; 
 +    lastDecodedHex = ""; 
 +    for (size_t i = 0; i < len; ++i) { 
 +      char tmp[4]; 
 +      snprintf(tmp, sizeof(tmp), "%02X", msg[i]); 
 +      if (i) lastDecodedHex += " "; 
 +      lastDecodedHex += tmp; 
 +    } 
 +    if (lastDecodedHex.length() == 0) lastDecodedHex = "-"; 
 + 
 +    symbolCount = maxSyms; 
 +    rmtReadAsync(busPin, symbols, &symbolCount); 
 + 
 +    if (messageContains(msg, len, hackerspaceBell, sizeof(hackerspaceBell)) || 
 +        messageContains(msg, len, hackerspaceBell2, sizeof(hackerspaceBell2))) { 
 +      triggerBell(false); 
 +    } 
 +  } 
 + 
 +  if (bellPulseActive && (now - bellPulseStartMillis > BellPulseMs)) { 
 +    digitalWrite(bellPin, LOW); 
 +    bellPulseActive = false; 
 +    if (lastState == "BELL") { 
 +      lastState = "IDLE"; 
 +    } 
 +  } 
 + 
 +  if (now - lastStatusMillis > StatusMs) { 
 +    lastStatusMillis = now; 
 +    publishStatus(); 
 +  } 
 + 
 +  if (now - lastDebugMillis > Debuginterval) { 
 +    lastDebugMillis = now; 
 +    Serial.print("state="); 
 +    Serial.print(lastState); 
 +    Serial.print(" source="); 
 +    Serial.print(lastSource); 
 +    Serial.print(" adc="); 
 +    Serial.print(adcTorValue); 
 +    Serial.print(" torState="); 
 +    Serial.println(currentTorState ? "1" : "0"); 
 +  } 
 + 
 +  delay(1);
 } }
 +</code>
 +
 +
  
  
sonstiges/tuerklingel.1696017510.txt.gz · Zuletzt geändert: von schuballaa