sonstiges:tuerklingel
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| sonstiges:tuerklingel [2023-09-29 21:58] – [Empfänger mit Netzteil versorgen] schuballaa | sonstiges:tuerklingel [2026-05-07 02:21] (aktuell) – schuballaa | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | ====== | + | ====== |
| + | < | ||
| - | ===== Ü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. | + | {{: |
| - | Es wurde eine Möglichkeit gefunden, das Klingelsignal abzugreifen und in die Räume Bornstr. 16-17 zu verlängern (E- und Kreativwerkstatt). | + | [[https:// |
| - | **Optional:** | + | {{:sonstiges: |
| - | 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!** | + | |
| + | * **Tor:** Erkennung über **LDR (Lichtsensor)** am Empfänger der Torklingel | ||
| - | Da sich die meisten Powerbanks bei geringer Last ausschalten, | + | ===== 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:// | ||
| + | * 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:// | + | * Warten auf ein Klingelereignis |
| + | * LEDs werden | ||
| - | Das Haustelefon ist vom Typ STR HT 3033. | ||
| - | Das Steuergerät ist ein SP 333 ({{: | + | ===== Umgesetzte Extras |
| - | ===== Pinout | + | |
| - | Öffnet man das Haustelefon über die einzig vorhandene Gehäuseschraube | + | * MQTT-Integration |
| + | * Akustische und visuelle Anzeige im Hauptraum | ||
| + | * Funkverteilung in die anderen Räume | ||
| + | * LED-Visualisierung | ||
| - | Die Belegung lässt sich durch ein Foto einer Relaisbox {{:sonstiges: | + | ===== Danksagungen: ===== |
| - | {{: | + | * **Marco** – Tips bei der Entwicklung der Hardware |
| - | {{: | + | * **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. | + | {{: |
| + | ==== 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:// | + | **Signalcharakteristik: |
| + | * Ruhespannung: | ||
| + | * Aktivsignal: ca. ~18 V | ||
| + | * Information in **zeitlichen Pulsfolgen** | ||
| - | Wenn geklingelt wird, zieht ein n channel mosfet den Pin 4 des [[https:// | + | **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:// | + | 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/ | ||
| + | * Zeitliche Analyse des Blinkmusters | ||
| + | * Entprellung zur Vermeidung von Fehltriggern | ||
| - | #include < | + | **Ergebnis: |
| - | #include < | + | * Zuordnung des Signals zu einem **Torklingel-Ereignis** |
| - | #include < | + | |
| - | // Set your new MAC Address\\ | + | ---- |
| + | |||
| + | ==== Ereignisverarbeitung ==== | ||
| + | |||
| + | Nach der Erkennung erfolgt die weitere Verarbeitung: | ||
| + | |||
| + | * Klassifikation: | ||
| + | * 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** | ||
| + | {{: | ||
| + | {{: | ||
| + | {{: | ||
| + | {{: | ||
| + | |||
| + | ==== Code ==== | ||
| + | |||
| + | < | ||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include " | ||
| + | #include < | ||
| + | |||
| + | // ===================================================== | ||
| + | // WLAN / MQTT | ||
| + | // ===================================================== | ||
| + | const char* WIFI_SSID | ||
| + | const char* WIFI_PASSWORD = " | ||
| + | |||
| + | const char* MQTT_HOST = " | ||
| + | const int | ||
| + | const char* MQTT_USER = " | ||
| + | const char* MQTT_PASS = " | ||
| + | |||
| + | const char* MQTT_TOPIC_TUER | ||
| + | const char* MQTT_TOPIC_TOR | ||
| + | const char* MQTT_TOPIC_STATUS | ||
| + | const char* MQTT_TOPIC_TUER_TEST = "/ | ||
| + | const char* MQTT_TOPIC_TOR_TEST | ||
| + | |||
| + | // ===================================================== | ||
| + | // OTA / Web | ||
| + | // ===================================================== | ||
| + | const char* OTA_HOSTNAME = " | ||
| + | const char* OTA_USER | ||
| + | const char* OTA_PASS | ||
| + | |||
| + | // ===================================================== | ||
| + | // NTP | ||
| + | // ===================================================== | ||
| + | const char* NTP_SERVER = " | ||
| + | const long GMT_OFFSET_SEC = 3600; | ||
| + | const int | ||
| + | |||
| + | // ===================================================== | ||
| + | // MAC | ||
| + | // ===================================================== | ||
| uint8_t newMACAddress[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63}; | uint8_t newMACAddress[] = {0x30, 0xAF, 0xA0, 0x05, 0x1D, 0x63}; | ||
| - | // REPLACE WITH YOUR ESP RECEIVER' | + | // ===================================================== |
| - | uint8_t | + | // Pins |
| - | uint8_t | + | // ===================================================== |
| + | constexpr int bellPin | ||
| + | constexpr int busPin | ||
| + | const int adcTorPin | ||
| + | const int testBellPin | ||
| + | |||
| + | // ===================================================== | ||
| + | // RMT / Nachrichten | ||
| + | // ===================================================== | ||
| + | constexpr | ||
| + | constexpr | ||
| + | |||
| + | 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 | ||
| + | unsigned long lastTorTriggerMillis | ||
| + | unsigned long lastButtonTriggerMillis = 0; | ||
| + | unsigned long lastDebugMillis | ||
| + | unsigned long lastStatusMillis | ||
| + | unsigned long bellPulseStartMillis | ||
| + | unsigned long lastWifiReconnectMs | ||
| + | unsigned long lastMqttReconnectMs | ||
| + | unsigned long lastNtpSyncMs | ||
| + | |||
| + | const unsigned long BellDebounceMs | ||
| + | const unsigned long TorDebounceMs | ||
| + | const unsigned long ButtonDebounceMs | ||
| + | const unsigned long Debuginterval | ||
| + | const unsigned long StatusMs | ||
| + | const unsigned long BellPulseMs | ||
| + | const unsigned long WifiRetryMs | ||
| + | const unsigned long MqttRetryMs | ||
| + | const unsigned long NtpResyncMs | ||
| + | const unsigned long LedEventDurationMs = 60000; | ||
| + | |||
| + | const int adcTorThreshold = 1500; | ||
| + | |||
| + | bool bellPulseActive = false; | ||
| + | bool timeValid = false; | ||
| + | bool lastButtonState = HIGH; | ||
| + | bool lastTorState = false; | ||
| + | |||
| + | String lastState = " | ||
| + | String lastSource = " | ||
| + | String lastDecodedHex = " | ||
| + | uint32_t lastDecodedLen = 0; | ||
| + | int adcTorValue = 0; | ||
| + | |||
| + | // ========================= | ||
| + | // LEDs | ||
| + | // ========================= | ||
| + | const int ledTuerPin = 23; | ||
| + | const int ledTorPin | ||
| + | |||
| + | 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, | ||
| + | int decodeByte(const uint16_t *pulses); | ||
| + | bool decodeMessage(const rmt_data_t *symbols, size_t symbolCount, | ||
| + | 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(" |
| - | uint64_t trigger;\\ | + | |
| - | } bell_struct; | + | |
| - | 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(" | ||
| + | Serial.print(" | ||
| + | Serial.println(WiFi.localIP()); | ||
| + | return true; | ||
| + | } | ||
| - | // callback when data is sent\\ | + | |
| - | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {\\ | + | |
| - | char macStr[18]; | + | |
| - | Serial.print("Packet to: ");\\ | + | |
| - | // Copies the sender mac address to a string\\ | + | |
| - | snprintf(macStr, | + | |
| - | | + | |
| - | Serial.print(macStr); | + | |
| - | Serial.print(" | + | |
| - | Serial.println(status == ESP_NOW_SEND_SUCCESS ? " | + | |
| } | } | ||
| - | void setup() {\\ | + | bool syncTime() { |
| - | Serial.begin(115200); | + | if (WiFi.status() != WL_CONNECTED) return false; |
| - | WiFi.mode(WIFI_STA); | + | Serial.println(" |
| + | configTime(GMT_OFFSET_SEC, | ||
| - | // Set the ESP32 Board MAC Address.\\ | + | time_t now = time(nullptr); |
| - | esp_wifi_set_mac(WIFI_IF_STA, | + | int retry = 0; |
| - | // Print the new MAC Address.\\ | + | while (now < 100000 && retry < 20) { |
| - | Serial.print(" | + | delay(500); |
| - | Serial.println(WiFi.macAddress()); | + | Serial.print(" |
| + | now = time(nullptr); | ||
| + | retry++; | ||
| + | } | ||
| + | | ||
| - | if (esp_now_init() != ESP_OK) {\\ | + | |
| - | Serial.println("Error initializing ESP-NOW");\\ | + | Serial.println("NTP Zeit konnte nicht geholt werden" |
| - | | + | timeValid |
| + | return false; | ||
| + | } | ||
| + | |||
| + | Serial.print("Aktuelle Unix-Zeit: "); | ||
| + | | ||
| + | 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;\\ | + | |
| - | peerInfo.encrypt | + | |
| - | // register first peer\\ | + | if (mqttClient.connected()) return true; |
| - | memcpy(peerInfo.peer_addr, broadcastAddress1, 6);\\ | + | if (WiFi.status() != WL_CONNECTED) return |
| - | if (esp_now_add_peer(& | + | |
| - | Serial.println(" | + | |
| - | return;\\ | + | if (now - lastMqttReconnectMs < MqttRetryMs) return false; |
| + | lastMqttReconnectMs = now; | ||
| + | |||
| + | String clientId = " | ||
| + | clientId += String((uint32_t)ESP.getEfuseMac(), HEX); | ||
| + | |||
| + | Serial.println(" | ||
| + | | ||
| + | Serial.println(" | ||
| + | |||
| + | mqttClient.subscribe(MQTT_TOPIC_TUER_TEST); | ||
| + | mqttClient.subscribe(MQTT_TOPIC_TOR_TEST); | ||
| + | Serial.println(" | ||
| + | |||
| + | return true; | ||
| + | } | ||
| + | |||
| + | Serial.print(" | ||
| + | Serial.println(mqttClient.state()); | ||
| + | return | ||
| } | } | ||
| - | // register second peer\\ | + | bool publishUnixTimeToTopic(const char* topic) { |
| - | memcpy(peerInfo.peer_addr, | + | if (!connectWiFi()) return false; |
| - | if (esp_now_add_peer(& | + | if (!ensureTimeValid()) return false; |
| - | Serial.println(" | + | if (!ensureMqttConnected()) return false; |
| - | return;\\ | + | |
| + | time_t now = time(nullptr); | ||
| + | | ||
| + | |||
| + | char payload[32]; | ||
| + | snprintf(payload, | ||
| + | |||
| + | bool ok = mqttClient.publish(topic, | ||
| + | |||
| + | if (ok) { | ||
| + | Serial.print(" | ||
| + | Serial.print(topic); | ||
| + | Serial.print(" | ||
| + | | ||
| + | } else { | ||
| + | Serial.print("MQTT publish fehlgeschlagen für "); | ||
| + | Serial.println(topic); | ||
| + | } | ||
| + | |||
| + | mqttClient.loop(); | ||
| + | return | ||
| } | } | ||
| - | // | + | 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; | ||
| + | } | ||
| + | |||
| + | // | ||
| + | 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); | ||
| + | | ||
| + | } | ||
| + | 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, | ||
| + | ledcWrite(ledTorPin, | ||
| + | return; | ||
| + | } | ||
| } | } | ||
| - | void triggerbell() {\\ | + | bool publishStatus() { |
| - | bell.trigger = 8562190430; | + | if (!connectWiFi()) return false; |
| + | if (!ensureMqttConnected()) return false; | ||
| - | esp_err_t result | + | String json = buildDebugJson(); |
| + | bool ok = mqttClient.publish(MQTT_TOPIC_STATUS, json.c_str(), true); | ||
| - | if (result == ESP_OK) {\\ | + | |
| - | Serial.println(" | + | Serial.println(" |
| - | } else {\\ | + | } else { |
| - | Serial.println(" | + | Serial.println(" |
| + | } | ||
| + | |||
| + | 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(" | ||
| + | preferences.end(); | ||
| + | return pending; | ||
| + | } | ||
| + | bool isMqttPendingTor() { | ||
| + | preferences.begin(" | ||
| + | bool pending = preferences.getBool(" | ||
| + | preferences.end(); | ||
| + | return pending; | ||
| } | } | ||
| - | void loop() { | + | void clearMqttPendingTuer() { |
| + | preferences.begin(" | ||
| + | preferences.putBool(" | ||
| + | preferences.end(); | ||
| + | } | ||
| + | void clearMqttPendingTor() { | ||
| + | preferences.begin(" | ||
| + | preferences.putBool(" | ||
| + | preferences.end(); | ||
| } | } | ||
| - | Empfänger1 | + | void markMqttPendingTuer() { |
| + | preferences.begin(" | ||
| + | preferences.putBool(" | ||
| + | preferences.end(); | ||
| + | } | ||
| - | #include <WiFi.h>\\ | + | void markMqttPendingTor() { |
| - | #include < | + | preferences.begin(" |
| - | #include <esp_now.h> | + | |
| + | | ||
| + | } | ||
| - | // Set MAC Address this device | + | 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 | + | // ===================================================== |
| - | bool isTriggered | + | // Web / Debug / OTA |
| - | unsigned long lastTriggeredTime = 0; | + | // ===================================================== |
| + | String buildDebugJson() { | ||
| + | String json; | ||
| + | | ||
| - | typedef struct bell_struct | + | json += "{"; |
| - | uint64_t trigger;\\ | + | json += "\"wifi\": |
| - | } bell_struct; | + | json += (WiFi.status() == WL_CONNECTED ? " |
| + | json += "\","; | ||
| + | json += "\" | ||
| + | json += WiFi.localIP().toString(); | ||
| + | json += " | ||
| + | json += " | ||
| + | json += WiFi.RSSI(); | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += millis() / 1000UL; | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += ESP.getFreeHeap(); | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += lastState; | ||
| + | json += " | ||
| + | json += " | ||
| + | json += lastSource; | ||
| + | json += " | ||
| + | json += " | ||
| + | json += adcTorValue; | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += adcTorThreshold; | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += (lastTorState ? " | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += lastDecodedLen; | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += lastDecodedHex; | ||
| + | json += " | ||
| + | json += " | ||
| + | json += (isMqttPendingTuer() ? " | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += (isMqttPendingTor() ? " | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += (bellPulseActive ? " | ||
| + | json += ","; | ||
| + | json += " | ||
| + | json += OTA_HOSTNAME; | ||
| + | json += " | ||
| + | json += " | ||
| + | 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, | + | void setupHttpRoutes() { |
| - | memcpy(&myData, incomingData, sizeof(myData));\\ | + | |
| - | Serial.print("Bytes received: ");\\ | + | String html; |
| - | Serial.println(len);\\ | + | html.reserve(3200); |
| - | Serial.print("received command:");\\ | + | |
| - | Serial.println(myData.trigger);\\ | + | html += "< |
| - | if (memcmp(mac, addr, 6) == 0) { // Check if expected Mac Addr\\ | + | html += "< |
| - | | + | html += "< |
| - | | + | html += "< |
| - | | + | html += "< |
| - | | + | html += "< |
| - | }\\ | + | html += "< |
| - | }\\ | + | html += "< |
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "< | ||
| + | | ||
| + | html += "</ | ||
| + | |||
| + | httpServer.send(200, "text/html", html); | ||
| + | }); | ||
| + | |||
| + | httpServer.on("/ | ||
| + | | ||
| + | }); | ||
| + | |||
| + | httpUpdater.setup(& | ||
| + | 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(" | + | |
| - | Serial.println(WiFi.macAddress()); | + | Serial.print(topicStr); |
| + | Serial.print(" | payload=" | ||
| + | Serial.println(payloadStr); | ||
| - | // ESP32 Board add-on before version < 1.0.5\\ | + | if (payloadStr != " |
| - | // | + | |
| - | // ESP32 Board add-on after version | + | if (topicStr == MQTT_TOPIC_TUER_TEST) { |
| - | esp_wifi_set_mac(WIFI_IF_STA, | + | Serial.println(" |
| + | triggerBell(true); | ||
| + | } else if (topicStr == MQTT_TOPIC_TOR_TEST) { | ||
| + | Serial.println(" | ||
| + | | ||
| + | } | ||
| + | } | ||
| - | Serial.print("[NEW] ESP32 Board MAC Address: | + | // ===================================================== |
| - | Serial.println(WiFi.macAddress()); | + | // Klingeltrigger |
| + | // ===================================================== | ||
| + | void triggerBell(bool fromButton) { | ||
| + | unsigned long now = millis(); | ||
| + | | ||
| - | pinMode(LED_BUILTIN, | + | Serial.println("Bell Triggered!" |
| - | pinMode(4, OUTPUT); | + | |
| - | digitalWrite(LED_BUILTIN, | + | |
| - | digitalWrite(4, | + | |
| - | delay(100); | + | |
| - | digitalWrite(4, | + | |
| - | digitalWrite(LED_BUILTIN, | + | |
| - | 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 |
| - | | + | |
| - | | + | |
| + | lastSource = fromButton ? "button" | ||
| + | lastState | ||
| + | |||
| + | markMqttPendingTuer(); | ||
| + | processPendingMqtt(); | ||
| + | | ||
| } | } | ||
| - | // Once ESPNow is successfully Init, we will register for recv CB to\\ | + | void triggerTorEvent(const char* source) { |
| - | // get recv packer info\\ | + | |
| - | esp_now_register_recv_cb(OnDataRecv);\\ | + | |
| + | if (now - lastTorTriggerMillis < TorDebounceMs) { | ||
| + | Serial.println(" | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | lastTorTriggerMillis = now; | ||
| + | |||
| + | ledMode = LED_MODE_SHOW_TOR; | ||
| + | ledModeUntilMs = now + LedEventDurationMs; | ||
| + | lastState = " | ||
| + | lastSource = source; | ||
| + | |||
| + | Serial.print(" | ||
| + | Serial.println(source); | ||
| + | |||
| + | markMqttPendingTor(); | ||
| + | processPendingMqtt(); | ||
| + | | ||
| } | } | ||
| - | \\ | + | // ===================================================== |
| - | void triggerbell() {\\ | + | // RMT Hilfsfunktionen |
| - | if (!isTriggered || (millis() - lastTriggeredTime | + | // ===================================================== |
| - | | + | int pulseType(uint16_t us) { |
| - | | + | 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_BAD; | ||
| + | } | ||
| + | |||
| + | size_t collectLows(const rmt_data_t *symbols, size_t symbolCount, | ||
| + | | ||
| + | |||
| + | | ||
| + | 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 && | ||
| + | | ||
| + | | ||
| + | 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(4, | + | |
| - | delay(100); | + | |
| - | digitalWrite(4, | + | |
| - | digitalWrite(LED_BUILTIN, | + | |
| - | Serial.println(" | + | |
| - | }\\ | + | |
| } | } | ||
| - | 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) {\\ | + | |
| - | | + | |
| - | | + | |
| - | | + | int bit = pulseType(pulses[i + 1]); |
| - | }\\ | + | if (bit < 0 || bit > 1) return |
| - | \\ | + | value |= bit << i; |
| - | // Reset the `isTriggered` variable after 5 seconds\\ | + | } |
| - | if (millis() - lastTriggeredTime > 5000) {\\ | + | |
| - | | + | |
| - | }\\ | + | |
| - | \\ | + | |
| } | } | ||
| - | Empfänger2 | + | bool decodeMessage(const rmt_data_t *symbols, size_t symbolCount, |
| + | uint16_t lows[maxSyms]; | ||
| + | size_t lowCount = collectLows(symbols, | ||
| - | #include < | + | len = 0; |
| - | # | + | for (size_t i = 0; i + 9 < lowCount && len < bufferSize; |
| - | # | + | if (pulseType(lows[i]) != P_START) { |
| + | ++i; | ||
| + | continue; | ||
| + | } | ||
| - | // Set MAC Address this device | + | 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 | + | 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, | + | for (size_t i = 0; i + whatLen <= len; ++i) { |
| - | memcpy(& | + | if (!memcmp(msg + i, what, whatLen)) return true; |
| - | Serial.print(" | + | |
| - | Serial.println(len);\\ | + | |
| - | Serial.print(" | + | |
| - | Serial.println(myData.trigger); | + | |
| - | if (memcmp(mac, addr, 6) == 0) { // Check if expected Mac Addr\\ | + | |
| - | | + | |
| - | | + | |
| - | Serial.println(" | + | |
| - | triggerbell(); | + | |
| - | }\\ | + | |
| - | }\\ | + | |
| } | } | ||
| + | // ===================================================== | ||
| + | // Setup | ||
| + | // ===================================================== | ||
| void setup() { | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | delay(500); | ||
| - | Serial.begin(115200); | + | ledcAttach(ledTuerPin, ledFreq, ledResolution); |
| + | ledcAttach(ledTorPin, | ||
| - | Serial.println(); | + | ledcWrite(ledTuerPin, 0); |
| + | ledcWrite(ledTorPin, | ||
| - | WiFi.mode(WIFI_STA); | + | pinMode(bellPin, OUTPUT); |
| + | digitalWrite(bellPin, | ||
| - | Serial.print("[OLD] ESP32 Board MAC Address: | + | pinMode(testBellPin, |
| - | Serial.println(WiFi.macAddress()); | + | |
| - | // ESP32 Board add-on before version < 1.0.5\\ | + | analogReadResolution(12); |
| - | // | + | |
| - | // ESP32 Board add-on after version > 1.0.5\\ | + | WiFi.mode(WIFI_STA); |
| - | esp_wifi_set_mac(WIFI_IF_STA, | + | esp_wifi_set_mac(WIFI_IF_STA, |
| - | Serial.print("[NEW] ESP32 Board MAC Address: | + | mqttClient.setServer(MQTT_HOST, MQTT_PORT); |
| - | Serial.println(WiFi.macAddress()); | + | mqttClient.setCallback(mqttCallback); |
| + | | ||
| + | mqttClient.setKeepAlive(30); | ||
| + | mqttClient.setSocketTimeout(2); | ||
| - | pinMode(LED_BUILTIN, | + | connectWiFi(); |
| - | pinMode(4, OUTPUT); | + | |
| + | ensureMqttConnected(); | ||
| - | digitalWrite(LED_BUILTIN, | + | if (MDNS.begin(OTA_HOSTNAME)) { |
| - | digitalWrite(4, HIGH); // trigger bell\\ | + | |
| - | delay(100);\\ | + | } |
| - | digitalWrite(4, LOW);\\ | + | |
| - | digitalWrite(LED_BUILTIN, HIGH);\\ | + | |
| - | delay(100); | + | |
| - | //Set device as a Wi-Fi Station\\ | + | setupHttpRoutes(); |
| - | WiFi.mode(WIFI_STA); | + | |
| - | //Init ESP-NOW\\ | + | rmtInit(busPin, RMT_RX_MODE, |
| - | if (esp_now_init() != ESP_OK) {\\ | + | |
| - | | + | |
| - | | + | |
| - | } | + | |
| - | // 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() { |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | digitalWrite(LED_BUILTIN, | + | |
| - | Serial.println("bell triggered" | + | |
| - | }\\ | + | |
| - | } | + | |
| - | void loop() {\\ | + | httpServer.handleClient(); |
| - | // Print out the `isTriggered` variable every second\\ | + | |
| - | if (millis() - lastPrintoutTime > 1000) {\\ | + | |
| - | Serial.print(" | + | adcTorValue = analogRead(adcTorPin); |
| - | Serial.println(isTriggered);\\ | + | |
| - | | + | bool currentTorState = (adcTorValue > adcTorThreshold); |
| - | }\\ | + | |
| - | \\ | + | |
| - | // Reset the `isTriggered` variable after 5 seconds\\ | + | if (!lastTorState && currentTorState) { |
| - | if (millis() - lastTriggeredTime > 5000) {\\ | + | Serial.print(" |
| - | | + | Serial.println(adcTorValue); |
| - | }\\ | + | triggerTorEvent(" |
| - | \\ | + | } |
| + | |||
| + | lastTorState = currentTorState; | ||
| + | |||
| + | | ||
| + | if (lastButtonState == HIGH && currentButtonState == LOW && | ||
| + | (now - lastButtonTriggerMillis > ButtonDebounceMs)) { | ||
| + | lastButtonTriggerMillis = now; | ||
| + | triggerBell(true); | ||
| + | | ||
| + | | ||
| + | |||
| + | if (rmtReceiveCompleted(busPin)) { | ||
| + | uint8_t msg[bufferSize]; | ||
| + | size_t len = 0; | ||
| + | |||
| + | decodeMessage(symbols, | ||
| + | |||
| + | lastDecodedLen = len; | ||
| + | lastDecodedHex = ""; | ||
| + | for (size_t i = 0; i < len; ++i) { | ||
| + | char tmp[4]; | ||
| + | snprintf(tmp, | ||
| + | if (i) lastDecodedHex += " "; | ||
| + | lastDecodedHex += tmp; | ||
| + | } | ||
| + | if (lastDecodedHex.length() == 0) lastDecodedHex = "-"; | ||
| + | |||
| + | symbolCount = maxSyms; | ||
| + | rmtReadAsync(busPin, | ||
| + | |||
| + | if (messageContains(msg, | ||
| + | messageContains(msg, | ||
| + | triggerBell(false); | ||
| + | } | ||
| + | | ||
| + | |||
| + | if (bellPulseActive && (now - bellPulseStartMillis > BellPulseMs)) { | ||
| + | digitalWrite(bellPin, | ||
| + | bellPulseActive | ||
| + | if (lastState == " | ||
| + | lastState = " | ||
| + | | ||
| + | } | ||
| + | |||
| + | if (now - lastStatusMillis > StatusMs) { | ||
| + | lastStatusMillis = now; | ||
| + | publishStatus(); | ||
| + | } | ||
| + | |||
| + | if (now - lastDebugMillis > Debuginterval) { | ||
| + | lastDebugMillis = now; | ||
| + | Serial.print(" | ||
| + | Serial.print(lastState); | ||
| + | Serial.print(" | ||
| + | Serial.print(lastSource); | ||
| + | Serial.print(" | ||
| + | Serial.print(adcTorValue); | ||
| + | Serial.print(" | ||
| + | Serial.println(currentTorState ? " | ||
| + | } | ||
| + | |||
| + | delay(1); | ||
| } | } | ||
| + | </ | ||
| + | |||
| + | |||
sonstiges/tuerklingel.1696017510.txt.gz · Zuletzt geändert: von schuballaa
