// DoorBell_ESP32C3_Extension.ino // scans for BLE Door Bell and makes status available on webpage and via telnet (23) connection // only scans for BLE devices whose names have been pre-registered // >>>> Using ESP32 board support V3.0.7 (other versions may not work) and board Dfrobot Beetle ESP32-C3 // Needs SafeString and ESPAutoWiFiConfig libraries installed. // Needs Huge APP, 3M APP, 1M SPIFF Partition setting /* DoorBell_ESP32C3_Extension.ino by Matthew Ford, 2021/12/06 (c)2021 Forward Computing and Control Pty. Ltd. NSW, Australia www.forward.com.au This code may be freely used for both private and commerical use. Provide this copyright is maintained. */ // Needs Huge APP, 2M APP, 1M SPIFF Partition setting const char* DoorBell_host = "10.1.1.106"; // DoorBell_ESP32C3_Monitor_Server bridge #define DEBUG static Stream *debugPtr = NULL; #include "SafeString.h" #include "SafeStringReader.h" #include "millisDelay.h" #include #include #include "ESPAutoWiFiConfig.h" #include "PinFlasher.h" const int Mp3SpeakerPin = 5; // GPIO pin number on Dfrobot Beetle ESP32-C3 const int BuzzerTonePin = 6; // GPIO pin number on Dfrobot Beetle ESP32-C3 const bool highForBuzzerOn = true; const int LedPin = 10; // GPIO pin number for on Dfrobot Beetle ESP32-C3 BUILT-IN led const bool highForLedOn = false; const unsigned ledFlashRate = 500; // on/off 0.5sec const int eepromOffset = 0; // eeprom not otherwise used PinFlasher buzzer(-1, highForBuzzerOn); // -1 to disable to start with to prevent noise if debugging PinFlasher pinFlasher(-1, highForLedOn); // -1 to disable to start with to prevent noise if debugging static const int portNo = 23; // What port to listen on for connections. telnet static const unsigned long TELNET_CONNECTION_TIMEOUT_MS = 10000; // 0 => never time out, else close connection if no data received for this time (ms) millisDelay failedConnectRebootDelay; unsigned long FAILED_CONNECT_REBOOT_DELAY_MS = 1ul * 60 * 1000; // reboot in 5mins this leave webpage etc running most of the time millisDelay buzzerDelay; unsigned long BUZZER_DELAY_ms = 200; // toggle tone to intermittent millisDelay buzzerOnTimer; unsigned long BUZZER_ON_TIMER_ms = 3000; // on for 3sec volatile bool buzzerOn = false; millisDelay bellAlertDelay; unsigned long BELL_ALERT_DELAY_MS = 4 * 1000; // 4sec millisDelay bellFlasherDelay; unsigned long BELL_FLASHER_DELAY_MS = 4 * 1000; // 4sec const unsigned long MSG_LENGTH_ms = 10000; // length of msg millisDelay msgDelay; // use the MSG_DELAY to adjust time between repeats of msg when raining and window open unsigned long MSG_DELAY_ms = MSG_LENGTH_ms + 1000; // delay between repeats of msg 1sec millisDelay msgTriggerDelay; unsigned long MSG_TRIGGER_DELAY_ms = 100; // pulse P1 low for this long to trigger/retrigger msg bool ledFlashing = false; void showBellIndicator() { if (!ledFlashing) { if (debugPtr) { debugPtr->print("showBellIndicator "); debugPtr->println(); } pinFlasher.setOnOff(ledFlashRate); // start flashing } ledFlashing = true; bellFlasherDelay.start(BELL_FLASHER_DELAY_MS); // restart timer } // led flashing AND has not been restarted by telent connection // turn flash off void cancelBellFlasher() { if (bellFlasherDelay.justFinished()) { if (debugPtr) { debugPtr->print("Turn Led Flash off"); debugPtr->println(); } ledFlashing = false; pinFlasher.setOnOff(PIN_OFF); // turn off flasher } } void triggerBell() { if (bellAlertDelay.isRunning()) { return; } // else if (debugPtr) { debugPtr->print("TriggerBell"); debugPtr->println(); } bellAlertDelay.start(BELL_ALERT_DELAY_MS); buzzer.setOnOff(350); if (debugPtr) { debugPtr->println("Bell Pressed"); } } void updateBell() { if (bellAlertDelay.justFinished()) { if (debugPtr) { debugPtr->println("Bell turned off"); } buzzer.setOnOff(PIN_OFF); } } volatile bool msgRunning = false; // call this to trigger msg, can call this repeatedly void triggerMsg() { if (msgRunning) { return; } if (debugPtr) { debugPtr->print("TriggerMsg"); debugPtr->println(); } msgRunning = true; msgTriggerDelay.start(MSG_TRIGGER_DELAY_ms); digitalWrite(Mp3SpeakerPin, LOW); } // call this each every loop() to control msg void updateMsg() { if (!msgRunning) { return; } if (msgTriggerDelay.justFinished()) { digitalWrite(Mp3SpeakerPin, HIGH); msgDelay.start(MSG_DELAY_ms); return; } if (msgDelay.justFinished()) { if (debugPtr) { debugPtr->println("Msg finished"); } msgRunning = false; // can retrigger now } } void initMp3Speaker() { pinMode(Mp3SpeakerPin, OUTPUT); digitalWrite(Mp3SpeakerPin, HIGH); } WiFiClient doorBellClient; // for connection millisDelay doorBellReconnectDelay; const unsigned long RECONNECT_DELAY_ms = 5000; // 5sec millisDelay doorBellClientKeepAliveTimer; // to send keepalive msgs (empty lines) const unsigned long KEEP_ALIVE_TIMER_ms = 5000; // 5sec millisDelay connectionAliveDelay; unsigned long CONNECTION_ALIVE_ms = 30000; // close connection is no msgs for 30sec // returns true if connected else false bool connectClientToHost(WiFiClient &client, const char* host, const uint16_t port) { if (!client.connect(host, port)) { if (debugPtr) { debugPtr->print("Connection to "); debugPtr->print(host); debugPtr->print(":"); debugPtr->print(port); debugPtr->println(" failed."); } if (!failedConnectRebootDelay.isRunning()) { failedConnectRebootDelay.start(FAILED_CONNECT_REBOOT_DELAY_MS); // restart in 1min if not connection } return false; } if (debugPtr) { debugPtr->print("Connected to "); debugPtr->print(host); debugPtr->print(":"); debugPtr->print(port); debugPtr->println(); } failedConnectRebootDelay.stop(); return true; } createSafeStringReader(sfReaderDoorBell, 120, "\n\r"); void handleDoorBellClient() { if (!doorBellClient.connected()) { connectionAliveDelay.stop(); if (doorBellClientKeepAliveTimer.isRunning()) { doorBellClientKeepAliveTimer.stop(); sfReaderDoorBell.end(); doorBellClient.stop(); } if (!doorBellReconnectDelay.isRunning()) { doorBellReconnectDelay.start(RECONNECT_DELAY_ms); if (debugPtr) { debugPtr->print("start reconnection delay"); debugPtr->println(); } } } if (doorBellReconnectDelay.justFinished()) { if (debugPtr) { debugPtr->print("reconnection delay finished"); debugPtr->println(); } if (connectClientToHost(doorBellClient, DoorBell_host, portNo)) { doorBellClient.println(); doorBellClient.setTimeout(5); doorBellClientKeepAliveTimer.start(KEEP_ALIVE_TIMER_ms); sfReaderDoorBell.connect(doorBellClient); sfReaderDoorBell.returnEmptyTokens(); // empty lines keep connection open connectionAliveDelay.start(CONNECTION_ALIVE_ms); } else { doorBellClient.stop(); return; } } if (doorBellClient.connected()) { if (sfReaderDoorBell.read()) { connectionAliveDelay.restart(); sfReaderDoorBell.trim(); if (!sfReaderDoorBell.isEmpty()) { if (debugPtr) { debugPtr->print("Door Bell data: "); debugPtr->println(sfReaderDoorBell); } if (sfReaderDoorBell.indexOf("Bell Pressed") >= 0) { triggerMsg(); triggerBell(); showBellIndicator(); } else { showBellIndicator(); } } } else { // did not find a line of input if (connectionAliveDelay.justFinished()) { doorBellClient.stop(); // stop and try to reconnect sfReaderDoorBell.end(); } } if (doorBellClientKeepAliveTimer.justFinished()) { doorBellClientKeepAliveTimer.restart(); doorBellClient.println(); } } } void setup() { Serial.begin(115200); #ifdef DEBUG for (int i = 10; i > 0; i--) { Serial.print(i); Serial.print(' '); delay(500); } SafeString::setOutput(Serial); // for debug error msgs debugPtr = &Serial; debugPtr->println(); #endif #ifdef DEBUG setESPAutoWiFiConfigDebugOut(Serial); // note this prints out network password!! #endif if (ESPAutoWiFiConfigSetup(LedPin, highForLedOn, eepromOffset )) { return; } // NOTE: do not have this code before ESPAutoWiFiConfigSetup as it will cause the XIAO ESP32C3 to reboot on watch dog, ESP32 is OK enableLoopWDT(); // default appears to be 5sec initMp3Speaker(); buzzer.setPin(BuzzerTonePin); // set pin for buzzer to enable. Set here to avoid some noise on startup pinFlasher.setPin(LedPin); } void loop() { if (ESPAutoWiFiConfigLoop()) { // handle auto configure/connect return; } updateMsg(); updateBell(); cancelBellFlasher(); pinFlasher.update(); handleDoorBellClient(); yield(); if (failedConnectRebootDelay.justFinished()) { // no connection for 1min while (1) {} // force watch dog timer reboot } }