From 4d1d7fa4aa3dd7a13506c7010029a818bedb4c67 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sat, 23 Jan 2021 18:24:56 -0600 Subject: [PATCH 01/49] Add Classic series panel support, PC1500/PC1550 --- README.md | 48 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 12 + .../Homebridge-MQTT/Homebridge-MQTT.ino | 12 + .../Arduino/KeybusReader/KeybusReader.ino | 12 + .../Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 12 + examples/Arduino/Status/Status.ino | 12 + examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino | 7 +- examples/esp32/Email/Email.ino | 27 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 24 +- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 24 +- examples/esp32/Homey/Homey.ino | 24 +- examples/esp32/KeybusReader/KeybusReader.ino | 24 +- .../esp32/KeybusReaderIP/KeybusReaderIP.ino | 24 +- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 24 +- examples/esp32/Pushbullet/Pushbullet.ino | 27 +- examples/esp32/Status/Status.ino | 24 +- examples/esp32/Telegram/Telegram.ino | 24 +- examples/esp32/TimeSyncNTP/TimeSyncNTP.ino | 13 +- examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 21 +- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 21 +- .../VirtualKeypad-Blynk.ino | 33 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 30 +- examples/esp8266/Email/Email.ino | 28 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 24 +- .../Homebridge-MQTT/Homebridge-MQTT.ino | 24 +- examples/esp8266/Homey/Homey.ino | 24 +- .../esp8266/KeybusReader/KeybusReader.ino | 24 +- .../esp8266/KeybusReaderIP/KeybusReaderIP.ino | 24 +- .../esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 24 +- examples/esp8266/Pushbullet/Pushbullet.ino | 28 +- examples/esp8266/Status/Status.ino | 24 +- examples/esp8266/Telegram/Telegram.ino | 24 +- examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino | 15 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 28 +- .../VirtualKeypad-Blynk.ino | 33 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 31 +- keywords.txt | 4 + library.json | 2 +- src/dscClassic.cpp | 1209 +++++++++++++++++ src/dscClassic.h | 208 +++ src/dscKeybus.h | 342 +++++ src/dscKeybusInterface.cpp | 57 +- src/dscKeybusInterface.h | 423 ++---- src/dscKeybusPrintData.cpp | 2 +- src/dscKeybusProcessData.cpp | 2 +- 45 files changed, 2492 insertions(+), 591 deletions(-) create mode 100644 src/dscClassic.cpp create mode 100644 src/dscClassic.h create mode 100644 src/dscKeybus.h diff --git a/README.md b/README.md index e602b50..1d86f5e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # DSC Keybus Interface -This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) security systems for integration with home automation, notifications on alarm events, and direct control as a virtual keypad. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. +This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, and direct control as a virtual keypad. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) @@ -39,7 +39,7 @@ Example integrations: **I Had**: _A DSC security system not being monitored by a third-party service._ **I Wanted**: _Notification if the alarm triggered._ -I was interested in finding a solution that directly accessed the pair of data lines that DSC uses for their proprietary Keybus protocol to send data between the panel, keypads, and other modules. Tapping into the data lines is an ideal task for a microcontroller and also presented an opportunity to work with the [Arduino](https://www.arduino.cc) and [FreeRTOS](https://www.freertos.org) (via [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)) platforms. +I was interested in finding a solution that directly accessed the pair of data lines that DSC uses for their proprietary Keybus protocol to send data between the panel, keypads, and other modules (instead of using the DSC IT-100 serial module). Tapping into the data lines is an ideal task for a microcontroller and also presented an opportunity to work with the [Arduino](https://www.arduino.cc) and [FreeRTOS](https://www.freertos.org) (via [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)) platforms. While there has been excellent [discussion about the DSC Keybus protocol](https://www.avrfreaks.net/forum/dsc-keybus-protocol) and a several existing projects, there were a few issues that remained unsolved: * Error-prone Keybus data capture. @@ -47,7 +47,7 @@ While there has been excellent [discussion about the DSC Keybus protocol](https: * Read-only - unable to control the Keybus to act as a virtual keypad. * No implementations to do useful work with the data. -Poking around with a logic analyzer and oscilloscope revealed that the errors capturing the Keybus data were timing issues - after resolving the data errors, it was possible to reverse engineer the protocol by capturing the Keybus binary data as the security system handled various events. +This library uses a combination of hardware and timer interrupts to accurately capture Keybus data, and has lead to reverse engineering much of the Keybus protocol. ## Features * Monitor the status of all partitions: @@ -59,7 +59,7 @@ Poking around with a logic analyzer and oscilloscope revealed that the errors ca * Monitor PGM outputs 1-14 status * Virtual keypad: - Write keys to the panel for all partitions -* Panel time - retrieve current panel date/time and set a new date/time +* Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code * Direct Keybus interface: - Does not require the [DSC IT-100 serial interface](https://www.dsc.com/alarm-security-products/IT-100%20-%20PowerSeries%20Integration%20Module/22). @@ -69,12 +69,12 @@ Poking around with a logic analyzer and oscilloscope revealed that the errors ca - Extensive data decoding: the majority of Keybus data as seen in the [DSC IT-100 Data Interface developer's guide](https://cms.dsc.com/download.php?t=1&id=16238) has been reverse engineered and documented in [`src/dscKeybusPrintData.cpp`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybusPrintData.cpp). - Non-blocking code: Allows sketches to run as quickly as possible without using `delay` or `delayMicroseconds` * Supported security systems: - - [DSC PowerSeries](https://www.dsc.com/?n=enduser&o=identify) - - Verified panels: PC585, PC1555MX, PC1565, PC5005, PC5010, PC5015, PC5020, PC1616, PC1808, PC1832, PC1864. - - All PowerSeries series are supported, please [post an issue](https://github.com/taligentx/dscKeybusInterface/issues) if you have a different panel and have tested the interface to update this list. + - [DSC PowerSeries](https://www.dsc.com/?n=enduser&o=identify) - all panels are supported, tested with: PC585, PC1555MX, PC1565, PC5005, PC5010, PC5015, PC5020, PC1616, PC1808, PC1832, PC1864 + - [DSC Classic series](https://www.dsc.com/?n=enduser&o=identify): PC1500, PC1550 + * Requires configuring the panel through *8 programming to enable PC16-OUT: section 19, option 4. + * PC2500, PC2550, PC3000 are untested, [post an issue](https://github.com/taligentx/dscKeybusInterface/issues) if you're able to test these panels. - Rebranded DSC PowerSeries (such as some ADT systems) should also work with this interface. * Unsupported security systems: - - DSC Classic series ([PC1500, PC1550, etc](https://www.dsc.com/?n=enduser&o=identify)) use a different data protocol, though support is possible. - DSC Alexor (PC9155) is all wireless and does not have an accessible Keybus interface. - DSC Neo series use a higher speed encrypted data protocol (Corbus) that is not currently possible to support. - Other brands (that are not rebranded DSC systems) use different protocols and are not supported. @@ -93,9 +93,10 @@ Poking around with a logic analyzer and oscilloscope revealed that the errors ca * Possible features (PRs welcome!): - [DSC IT-100](https://cms.dsc.com/download.php?t=1&id=16238) emulation - Unlock 6-digit installer codes - - DSC Classic series support: This protocol is [already decoded](https://github.com/dougkpowers/pc1550-interface), use with this library would require major changes. ## Release notes +* develop + - New: DSC Classic series panel support: PC1500, PC1550 * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT @@ -213,7 +214,6 @@ The included examples demonstrate how to use the library and can be used as-is o This can be used to send SMS text messages if the number's service provider has an [email to SMS gateway](https://en.wikipedia.org/wiki/SMS_gateway#Email_clients) - examples for the US: * T-mobile: 5558675309@tmomail.net * Verizon: 5558675309@vtext.com - * Sprint: 5558675309@messaging.sprintpcs.com * AT&T: 5558675309@txt.att.net * **VirtualKeypad-Blynk** (esp8266/esp32): Provides a virtual keypad interface for the free [Blynk](https://www.blynk.cc) app on iOS and Android, including viewing alarm memory, programming zone lights, and the event buffer. Scan one of the following QR codes from within the Blynk app for an example keypad layout: @@ -248,34 +248,43 @@ DSC Aux(+) ---+--- Arduino Vin pin DSC Aux(-) --- Arduino/esp8266/esp32 Ground - Arduino +--- dscClockPin (Arduino Uno: 2,3) + Arduino +--- dscClockPin (Arduino Uno: 3) DSC Yellow ---+--- 15k ohm resistor ---| | +--- 10k ohm resistor --- Ground | - | esp8266/esp32 +--- dscClockPin (esp8266: D1,D2,D8 / esp32: 4,13,16-39) + | esp8266/esp32 +--- dscClockPin (esp8266: D1, GPIO 5 / esp32: 18) +--- 33k ohm resistor ---| +--- 10k ohm resistor --- Ground - Arduino +--- dscReadPin (Arduino Uno: 2-12) + Arduino +--- dscReadPin (Arduino Uno: 5) DSC Green ----+--- 15k ohm resistor ---| | +--- 10k ohm resistor --- Ground | - | esp8266/esp32 +--- dscReadPin (esp8266: D1,D2,D8 / esp32: 4,13,16-39) + | esp8266/esp32 +--- dscReadPin (esp8266: D2, GPIO 4 / esp32: 19) + +--- 33k ohm resistor ---| + +--- 10k ohm resistor --- Ground + +Classic series only, PGM configured for PC-16 output: + Arduino +--- dscPC16Pin (Arduino Uno: 4) +DSC PGM ------+--- 15k ohm resistor ---| + | +--- 10k ohm resistor --- Ground + | + | esp8266/esp32 +--- dscPC16Pin (esp8266: D7, GPIO 13 / esp32: 17) +--- 33k ohm resistor ---| +--- 10k ohm resistor --- Ground Virtual keypad (optional): DSC Green ---- NPN collector --\ - |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12 / esp8266: D1,D2,D8 / esp32: 4,13,16-33) + |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 6 / esp8266: D8, GPIO 15 / esp32: 21) Ground --- NPN emitter --/ ``` -* The DSC Keybus operates at ~12.6v, a pair of resistors per data line will bring this down to an appropriate voltage for each microcontroller. +* The DSC PowerSeries Keybus operates at ~12.6v and the Classic series operates at ~13.75v, a pair of resistors per data line will bring this down to an appropriate voltage for each microcontroller. * Arduino: * The DSC yellow (clock) line connects to a [hardware interrupt pin](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) - for the Uno, these are pins 2 or 3. The example sketches use dscClockPin: 3. * The DSC green (data) line can be connected to any of the remaining digital pins 2-12. The examples sketches use dscReadPin: 5 and dscWritePin: 6. - * esp8266: connect the DSC lines to GPIO pins that are normally low to avoid putting spurious data on the Keybus: D1 (GPIO5), D2 (GPIO4) and D8 (GPIO15). The example sketches use dscClockPin: D1, dscReadPin: D2, dscWritePin: D8. - * esp32: connect the DSC lines to GPIO pins that do not send signals at boot: 4, 13, 16-39. For virtual keypad, use pins 4, 13, 16-33 - pins 34-39 are input only and cannot be used. The example sketches use dscClockPin: 18, dscReadPin: 19, dscWritePin: 21. + * esp8266: connect the DSC lines to GPIO pins that are normally low to avoid putting spurious data on the Keybus: D1 (GPIO5), D2 (GPIO4) and D8 (GPIO15). The example sketches use dscClockPin: D1, dscReadPin: D2, dscWritePin: D8. For the Classic series, dscPC16Pin: D7 (GPIO 13). + * esp32: connect the DSC lines to GPIO pins that do not send signals at boot: 4, 13, 16-39. For virtual keypad, use pins 4, 13, 16-33 - pins 34-39 are input only and cannot be used. The example sketches use dscClockPin: 18, dscReadPin: 19, dscWritePin: 21. For the Classic series, dscPC16Pin: 17. * Virtual keypad uses an NPN transistor and a resistor to write to the Keybus. Most small signal NPN transistors should be suitable, for example: * 2N3904 * BC547, BC548, BC549 @@ -320,6 +329,9 @@ Panel options affecting this interface, configured by `*8 + installer code` - se - AC power failure reporting delay: The default delay is 30 minutes and can be set to `000` to immediately report a power failure. +* PC1550 Classic series section `19`: + - PC16-OUT: Enable option 4 to set the PGM output to PC16 mode to send required panel status data on the Keybus. + ## Notes * For OTA updates on esp8266 and esp32, you may need to stop the interface using `dsc.stop();`: ``` diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index f34a4b7..5732936 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -177,6 +177,10 @@ entity: alarm_control_panel.security_partition_1 * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) @@ -193,6 +197,9 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -221,11 +228,16 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif EthernetClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index 3c2d69e..48bf11f 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -168,6 +168,10 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) @@ -184,6 +188,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -208,11 +215,16 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif EthernetClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/Arduino/KeybusReader/KeybusReader.ino b/examples/Arduino/KeybusReader/KeybusReader.ino index 3a4ccc2..70bf772 100644 --- a/examples/Arduino/KeybusReader/KeybusReader.ino +++ b/examples/Arduino/KeybusReader/KeybusReader.ino @@ -23,6 +23,10 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) @@ -39,16 +43,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif void setup() { diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index dbf79b9..f0f8570 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -98,6 +98,10 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) @@ -114,6 +118,9 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -142,11 +149,16 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif EthernetClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/Arduino/Status/Status.ino b/examples/Arduino/Status/Status.ino index f71d143..5272cb6 100644 --- a/examples/Arduino/Status/Status.ino +++ b/examples/Arduino/Status/Status.ino @@ -23,6 +23,10 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) @@ -39,16 +43,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif void setup() { diff --git a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino index e46039c..c94469e 100644 --- a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino @@ -25,6 +25,10 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin (Arduino Uno: 2-12) + * DSC PGM ------ 15k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad: * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) @@ -60,12 +64,13 @@ byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address uniqu // Configures the Keybus interface with the specified pins #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components -EthernetUDP ipClient; dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +EthernetUDP ipClient; byte ntpBuffer[48]; unsigned int localPort = 8888; bool ntpSynced = true; diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index d9c83a5..defa9fe 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -16,23 +16,18 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): - * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) - * Ground --- NPN emitter --/ + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground * - * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should - * be suitable, for example: - * -- 2N3904 - * -- BC547, BC548, BC549 * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface @@ -40,6 +35,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -49,11 +47,16 @@ const char* wifiPassword = ""; const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 169c05c..9f12fff 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -168,17 +168,21 @@ entity: alarm_control_panel.security_partition_1 * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -192,6 +196,9 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -220,12 +227,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index 1ca9f47..ffdee7e 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -156,17 +156,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -180,6 +184,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -203,12 +210,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index dce044d..72ccbd3 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -19,17 +19,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -45,6 +49,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -56,12 +63,17 @@ const char* accessCode = ""; // An access code is required to disarm/night arm // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif bool wifiConnected = true; diff --git a/examples/esp32/KeybusReader/KeybusReader.ino b/examples/esp32/KeybusReader/KeybusReader.ino index 75411fa..187331c 100644 --- a/examples/esp32/KeybusReader/KeybusReader.ino +++ b/examples/esp32/KeybusReader/KeybusReader.ino @@ -15,17 +15,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -39,16 +43,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif void setup() { diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index 301dda5..4ed1d5d 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -19,17 +19,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -45,6 +49,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -57,12 +64,17 @@ const int serverPort = 23; // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif WiFiServer ipServer(serverPort); WiFiClient ipClient; diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index 2788627..f1389fe 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -98,17 +98,21 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -122,6 +126,9 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -150,12 +157,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index bf5deb1..9fd205e 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -19,23 +19,18 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): - * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) - * Ground --- NPN emitter --/ + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground * - * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should - * be suitable, for example: - * -- 2N3904 - * -- BC547, BC548, BC549 * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface @@ -43,6 +38,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -53,8 +51,9 @@ const char* pushbulletToken = ""; // Set the access token generated in the Push const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 // HTTPS root certificate for api.pushbullet.com: GlobalSign Root CA - R2, expires 2021.12.15 const char pushbulletCertificateRoot[] = R"=EOF=( @@ -86,7 +85,11 @@ Kk5GkNl1LNj/jO7M3GnrbOYV0KP/SAusVd/fJZ1CtlGjZpVgxdAi5yJ6UaXMhw== )=EOF="; // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp32/Status/Status.ino b/examples/esp32/Status/Status.ino index 783241c..08c7e4f 100644 --- a/examples/esp32/Status/Status.ino +++ b/examples/esp32/Status/Status.ino @@ -14,17 +14,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -38,16 +42,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif void setup() { diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 67b04b4..898f73f 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -34,17 +34,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -58,6 +62,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -73,12 +80,17 @@ const char* telegramUserID = ""; // Set the Telegram chat user ID const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClientSecure ipClient; UniversalTelegramBot telegramBot(telegramBotToken, ipClient); const int telegramCheckInterval = 1000; diff --git a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino index 8533b14..d1feeb1 100644 --- a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino @@ -14,17 +14,17 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -52,9 +52,10 @@ const char* ntpServer = "pool.ntp.org"; // Set the NTP server const byte timePartition = 1; // Set the partition to use for setting the time // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index 7909b35..35f9aaa 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -18,14 +18,19 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * + * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface * @@ -34,6 +39,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + // Configures GSM modem model. Must be done before including TinyGsmClient library. #define TINY_GSM_MODEM_SIM800 @@ -56,11 +64,16 @@ const char* sendToPhoneNumbers[] = { #define MODEM_RX 26 // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif TinyGsm modem(Serial1); void setup() { diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index a54342f..f051194 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -12,14 +12,19 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * + * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface * @@ -28,6 +33,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -42,11 +50,16 @@ const char* To = ""; // i.e. 16041234567 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index f214a14..f809fff 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -62,17 +62,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -86,6 +90,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -96,18 +103,24 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; +const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* blynkAuthToken = ""; // Token generated from within the Blynk app const char* blynkServer = ""; // Blynk local server address const int blynkPort = 8080; // Blynk local server port bool showLCDoutput = true; // Control if LCD programming output is displayed on VirtualPin20 // Configures the Keybus interface with the specified pins -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif bool wifiConnected = true; bool partitionChanged, pausedZones, extendedBuffer; bool decimalOutput, inputDigits; @@ -851,6 +864,7 @@ void setLights(byte partition, bool forceUpdate) { // Processes status data not natively handled within the library void processStatus() { + #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { @@ -888,6 +902,7 @@ void processStatus() { break; case 0xEC: if (pausedZones) processEventBufferEC(); break; } + #endif } @@ -917,6 +932,7 @@ void processProgramZones(byte startByte, const char* ledColor) { void processLCDoutputData() { + #ifndef dscClassicSeries if (!showLCDoutput) return; // Do not display LCD output data if showLCDoutput is false char dataInfo[21] = "LCD Display: "; char dataBuffer[4]; @@ -935,10 +951,12 @@ void processLCDoutputData() { } } Blynk.virtualWrite(V20, dataInfo); + #endif } void processEventBufferAA() { + #ifndef dscClassicSeries if (extendedBuffer) return; // Skips 0xAA data when 0xEC extended event buffer data is available char eventInfo[45] = "Event: "; @@ -993,10 +1011,12 @@ void processEventBufferAA() { case 0x02: printPanelStatus2(6); break; case 0x03: printPanelStatus3(6); break; } + #endif } void processEventBufferEC() { + #ifndef dscClassicSeries if (!extendedBuffer) extendedBuffer = true; char eventInfo[45] = "Event: "; @@ -1068,6 +1088,7 @@ void processEventBufferEC() { case 0x18: printPanelStatus18(8); break; case 0x1B: printPanelStatus1B(8); break; } + #endif } diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 6f454e7..0dfe442 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -36,17 +36,21 @@ * * DSC Aux(-) --- esp32 Ground * - * +--- dscClockPin (esp32: 4,13,16-39) + * +--- dscClockPin // Default: 18 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp32: 4,13,16-39) + * +--- dscReadPin // Default: 19 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: 17 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -62,6 +66,8 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries #include #include @@ -77,16 +83,22 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; +const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* dnsHostname = "dsc"; // Sets the domain name - if set to "dsc", access via: http://dsc.local const byte dscPartition = 1; // Set the partition for the keypad // Configures the Keybus interface with the specified pins -#define dscClockPin 18 // esp32: 4,13,16-39 -#define dscReadPin 19 // esp32: 4,13,16-39 -#define dscWritePin 21 // esp32: 4,13,16-33 +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif AsyncWebServer server(80); AsyncWebSocket ws("/ws"); Chrono ws_ping_pong(Chrono::SECONDS); @@ -462,6 +474,7 @@ void setLights(byte partition) { // Processes status data not natively handled within the library void processStatus() { + #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xB8) && !pausedZones) { @@ -495,6 +508,7 @@ void processStatus() { break; case 0xEC: if (pausedZones) processEventBufferEC(); break; } + #endif } @@ -527,6 +541,7 @@ void processProgramZones(byte startByte) { void processEventBufferAA() { + #ifndef dscClassicSeries if (extendedBuffer) return; // Skips 0xAA data when 0xEC extended event buffer data is available char eventInfo[45] = "Event: "; @@ -588,10 +603,12 @@ void processEventBufferAA() { case 0x02: printPanelStatus2(6); break; case 0x03: printPanelStatus3(6); break; } + #endif } void processEventBufferEC() { + #ifndef dscClassicSeries if (!extendedBuffer) extendedBuffer = true; char eventInfo[45] = "Event: "; @@ -670,6 +687,7 @@ void processEventBufferEC() { case 0x18: printPanelStatus18(8); break; case 0x1B: printPanelStatus1B(8); break; } + #endif } diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index 7b8d9ae..3b55307 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -21,23 +21,17 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): - * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) - * Ground --- NPN emitter --/ - * - * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should - * be suitable, for example: - * -- 2N3904 - * -- BC547, BC548, BC549 + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface @@ -45,6 +39,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -54,11 +51,16 @@ const char* wifiPassword = ""; const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 9998bff..217032a 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -176,17 +176,21 @@ entity: alarm_control_panel.security_partition_1 * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -200,6 +204,9 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -228,12 +235,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index 9a33226..ec2c83e 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -162,17 +162,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -186,6 +190,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -209,12 +216,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index a0e4b1d..73df204 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -22,17 +22,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -48,6 +52,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -59,12 +66,17 @@ const char* accessCode = ""; // An access code is required to disarm/night arm // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif bool wifiConnected = true; diff --git a/examples/esp8266/KeybusReader/KeybusReader.ino b/examples/esp8266/KeybusReader/KeybusReader.ino index 874c4af..f2d8062 100644 --- a/examples/esp8266/KeybusReader/KeybusReader.ino +++ b/examples/esp8266/KeybusReader/KeybusReader.ino @@ -16,17 +16,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -40,16 +44,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif void setup() { diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index ecbf98c..401c673 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -20,17 +20,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -46,6 +50,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -58,12 +65,17 @@ const int serverPort = 23; // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif WiFiServer ipServer(serverPort); WiFiClient ipClient; diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index 128a393..bd9c6c6 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -99,17 +99,21 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -123,6 +127,9 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -151,12 +158,17 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 8765531..9d3f81d 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -25,23 +25,17 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): - * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) - * Ground --- NPN emitter --/ - * - * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should - * be suitable, for example: - * -- 2N3904 - * -- BC547, BC548, BC549 + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface @@ -49,6 +43,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -59,8 +56,9 @@ const char* pushbulletToken = ""; // Set the access token generated in the Push const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 // HTTPS root certificate for api.pushbullet.com: GlobalSign Root CA - R2, expires 2021.12.15 const char pushbulletCertificateRoot[] = R"=EOF=( @@ -92,7 +90,11 @@ Kk5GkNl1LNj/jO7M3GnrbOYV0KP/SAusVd/fJZ1CtlGjZpVgxdAi5yJ6UaXMhw== )=EOF="; // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif X509List pushbulletCert(pushbulletCertificateRoot); WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp8266/Status/Status.ino b/examples/esp8266/Status/Status.ino index 219d72e..45ab1ce 100644 --- a/examples/esp8266/Status/Status.ino +++ b/examples/esp8266/Status/Status.ino @@ -16,17 +16,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -40,16 +44,24 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin); +#endif void setup() { diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index 1acedc5..01fc5c9 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -34,17 +34,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -58,6 +62,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -73,12 +80,17 @@ const char* telegramUserID = ""; // Set the Telegram chat user ID const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif X509List telegramCert(TELEGRAM_CERTIFICATE_ROOT); WiFiClientSecure ipClient; UniversalTelegramBot telegramBot(telegramBotToken, ipClient); diff --git a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino index d514ea8..5f28bfe 100644 --- a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino @@ -14,17 +14,17 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad: + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -52,9 +52,10 @@ const char* ntpServer = "pool.ntp.org"; // Set the NTP server const byte timePartition = 1; // Set the partition to use for setting the time // Configures the Keybus interface with the specified pins. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index f174831..1d41c4c 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -18,23 +18,17 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): - * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) - * Ground --- NPN emitter --/ - * - * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should - * be suitable, for example: - * -- 2N3904 - * -- BC547, BC548, BC549 + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface @@ -44,6 +38,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include @@ -58,11 +55,16 @@ const char* To = ""; // i.e. 16041234567 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif WiFiClientSecure ipClient; bool wifiConnected = true; diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 738dbd0..77713f2 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -63,17 +63,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -87,6 +91,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -96,18 +103,24 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; +const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* blynkAuthToken = ""; // Token generated from within the Blynk app const char* blynkServer = ""; // Blynk local server address const int blynkPort = 8080; // Blynk local server port bool showLCDoutput = true; // Control if LCD programming output is displayed on VirtualPin20 // Configures the Keybus interface with the specified pins -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif bool wifiConnected = true; bool partitionChanged, pausedZones, extendedBuffer; bool decimalOutput, inputDigits; @@ -853,6 +866,7 @@ void setLights(byte partition, bool forceUpdate) { // Processes status data not natively handled within the library void processStatus() { + #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { @@ -890,6 +904,7 @@ void processStatus() { break; case 0xEC: if (pausedZones) processEventBufferEC(); break; } + #endif } @@ -919,6 +934,7 @@ void processProgramZones(byte startByte, const char* ledColor) { void processLCDoutputData() { + #ifndef dscClassicSeries if (!showLCDoutput) return; // Do not display LCD output data if showLCDoutput is false char dataInfo[21] = "LCD Display: "; char dataBuffer[4]; @@ -937,10 +953,12 @@ void processLCDoutputData() { } } Blynk.virtualWrite(V20, dataInfo); + #endif } void processEventBufferAA() { + #ifndef dscClassicSeries if (extendedBuffer) return; // Skips 0xAA data when 0xEC extended event buffer data is available char eventInfo[45] = "Event: "; @@ -995,10 +1013,12 @@ void processEventBufferAA() { case 0x02: printPanelStatus2(6); break; case 0x03: printPanelStatus3(6); break; } + #endif } void processEventBufferEC() { + #ifndef dscClassicSeries if (!extendedBuffer) extendedBuffer = true; char eventInfo[45] = "Event: "; @@ -1070,6 +1090,7 @@ void processEventBufferEC() { case 0x18: printPanelStatus18(8); break; case 0x1B: printPanelStatus1B(8); break; } + #endif } diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 6341443..412c196 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -45,17 +45,21 @@ * * DSC Aux(-) --- esp8266 Ground * - * +--- dscClockPin (esp8266: D1, D2, D8) + * +--- dscClockPin // Default: D1, GPIO 5 * DSC Yellow --- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscReadPin (esp8266: D1, D2, D8) + * +--- dscReadPin // Default: D2, GPIO 4 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * + * +--- dscPC16Pin // Default: D7, GPIO 13 + * DSC PGM ------ 33k ohm resistor ---| + * (Classic series only) +--- 10k ohm resistor --- Ground + * * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should @@ -71,6 +75,9 @@ * This example code is in the public domain. */ +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +//#define dscClassicSeries + #include #include #include @@ -83,16 +90,22 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; +const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* dnsHostname = "dsc"; // Sets the domain name - if set to "dsc", access via: http://dsc.local const byte dscPartition = 1; // Set the partition for the keypad // Configures the Keybus interface with the specified pins -#define dscClockPin D1 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscReadPin D2 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) -#define dscWritePin D8 // esp8266: D1, D2, D8 (GPIO 5, 4, 15) +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 // Initialize components +#ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif AsyncWebServer server(80); AsyncWebSocket ws("/ws"); Chrono ws_ping_pong(Chrono::SECONDS); @@ -470,6 +483,7 @@ void setLights(byte partition) { // Processes status data not natively handled within the library void processStatus() { + #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xB8) && !pausedZones) { @@ -503,6 +517,7 @@ void processStatus() { break; case 0xEC: if (pausedZones) processEventBufferEC(); break; } + #endif } @@ -535,6 +550,7 @@ void processProgramZones(byte startByte) { void processEventBufferAA() { + #ifndef dscClassicSeries if (extendedBuffer) return; // Skips 0xAA data when 0xEC extended event buffer data is available char eventInfo[45] = "Event: "; @@ -596,10 +612,12 @@ void processEventBufferAA() { case 0x02: printPanelStatus2(6); break; case 0x03: printPanelStatus3(6); break; } + #endif } void processEventBufferEC() { + #ifndef dscClassicSeries if (!extendedBuffer) extendedBuffer = true; char eventInfo[45] = "Event: "; @@ -678,6 +696,7 @@ void processEventBufferEC() { case 0x18: printPanelStatus18(8); break; case 0x1B: printPanelStatus1B(8); break; } + #endif } diff --git a/keywords.txt b/keywords.txt index dd82532..b6e4ac8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,12 +1,15 @@ dscKeybusInterface KEYWORD1 +dscClassicInterface KEYWORD1 dsc KEYWORD1 dscClockPin LITERAL1 dscReadPin LITERAL1 dscWritePin LITERAL1 dscRelayPin LITERAL1 +dscPC16Pin LITERAL1 dscZones LITERAL1 dscPartitions LITERAL1 +dscClassicSeries LITERAL1 hideKeypadDigits KEYWORD2 displayTrailingBits KEYWORD2 @@ -69,6 +72,7 @@ pgmOutputsStatusChanged KEYWORD2 lights KEYWORD2 status KEYWORD2 panelData KEYWORD2 +pc16Data KEYWORD2 panelVersion KEYWORD2 printPanelBinary KEYWORD2 diff --git a/library.json b/library.json index b85bced..88af0af 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "dscKeybusInterface", - "keywords": "dsc, home-automation, home-security, homebridge, homekit, home-assistant, homeassistant, homey, openhab, google-home, blnynk, web, webserver, telegram, pushbullet, twilio, email, esp8266, esp32", + "keywords": "dsc, home-automation, home-security, homebridge, homekit, home-assistant, homeassistant, homey, openhab, google-home, blynk, web, webserver, telegram, pushbullet, twilio, email, esp8266, esp32", "description": "This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to DSC PowerSeries security systems for integration with home automation (Home Assistant, Apple HomeKit, Homey), notifications on system events, and usage as a virtual keypad.", "repository": { diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp new file mode 100644 index 0000000..7cf4971 --- /dev/null +++ b/src/dscClassic.cpp @@ -0,0 +1,1209 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "dscClassic.h" + +#if defined(ESP32) +hw_timer_t * dscClassicInterface::timer0 = NULL; +portMUX_TYPE dscClassicInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; +#endif + +dscClassicInterface::dscClassicInterface(byte setClockPin, byte setReadPin, byte setPC16Pin, byte setWritePin, const char * setAccessCode) { + dscClockPin = setClockPin; + dscReadPin = setReadPin; + dscPC16Pin = setPC16Pin; + dscWritePin = setWritePin; + if (dscWritePin != 255) virtualKeypad = true; + writeReady = false; + writePartition = 1; + pauseStatus = false; + accessCodeStay = setAccessCode; + strcpy(accessCodeAway, accessCodeStay); + strcat(accessCodeAway, "*1"); + strcpy(accessCodeNight, "*9"); + strcat(accessCodeNight, accessCodeStay); +} + + +void dscClassicInterface::begin(Stream &_stream) { + pinMode(dscClockPin, INPUT); + pinMode(dscReadPin, INPUT); + pinMode(dscPC16Pin, INPUT); + if (virtualKeypad) pinMode(dscWritePin, OUTPUT); + stream = &_stream; + + // Platform-specific timers trigger a read of the data line 250us after the Keybus clock changes: + + // Arduino/AVR Timer1 calls ISR(TIMER1_OVF_vect) from dscClockInterrupt() and is disabled in the ISR for a one-shot timer + #if defined(__AVR__) + TCCR1A = 0; + TCCR1B = 0; + TIMSK1 |= (1 << TOIE1); + + // esp8266 timer1 calls dscDataInterrupt() from dscClockInterrupt() as a one-shot timer + #elif defined(ESP8266) + timer1_isr_init(); + timer1_attachInterrupt(dscDataInterrupt); + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); + + // esp32 timer0 calls dscDataInterrupt() from dscClockInterrupt() + #elif defined(ESP32) + timer0 = timerBegin(0, 80, true); + timerStop(timer0); + timerAttachInterrupt(timer0, &dscDataInterrupt, true); + timerAlarmWrite(timer0, 250, true); + timerAlarmEnable(timer0); + #endif + + // Generates an interrupt when the Keybus clock rises or falls - requires a hardware interrupt pin on Arduino/AVR + attachInterrupt(digitalPinToInterrupt(dscClockPin), dscClockInterrupt, CHANGE); +} + + +void dscClassicInterface::stop() { + + // Disables Arduino/AVR Timer1 interrupts + #if defined(__AVR__) + TIMSK1 = 0; + + // Disables esp8266 timer1 + #elif defined(ESP8266) + timer1_disable(); + timer1_detachInterrupt(); + + // Disables esp32 timer0 + #elif defined(ESP32) + timerAlarmDisable(timer0); + timerEnd(timer0); + #endif + + // Disables the Keybus clock pin interrupt + detachInterrupt(digitalPinToInterrupt(dscClockPin)); + + // Resets the panel capture data and counters + panelBufferLength = 0; + for (byte i = 0; i < dscReadSize; i++) { + isrPanelData[i] = 0; + isrPC16Data[i] = 0; + isrModuleData[i] = 0; + } + isrPanelBitTotal = 0; + isrPanelBitCount = 0; + isrPanelByteCount = 0; + + // Resets the keypad and module data counters + isrModuleBitTotal = 0; + isrModuleBitCount = 0; + isrModuleByteCount = 0; +} + + +bool dscClassicInterface::loop() { + + #if defined(ESP8266) || defined(ESP32) + yield(); + #endif + + // Checks if Keybus data is detected and sets a status flag if data is not detected for 3s + #if defined(ESP32) + portENTER_CRITICAL(&timer0Mux); + #else + noInterrupts(); + #endif + + if (millis() - keybusTime > 3000) keybusConnected = false; // keybusTime is set in dscDataInterrupt() when the clock resets + else keybusConnected = true; + + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #else + interrupts(); + #endif + + if (previousKeybus != keybusConnected) { + previousKeybus = keybusConnected; + keybusChanged = true; + if (!pauseStatus) statusChanged = true; + if (!keybusConnected) return true; + } + + // Writes keys when multiple keys are sent as a char array + if (writeKeysPending) writeKeys(writeKeysArray); + + // Skips processing if the panel data buffer is empty + if (panelBufferLength == 0) return false; + + // Copies data from the buffer to panelData[] + static byte panelBufferIndex = 1; + byte dataIndex = panelBufferIndex - 1; + for (byte i = 0; i < dscReadSize; i++) { + panelData[i] = panelBuffer[dataIndex][i]; + pc16Data[i] = pc16Buffer[dataIndex][i]; + } + panelBitCount = panelBufferBitCount[dataIndex]; + panelByteCount = panelBufferByteCount[dataIndex]; + panelBufferIndex++; + + // Resets counters when the buffer is cleared + #if defined(ESP32) + portENTER_CRITICAL(&timer0Mux); + #else + noInterrupts(); + #endif + + if (panelBufferIndex > panelBufferLength) { + panelBufferIndex = 1; + panelBufferLength = 0; + } + + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #else + interrupts(); + #endif + + // Waits at startup for valid data + static bool startupCycle = true; + if (startupCycle) { + if (panelByteCount != 2 || pc16Data[0] == 0xFF) return false; + else { + startupCycle = false; + writeReady = true; + } + } + + // Sets writeReady status + if (!writeKeyPending && !writeKeysPending) writeReady = true; + else writeReady = false; + + processPanelStatus(); + return true; +} + + +// Resets the state of all status components as changed for sketches to get the current status +void dscClassicInterface::resetStatus() { + statusChanged = true; + keybusChanged = true; + troubleChanged = true; + readyChanged[0] = true; + armedChanged[0] = true; + alarmChanged[0] = true; + fireChanged[0] = true; + openZonesStatusChanged = true; + alarmZonesStatusChanged = true; + openZonesChanged[0] = 0xFF; + alarmZonesChanged[0] = 0xFF; + pgmOutputsChanged[0] = true; +} + + +// Processes status +void dscClassicInterface::processPanelStatus() { + + // Keypad lights - maps Classic series keypad lights to PowerSeries keypad light order for sketch compatibility + if (bitRead(panelData[1], 7)) { + readyLight = true; + bitWrite(lights[0], 0, 1); + } + else { + readyLight = false; + bitWrite(lights[0], 0, 0); + } + if (bitRead(panelData[1], 6)) { + armedLight = true; + bitWrite(lights[0], 1, 1); + } + else { + armedLight = false; + bitWrite(lights[0], 1, 0); + } + if (bitRead(panelData[1], 5)) { + memoryLight = true; + bitWrite(lights[0], 2, 1); + } + else { + memoryLight = false; + bitWrite(lights[0], 2, 0); + } + if (bitRead(panelData[1], 4)) { + bypassLight = true; + bitWrite(lights[0], 3, 1); + } + else { + bypassLight = false; + bitWrite(lights[0], 3, 0); + } + if (bitRead(panelData[1], 3)) { + troubleLight = true; + bitWrite(lights[0], 4, 1); + } + else { + troubleLight = false; + bitWrite(lights[0], 4, 0); + } + if (bitRead(panelData[1], 0)) beep = true; + else beep = false; + + if (lights[0] != previousLights) { + previousLights = lights[0]; + if (!pauseStatus) statusChanged = true; + } + + // PC-16 status + if (bitRead(pc16Data[1], 7)) troubleBit = true; + else troubleBit = false; + if (bitRead(pc16Data[1], 6)) armedBypassBit = true; + else armedBypassBit = false; + if (bitRead(pc16Data[1], 5)) armedBit = true; + else armedBit = false; + if (bitRead(pc16Data[1], 0)) alarmBit = true; + else alarmBit = false; + + + // Checks for memory light blinking + static unsigned long memoryLightTimeOn = 0; + static unsigned long memoryLightTimeOff = 0; + if (memoryLight) { + memoryLightTimeOn = millis(); + if (millis() - memoryLightTimeOff < 600) { + memoryBlink = true; + lightBlink = true; + } + else { + memoryBlink = false; + if (!armedBlink && !bypassBlink && !troubleBlink) lightBlink = false; + } + } + else { + memoryLightTimeOff = millis(); + if (millis() - memoryLightTimeOn > 600) { + memoryBlink = false; + if (!armedBlink && !bypassBlink && !troubleBlink) lightBlink = false; + } + } + + // Checks for armed light blinking + static unsigned long armedLightTimeOn = 0; + static unsigned long armedLightTimeOff = 0; + if (armedLight) { + armedLightTimeOn = millis(); + if (millis() - armedLightTimeOff < 600) { + armedBlink = true; + lightBlink = true; + } + else { + armedBlink = false; + if (!memoryBlink && !bypassBlink && !troubleBlink) lightBlink = false; + } + } + else { + armedLightTimeOff = millis(); + if (millis() - armedLightTimeOn > 1200) { + armedBlink = false; + if (!memoryBlink && !bypassBlink && !troubleBlink) lightBlink = false; + } + } + + // Checks for bypass light blinking + static unsigned long bypassLightTimeOn = 0; + static unsigned long bypassLightTimeOff = 0; + if (bypassLight) { + bypassLightTimeOn = millis(); + if (millis() - bypassLightTimeOff < 600) { + bypassBlink = true; + lightBlink = true; + } + else { + bypassBlink = false; + if (!memoryBlink && !armedBlink && !troubleBlink) lightBlink = false; + } + } + else { + bypassLightTimeOff = millis(); + if (millis() - bypassLightTimeOn > 1200) { + bypassBlink = false; + if (!memoryBlink && !armedBlink && !troubleBlink) lightBlink = false; + } + } + + // Checks for trouble light blinking + static unsigned long troubleLightTimeOn = 0; + static unsigned long troubleLightTimeOff = 0; + if (troubleLight) { + troubleLightTimeOn = millis(); + if (millis() - troubleLightTimeOff < 600) { + troubleBlink = true; + lightBlink = true; + } + else { + troubleBlink = false; + if (!memoryBlink && !armedBlink && !bypassBlink) lightBlink = false; + } + } + else { + troubleLightTimeOff = millis(); + if (millis() - troubleLightTimeOn > 1200) { + troubleBlink = false; + if (!memoryBlink && !armedBlink && !bypassBlink) lightBlink = false; + } + } + + // Checks for beep status + static unsigned long beepTimeOn = 0; + static unsigned long beepTimeOff = 0; + if (beep) { + beepTimeOn = millis(); + } + else if (millis() - beepTimeOn > 500) { + beepTimeOff = millis(); + } + + // Armed status + static bool armedStayTriggered; + if (armedBit) { + armed[0] = true; + exitDelayArmed = true; + if (bypassLight || armedBypassBit) { + armedStay[0] = true; + armedStayTriggered = true; + armedAway[0] = false; + } + else if (armedStayTriggered) { + if (!beep && !alarmBit && millis() - beepTimeOff > 2000) { + armedStay[0] = false; + armedAway[0] = true; + } + } + else { + armedStay[0] = false; + armedAway[0] = true; + } + + if (armedBlink) { + noEntryDelay[0] = true; + exitState[0] = DSC_EXIT_NO_ENTRY_DELAY; + } + + // Reset ready status + processReadyStatus(false); + } + else { + armedStayTriggered = false; + processArmedStatus(false); + processAlarmStatus(false); + } + + if (armed[0] != previousArmed || armedStay[0] != previousArmedStay || armedAway[0] != previousArmedAway) { + previousArmed = armed[0]; + previousArmedStay = armedStay[0]; + previousArmedAway = armedAway[0]; + armedChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } + + // Ready status + if (readyLight && !armedBit) { + processReadyStatus(true); + processArmedStatus(false); + processAlarmStatus(false); + exitDelayArmed = false; + previousAlarmTriggered = false; + starKeyDetected = false; + if (!armedBlink) noEntryDelay[0] = false; + + if (armedLight) { + processExitDelayStatus(true); + exitDelayTriggered = true; + if (exitState[0] != DSC_EXIT_NO_ENTRY_DELAY) { + if (bypassLight) exitState[0] = DSC_EXIT_STAY; + else exitState[0] = DSC_EXIT_AWAY; + if (exitState[0] != previousExitState) { + previousExitState = exitState[0]; + exitDelayChanged[0] = true; + exitStateChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } + } + } + + // Reset armed status + else if (!exitDelayArmed && !armedBlink && millis() - armedLightTimeOn > 600) { + processExitDelayStatus(false); + exitState[0] = 0; + } + } + + else { + if (panelData[0]) processReadyStatus(false); + + if (exitDelayArmed && !armedBit) { + processReadyStatus(false); + exitDelayArmed = false; + } + if (exitDelay[0] && armedBit) { + processExitDelayStatus(false); + } + } + + // Zones status + byte zonesChanged; + if (!previousAlarmTriggered && !memoryBlink && !bypassBlink && !troubleBlink && !starKeyDetected) { + for (byte bit = 7; bit <= 7; bit--) { + if ((!bitRead(zonesTriggered, bit) && !alarmBit) || exitDelay[0]) { + if (bitRead(panelData[0], bit)) { + bitWrite(openZones[0], 7 - bit, 1); + } + else bitWrite(openZones[0], 7 - bit, 0); + } + } + zonesChanged = openZones[0] ^ previousOpenZones; + if (zonesChanged != 0) { + previousOpenZones = openZones[0]; + openZonesStatusChanged = true; + if (!pauseStatus) statusChanged = true; + + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(zonesChanged, zoneBit)) { + bitWrite(openZonesChanged[0], zoneBit, 1); + } + } + } + } + + // Alarm zones status + for (byte bit = 7; bit > 1; bit--) { + if (bitRead(pc16Data[0], bit)) { + bitWrite(alarmZones[0], 7 - bit, 1); + bitWrite(zonesTriggered, 7 - bit, 1); + } + else bitWrite(alarmZones[0], 7 - bit, 0); + } + zonesChanged = alarmZones[0] ^ previousAlarmZones; + if (zonesChanged != 0) { + previousAlarmZones = alarmZones[0]; + alarmZonesStatusChanged = true; + if (!pauseStatus) statusChanged = true; + + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(zonesChanged, zoneBit)) { + bitWrite(alarmZonesChanged[0], zoneBit, 1); + + // Handles zones closed during an alarm + if (alarmBit) { + if (bitRead(alarmZones[0], zoneBit)) { + bitWrite(openZones[0], zoneBit, 1); + bitWrite(openZonesChanged[0], zoneBit, 1); + openZonesStatusChanged = true; + } + else { + bitWrite(openZones[0], zoneBit, 0); + bitWrite(openZonesChanged[0], zoneBit, 1); + openZonesStatusChanged = true; + } + previousOpenZones = openZones[0]; + } + } + } + } + + // Alarm status - requires PGM output section 24 configured to option 08: Strobe Output + if ((panelData[1] & 0xFE) != 0) { + if (alarmBit && !memoryBlink) { + processReadyStatus(false); + processAlarmStatus(true); + alarmTriggered = true; + } + else if (!memoryBlink && !armedChanged[0]) { + processAlarmStatus(false); + if (alarmTriggered) { + alarmTriggered = false; + previousAlarmTriggered = true; + } + } + } + + // Trouble status + if (troubleBit) trouble = true; + else trouble = false; + if (trouble != previousTrouble) { + previousTrouble = trouble; + troubleChanged = true; + if (!pauseStatus) statusChanged = true; + } + + // Fire status + if (bitRead(pc16Data[0], 0)) fire[0] = true; + else fire[0] = false; + if (fire[0] != previousFire) { + previousFire = fire[0]; + fireChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } + + // Keypad Fire alarm + if (bitRead(pc16Data[1], 1)) { + static unsigned long previousFireAlarm = 0; + if (millis() - previousFireAlarm > 1000) { + keypadFireAlarm = true; + previousFireAlarm = millis(); + if (!pauseStatus) statusChanged = true; + } + } + + // Keypad Aux alarm + if (bitRead(pc16Data[1], 2)) { + static unsigned long previousAuxAlarm = 0; + if (millis() - previousAuxAlarm > 1000) { + keypadAuxAlarm = true; + previousAuxAlarm = millis(); + if (!pauseStatus) statusChanged = true; + } + } + + // Keypad Panic alarm + if (bitRead(pc16Data[1], 3)) { + static unsigned long previousPanicAlarm = 0; + if (millis() - previousPanicAlarm > 1000) { + keypadPanicAlarm = true; + previousPanicAlarm = millis(); + if (!pauseStatus) statusChanged = true; + } + } + + // Status - sets the status to match PowerSeries status codes for sketch compatibility + if (memoryBlink && bypassBlink && troubleBlink) { + status[0] = 0xE4; // Programming + } + else { + if (readyChanged[0]) { + if (ready[0]) status[0] = 0x01; // Partition ready + else if (openZonesStatusChanged && openZones[0]) status[0] = 0x03; // Zones open + } + + if (armedChanged[0]) { + if (armed[0]) { + if (armedAway[0]) status[0] = 0x05; // Armed away + else if (armedStay[0]) status[0] = 0x04; // Armed stay + + if (noEntryDelay[0]) status[0] = 0x06; // Armed with no entry delay + } + else status[0] = 0x3E; // Disarmed + } + + if (alarmChanged[0]) { + if (alarm[0]) status[0] = 0x11; // Alarm + else if (!armedChanged[0]) status[0] = 0x3E; // Disarmed + } + + if (exitDelayChanged[0]) { + if (exitDelay[0]) status[0] = 0x08; // Exit delay in progress + else if (!armed[0]) status[0] = 0x3E; // Disarmed + } + + if (status[0] == 0x3E) { + if (ready[0]) status[0] = 0x01; + else if (openZones[0]) status[0] = 0x03; + } + } + + if (status[0] != previousStatus) { + previousStatus = status[0]; + if (!pauseStatus) statusChanged = true; + } +} + + +void dscClassicInterface::processReadyStatus(bool status) { + ready[0] = status; + if (ready[0] != previousReady) { + previousReady = ready[0]; + readyChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } +} + + +void dscClassicInterface::processAlarmStatus(bool status) { + alarm[0] = status; + if (alarm[0] != previousAlarm) { + previousAlarm = alarm[0]; + alarmChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } +} + + +void dscClassicInterface::processExitDelayStatus(bool status) { + exitDelay[0] = status; + if (exitDelay[0] != previousExitDelay) { + previousExitDelay = exitDelay[0]; + exitDelayChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } +} + + +void dscClassicInterface::processArmedStatus(bool armedStatus) { + armedStay[0] = armedStatus; + armedAway[0] = armedStatus; + armed[0] = armedStatus; + + if (armed[0] != previousArmed) { + previousArmed = armed[0]; + armedChanged[0] = true; + if (!pauseStatus) statusChanged = true; + } +} + + +bool dscClassicInterface::handleModule() { + if (!moduleDataCaptured) return false; + moduleDataCaptured = false; + + if (moduleBitCount < 8) return false; + + return true; +} + +// Sets up writes for a single key +void dscClassicInterface::write(const char receivedKey) { + + // Blocks if a previous write is in progress + while(writeKeyPending || writeKeysPending) { + loop(); + #if defined(ESP8266) + yield(); + #endif + } + + if (strlen(accessCodeStay) < 4) { + setWriteKey(receivedKey); + } + else { + switch(receivedKey) { + case 's': + case 'S': write(accessCodeStay); break; + case 'w': + case 'W': write(accessCodeAway); break; + case 'n': + case 'N': write(accessCodeNight); break; + default: setWriteKey(receivedKey); + } + } +} + + +// Sets up writes for multiple keys sent as a char array +void dscClassicInterface::write(const char *receivedKeys, bool blockingWrite) { + + // Blocks if a previous write is in progress + while(writeKeyPending || writeKeysPending) { + loop(); + #if defined(ESP8266) + yield(); + #endif + } + + if (strlen(receivedKeys) == 1) { + write(receivedKeys[0]); + return; + } + + writeKeysArray = receivedKeys; + + if (writeKeysArray[0] != '\0') { + writeKeysPending = true; + writeReady = false; + } + + // Optionally blocks until the write is complete, necessary if the received keys char array is ephemeral + if (blockingWrite) { + while (writeKeysPending) { + writeKeys(writeKeysArray); + loop(); + #if defined(ESP8266) + yield(); + #endif + } + } + else writeKeys(writeKeysArray); +} + + +// Writes multiple keys from a char array +void dscClassicInterface::writeKeys(const char *writeKeysArray) { + static byte writeCounter = 0; + + if (!writeKeyPending && writeKeysPending && writeCounter < strlen(writeKeysArray)) { + if (writeKeysArray[writeCounter] != '\0') { + setWriteKey(writeKeysArray[writeCounter]); + writeCounter++; + if (writeKeysArray[writeCounter] == '\0') { + writeKeysPending = false; + writeCounter = 0; + } + } + } +} + + +// Specifies the key value to be written by dscClockInterrupt() and selects the write partition. This includes a 500ms +// delay after alarm keys to resolve errors when additional keys are sent immediately after alarm keys. +void dscClassicInterface::setWriteKey(const char receivedKey) { + static unsigned long previousTime; + + + // Sets the binary to write for virtual keypad keys + if (!writeKeyPending && (millis() - previousTime > 500 || millis() <= 500)) { + bool validKey = true; + + // Sets binary for virtual keypad keys + switch (receivedKey) { + case '0': writeKey = 0xD7; break; + case '1': writeKey = 0xBE; break; + case '2': writeKey = 0xDE; break; + case '3': writeKey = 0xEE; break; + case '4': writeKey = 0xBD; break; + case '5': writeKey = 0xDD; break; + case '6': writeKey = 0xED; break; + case '7': writeKey = 0xBB; break; + case '8': writeKey = 0xDB; break; + case '9': writeKey = 0xEB; break; + case '*': writeKey = 0xB7; break; + case '#': writeKey = 0xE7; break; + case 'F': + case 'f': writeKey = 0x3F; writeAlarm = true; break; // Keypad fire alarm + case 'A': + case 'a': writeKey = 0x5F; writeAlarm = true; break; // Keypad auxiliary alarm + case 'P': + case 'p': writeKey = 0x6F; writeAlarm = true; break; // Keypad panic alarm + default: { + validKey = false; + break; + } + } + + if (writeAlarm) previousTime = millis(); // Sets a marker to time writes after keypad alarm keys + if (validKey) { + writeKeyPending = true; // Sets a flag indicating that a write is pending, cleared by dscClockInterrupt() + writeReady = false; + } + } +} + + +/* + * printPanelMessage() checks the first byte of a message from the + * panel (panelData[0]) to process known commands. + * + * Structure decoding status refers to whether all bits of the message have + * a known purpose. + * + * Content decoding status refers to whether all values of the message are known. + */ +void dscClassicInterface::printPanelMessage() { + + stream->print(F("Lights: ")); + if (panelData[1]) { + if (bitRead(panelData[1], 7)) stream->print(F("Ready ")); + if (bitRead(panelData[1], 6)) stream->print(F("Armed ")); + if (bitRead(panelData[1], 5)) stream->print(F("Memory ")); + if (bitRead(panelData[1], 4)) stream->print(F("Bypass ")); + if (bitRead(panelData[1], 3)) stream->print(F("Trouble ")); + if (bitRead(panelData[1], 0)) stream->print(F("Beep ")); + } + else stream->print(F("none ")); + + stream->print(F("| Status: ")); + if (pc16Data[1]) { + if (bitRead(pc16Data[1], 7)) stream->print(F("Trouble ")); + if (bitRead(pc16Data[1], 6)) stream->print(F("Armed with bypassed zones ")); + if (bitRead(pc16Data[1], 5)) stream->print(F("Armed ")); + if (bitRead(pc16Data[1], 3)) stream->print(F("Keypad Panic alarm ")); + if (bitRead(pc16Data[1], 2)) stream->print(F("Keypad Aux alarm ")); + if (bitRead(pc16Data[1], 1)) stream->print(F("Keypad Fire alarm ")); + if (bitRead(pc16Data[1], 0)) stream->print(F("Alarm ")); + } + else stream->print(F("none ")); + + stream->print(F("| Zones open: ")); + if (panelData[0] == 0) stream->print(F("none ")); + else { + for (byte bit = 7; bit <= 7; bit--) { + if (bitRead(panelData[0], bit)) { + stream->print(8 - bit); + stream->print(F(" ")); + } + } + } + + if (pc16Data[0] & 0xFE) { + stream->print(F("| Zone alarm: ")); + for (byte bit = 7; bit > 1; bit--) { + if (bitRead(pc16Data[0], bit)) { + stream->print(8 - bit); + stream->print(F(" ")); + } + } + } + + if (bitRead(pc16Data[0], 0)) { + stream->print(F("| Fire alarm")); + } +} + + + +// Processes keypad and module notifications and responses to panel queries +void dscClassicInterface::printModuleMessage() { + + stream->print(F("[Keypad] ")); + if (hideKeypadDigits) { + switch (moduleData[0]) { + case 0xBE: + case 0xDE: + case 0xEE: + case 0xBD: + case 0xDD: + case 0xED: + case 0xBB: + case 0xDB: + case 0xEB: + case 0xD7: Serial.print("[Digit]"); break; + case 0xB7: Serial.print("*"); break; + case 0xE7: Serial.print("#"); break; + case 0x3F: Serial.print(F("Fire alarm")); break; + case 0x5F: Serial.print(F("Aux alarm")); break; + case 0x6F: Serial.print(F("Panic alarm")); break; + } + } + + else { + switch (moduleData[0]) { + case 0xBE: Serial.print("1"); break; + case 0xDE: Serial.print("2"); break; + case 0xEE: Serial.print("3"); break; + case 0xBD: Serial.print("4"); break; + case 0xDD: Serial.print("5"); break; + case 0xED: Serial.print("6"); break; + case 0xBB: Serial.print("7"); break; + case 0xDB: Serial.print("8"); break; + case 0xEB: Serial.print("9"); break; + case 0xD7: Serial.print("0"); break; + case 0xB7: Serial.print("*"); break; + case 0xE7: Serial.print("#"); break; + case 0x3F: Serial.print(F("Fire alarm")); break; + case 0x5F: Serial.print(F("Aux alarm")); break; + case 0x6F: Serial.print(F("Panic alarm")); break; + } + } +} + + +// Prints the panel message as binary with an optional parameter to print spaces between bytes +void dscClassicInterface::printPanelBinary(bool printSpaces) { + for (byte panelByte = 0; panelByte < panelByteCount; panelByte++) { + for (byte mask = 0x80; mask; mask >>= 1) { + if (mask & panelData[panelByte]) stream->print("1"); + else stream->print("0"); + } + if (printSpaces && panelByte != panelByteCount - 1) stream->print(" "); + } + + if (printSpaces) stream->print(" "); + + for (byte panelByte = 0; panelByte < panelByteCount; panelByte++) { + for (byte mask = 0x80; mask; mask >>= 1) { + if (mask & pc16Data[panelByte]) stream->print("1"); + else stream->print("0"); + } + if (printSpaces && panelByte != panelByteCount - 1) stream->print(" "); + } +} + + +// Prints the panel message as binary with an optional parameter to print spaces between bytes +void dscClassicInterface::printModuleBinary(bool printSpaces) { + bool keypadDigit = false; + if (hideKeypadDigits) { + switch (moduleData[0]) { + case 0xBE: + case 0xDE: + case 0xEE: + case 0xBD: + case 0xDD: + case 0xED: + case 0xBB: + case 0xDB: + case 0xEB: keypadDigit = true; break; + default: keypadDigit = false; + } + } + + for (byte moduleByte = 0; moduleByte < moduleByteCount; moduleByte++) { + if (hideKeypadDigits && keypadDigit && moduleByte == 0) stream->print("........"); + else { + for (byte mask = 0x80; mask; mask >>= 1) { + if (mask & moduleData[moduleByte]) stream->print("1"); + else stream->print("0"); + } + } + if (printSpaces && moduleByte != panelByteCount - 1) stream->print(" "); + } +} + + +// Prints the panel command as hex +void dscClassicInterface::printPanelCommand() { + stream->print(F("Panel")); +} + + +#if defined(__AVR__) +bool dscClassicInterface::redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes) { +#elif defined(ESP8266) +bool ICACHE_RAM_ATTR dscClassicInterface::redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes) { +#elif defined(ESP32) +bool IRAM_ATTR dscClassicInterface::redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes) { +#endif + + bool redundantData = true; + for (byte i = 0; i < checkedBytes; i++) { + if (previousCmd[i] != currentCmd[i]) { + redundantData = false; + break; + } + } + if (redundantData) return true; + else { + for (byte i = 0; i < dscReadSize; i++) previousCmd[i] = currentCmd[i]; + return false; + } +} + + +// Called as an interrupt when the DSC clock changes to write data for virtual keypad and setup timers to read +// data after an interval. +#if defined(__AVR__) +void dscClassicInterface::dscClockInterrupt() { +#elif defined(ESP8266) +void ICACHE_RAM_ATTR dscClassicInterface::dscClockInterrupt() { +#elif defined(ESP32) +void IRAM_ATTR dscClassicInterface::dscClockInterrupt() { +#endif + + // Data sent from the panel and keypads/modules has latency after a clock change (observed up to 160us for + // keypad data). The following sets up a timer for each platform that will call dscDataInterrupt() in + // 250us to read the data line. + + // AVR Timer1 calls dscDataInterrupt() via ISR(TIMER1_OVF_vect) when the Timer1 counter overflows + #if defined(__AVR__) + TCNT1=61535; // Timer1 counter start value, overflows at 65535 in 250us + TCCR1B |= (1 << CS10); // Sets the prescaler to 1 + + // esp8266 timer1 calls dscDataInterrupt() in 250us + #elif defined(ESP8266) + timer1_write(1250); + + // esp32 timer1 calls dscDataInterrupt() in 250us + #elif defined(ESP32) + timerStart(timer0); + portENTER_CRITICAL(&timer0Mux); + #endif + + static unsigned long previousClockHighTime; + if (digitalRead(dscClockPin) == HIGH) { + if (virtualKeypad) digitalWrite(dscWritePin, LOW); // Restores the data line after a virtual keypad write + previousClockHighTime = micros(); + } + + else { + clockHighTime = micros() - previousClockHighTime; // Tracks the clock high time to find the reset between commands + + // Virtual keypad + if (virtualKeypad) { + static bool writeStart = false; + + if (writeKeyPending && millis() - writeCompleteTime > 50) { + writeKeyWait = false; + } + + // Writes a regular key + if (writeKeyPending && !writeKeyWait) { + + if (clockHighTime > 2000) { + if (!((writeKey >> 7) & 0x01)) digitalWrite(dscWritePin, HIGH); + writeStart = true; + } + else if (writeStart && isrPanelBitTotal <= 7) { + if (!((writeKey >> (7 - isrPanelBitCount)) & 0x01)) digitalWrite(dscWritePin, HIGH); + if (isrPanelBitTotal == 7) { + writeKeyPending = false; + writeKeyWait = true; + writeCompleteTime = millis(); + writeStart = false; + } + } + } + } + } + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #endif +} + + +// Interrupt function called by AVR Timer1, esp8266 timer1, and esp32 timer1 after 250us to read the data line +#if defined(__AVR__) +void dscClassicInterface::dscDataInterrupt() { +#elif defined(ESP8266) +void ICACHE_RAM_ATTR dscClassicInterface::dscDataInterrupt() { +#elif defined(ESP32) +void IRAM_ATTR dscClassicInterface::dscDataInterrupt() { + timerStop(timer0); + portENTER_CRITICAL(&timer0Mux); +#endif + + static bool skipData = false; + + // Panel sends data while the clock is high + if (digitalRead(dscClockPin) == HIGH) { + + // Stops processing Keybus data at the dscReadSize limit + if (isrPanelByteCount >= dscReadSize) skipData = true; + + else { + if (isrPanelBitCount < 8) { + // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 + isrPanelData[isrPanelByteCount] <<= 1; + isrPC16Data[isrPanelByteCount] <<= 1; + + if (digitalRead(dscReadPin) == HIGH) isrPanelData[isrPanelByteCount] |= 1; + if (digitalRead(dscPC16Pin) == HIGH) isrPC16Data[isrPanelByteCount] |= 1; + } + + // Increments the bit counter if the byte is incomplete + if (isrPanelBitCount < 7) isrPanelBitCount++; + + // Byte is complete, set the counters for the next byte + else { + isrPanelBitCount = 0; + isrPanelByteCount++; + } + + isrPanelBitTotal++; + } + } + + // Keypads and modules send data while the clock is low + else { + static bool moduleDataDetected = false; + + // Saves data and resets counters after the clock cycle is complete (high for at least 1ms) + if (clockHighTime > 2000) { + keybusTime = millis(); + + // Skips incomplete data and redundant data + if (isrPanelBitTotal < 8) skipData = true; + else { + static byte previousPanelData[dscReadSize]; + static byte previousPC16Data[dscReadSize]; + + if (lightBlink && readyLight) skipData = false; + else if (redundantPanelData(previousPanelData, isrPanelData, isrPanelByteCount) && + redundantPanelData(previousPC16Data, isrPC16Data, isrPanelByteCount)) { + skipData = true; + } + } + + // Stores new panel data in the panel buffer + if (panelBufferLength == dscBufferSize) bufferOverflow = true; + else if (!skipData && panelBufferLength < dscBufferSize) { + for (byte i = 0; i < dscReadSize; i++) { + panelBuffer[panelBufferLength][i] = isrPanelData[i]; + pc16Buffer[panelBufferLength][i] = isrPC16Data[i]; + } + panelBufferBitCount[panelBufferLength] = isrPanelBitTotal; + panelBufferByteCount[panelBufferLength] = isrPanelByteCount; + panelBufferLength++; + } + + // Stores new keypad and module data - this data is not buffered + if (processModuleData) { + if (moduleDataDetected) { + moduleDataDetected = false; + moduleDataCaptured = true; // Sets a flag for handleModule() + for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; + moduleBitCount = isrModuleBitTotal; + moduleByteCount = isrModuleByteCount; + } + + // Resets the keypad and module capture data and counters + for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; + isrModuleBitTotal = 0; + isrModuleBitCount = 0; + isrModuleByteCount = 0; + } + + // Resets the panel capture data and counters + for (byte i = 0; i < dscReadSize; i++) { + isrPanelData[i] = 0; + isrPC16Data[i] = 0; + } + isrPanelBitTotal = 0; + isrPanelBitCount = 0; + isrPanelByteCount = 0; + skipData = false; + } + + // Keypad and module data is not buffered and skipped if the panel data buffer is filling + if (processModuleData && isrModuleByteCount < dscReadSize && panelBufferLength <= 1) { + + // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 + if (isrModuleBitCount < 8) { + isrModuleData[isrModuleByteCount] <<= 1; + if (digitalRead(dscReadPin) == HIGH) { + isrModuleData[isrModuleByteCount] |= 1; + } + else moduleDataDetected = true; // Keypads and modules send data by pulling the data line low + } + + // Byte is complete, set the counters for the next byte + if (isrModuleBitCount == 7) { + isrModuleBitCount = 0; + isrModuleByteCount++; + if (moduleDataDetected && isrModuleData[0] == 0xB7) { + starKeyDetected = true; + } + } + + // Increments the bit counter if the byte is incomplete + else if (isrModuleBitCount < 7) { + isrModuleBitCount++; + } + + isrModuleBitTotal++; + } + + } + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #endif +} diff --git a/src/dscClassic.h b/src/dscClassic.h new file mode 100644 index 0000000..5736f9e --- /dev/null +++ b/src/dscClassic.h @@ -0,0 +1,208 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef dscClassic_h +#define dscClassic_h + +#include + +const byte dscPartitions = 1; // Maximum number of partitions - requires 19 bytes of memory per partition +const byte dscZones = 1; // Maximum number of zone groups, 8 zones per group - requires 6 bytes of memory per zone group +const byte dscReadSize = 2; // Maximum bytes of a Keybus command + +#if defined(__AVR__) +const byte dscBufferSize = 10; // Number of commands to buffer if the sketch is busy - requires dscReadSize + 2 bytes of memory per command +#elif defined(ESP8266) || defined(ESP32) +const byte dscBufferSize = 50; +#endif + +// Exit delay target states +#define DSC_EXIT_STAY 1 +#define DSC_EXIT_AWAY 2 +#define DSC_EXIT_NO_ENTRY_DELAY 3 + +class dscClassicInterface { + + public: + + // Initializes writes as disabled by default + dscClassicInterface(byte setClockPin, byte setReadPin, byte setPC16Pin, byte setWritePin = 255, const char * setAccessCode = ""); + + // Interface control + void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default + bool loop(); // Returns true if valid panel data is available + void stop(); // Disables the clock hardware interrupt and data timer interrupt + void resetStatus(); // Resets the state of all status components as changed for sketches to get the current status + + // Writes a single key - nonblocking unless a previous write is in progress + void write(const char receivedKey); + + // Writes multiple keys from a char array + // + // By default, this is nonblocking unless there is a previous write in progress - in this case, the sketch must keep the char + // array defined at least until the write is complete. + // + // If the char array is ephemeral, check if the write is complete by checking writeReady or set blockingWrite to true to + // block until the write is complete. + void write(const char * receivedKeys, bool blockingWrite = false); + + // Write control + static byte writePartition; // Set to a partition number for virtual keypad + bool writeReady; // True if the library is ready to write a key + + // Prints output to the stream interface set in begin() + void printPanelBinary(bool printSpaces = true); // Includes spaces between bytes by default + void printPanelCommand(); // Prints the panel command as hex + void printPanelMessage(); // Prints the decoded panel message + void printModuleBinary(bool printSpaces = true); // Includes spaces between bytes by default + void printModuleMessage(); // Prints the decoded keypad or module message + + // These can be configured in the sketch setup() before begin() + bool hideKeypadDigits; // Controls if keypad digits are hidden for publicly posted logs (default: false) + static bool processModuleData; // Controls if keypad and module data is processed and displayed (default: false) + + // Status tracking + bool statusChanged; // True after any status change + bool pauseStatus; // Prevent status from showing as changed, set in sketch to control when to update status + bool keybusConnected, keybusChanged; // True if data is detected on the Keybus + bool trouble, troubleChanged; + bool keypadFireAlarm, keypadAuxAlarm, keypadPanicAlarm; + bool ready[dscPartitions], readyChanged[dscPartitions]; + bool armed[dscPartitions], armedAway[dscPartitions], armedStay[dscPartitions]; + bool noEntryDelay[dscPartitions], armedChanged[dscPartitions]; + bool alarm[dscPartitions], alarmChanged[dscPartitions]; + bool exitDelay[dscPartitions], exitDelayChanged[dscPartitions]; + byte exitState[dscPartitions], exitStateChanged[dscPartitions]; + bool fire[dscPartitions], fireChanged[dscPartitions]; + bool openZonesStatusChanged; + byte openZones[dscZones], openZonesChanged[dscZones]; // Zone status is stored in an array using 1 bit per zone, up to 64 zones + bool alarmZonesStatusChanged; + byte alarmZones[dscZones], alarmZonesChanged[dscZones]; // Zone alarm status is stored in an array using 1 bit per zone, up to 64 zones + bool pgmOutputsStatusChanged; + byte pgmOutputs[1], pgmOutputsChanged[1]; + bool armedLight, memoryLight, bypassLight, troubleLight, beep; + static volatile bool readyLight, lightBlink; + bool readyBlink, armedBlink, memoryBlink, bypassBlink, troubleBlink; + + /* panelData[], pc16Data[], and moduleData[] store panel and keypad/module data in an array. These can + * be accessed directly in the sketch to get data that is not already tracked in the library. See + * dscClassic.cpp for the currently known DSC commands and data. + * + * panelData[] example: + * Byte 0 Byte 2 Byte 3 Byte 4 Byte 5 + * 00000101 0 10000001 00000001 10010001 11000111 [0x05] Partition 1: Ready Backlight - Partition ready | Partition 2: disabled + * ^ Byte 1 (stop bit) + */ + static byte panelData[dscReadSize]; + static byte pc16Data[dscReadSize]; + static volatile byte moduleData[dscReadSize]; + + // status[] and lights[] store the current status message and LED state. These can be accessed directly in the + // sketch to get data that is not already tracked in the library. See printPanelMessages() and + // printPanelLights() in dscClassic.cpp to see how this data translates to the status message and LED status. + byte status[dscPartitions]; + byte lights[dscPartitions]; + + // Process keypad and module data, returns true if data is available + bool handleModule(); + + // True if dscBufferSize needs to be increased + static volatile bool bufferOverflow; + + // Timer interrupt function to capture data - declared as public for use by AVR Timer2 + static void dscDataInterrupt(); + + // Sketch cross-compatibility - these elements are not currently used for the Classic series + byte accessCode[dscPartitions]; + bool accessCodeChanged[dscPartitions]; + bool accessCodePrompt; + bool decimalInput; + bool powerTrouble, powerChanged; + bool batteryTrouble, batteryChanged; + bool disabled[dscPartitions], disabledChanged[dscPartitions]; + bool entryDelay[dscPartitions], entryDelayChanged[dscPartitions]; + byte panelVersion; + bool displayTrailingBits; + bool timestampChanged; + byte hour, minute, day, month; + int year; + bool setTime(unsigned int year, byte month, byte day, byte hour, byte minute, const char* accessCode, byte timePartition = 1); + + private: + + void processPanelStatus(); + void processReadyStatus(bool status); + void processArmed(bool status); + void processArmedStatus(bool status); + void processAlarmStatus(bool status); + void processExitDelayStatus(bool status); + void writeKeys(const char * writeKeysArray); + void setWriteKey(const char receivedKey); + static void dscClockInterrupt(); + static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); + + #if defined(ESP32) + static hw_timer_t * timer0; + static portMUX_TYPE timer0Mux; + #endif + + Stream* stream; + const char * writeKeysArray; + const char * accessCodeStay; + char accessCodeAway[7]; + char accessCodeNight[7]; + bool writeKeysPending; + bool writeArm; + bool previousTrouble; + bool previousKeybus; + byte previousLights, previousStatus; + bool previousReady; + bool previousExitDelay, previousEntryDelay, exitDelayArmed, exitDelayTriggered; + byte previousExitState; + bool previousArmed, previousArmedStay, previousArmedAway; + bool previousAlarm; + bool alarmTriggered, previousAlarmTriggered; + byte zonesTriggered; + bool previousFire; + byte previousOpenZones, previousAlarmZones; + byte previousPgmOutput; + bool troubleBit, armedBypassBit, armedBit, alarmBit; + + static byte dscClockPin; + static byte dscReadPin; + static byte dscPC16Pin; + static byte dscWritePin; + static byte writeByte, writeBit; + static bool virtualKeypad; + static char writeKey; + static byte panelBitCount, panelByteCount; + static volatile bool writeKeyPending, writeKeyWait; + static volatile bool writeAlarm, starKeyDetected, starKeyCheck, starKeyWait; + static volatile bool moduleDataCaptured; + static volatile unsigned long clockHighTime, keybusTime, writeCompleteTime; + static volatile byte panelBufferLength; + static volatile byte panelBuffer[dscBufferSize][dscReadSize], pc16Buffer[dscBufferSize][dscReadSize]; + static volatile byte panelBufferBitCount[dscBufferSize], panelBufferByteCount[dscBufferSize]; + static volatile byte moduleBitCount, moduleByteCount; + static volatile byte moduleCmd; + static volatile byte isrPanelData[dscReadSize], isrPC16Data[dscReadSize], isrPanelBitTotal, isrPanelBitCount, isrPanelByteCount; + static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; +}; + +#endif // dscClassic_h \ No newline at end of file diff --git a/src/dscKeybus.h b/src/dscKeybus.h new file mode 100644 index 0000000..5c76183 --- /dev/null +++ b/src/dscKeybus.h @@ -0,0 +1,342 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef dscKeybus_h +#define dscKeybus_h + +#include + +#if defined(__AVR__) +const byte dscPartitions = 4; // Maximum number of partitions - requires 19 bytes of memory per partition +const byte dscZones = 4; // Maximum number of zone groups, 8 zones per group - requires 6 bytes of memory per zone group +const byte dscBufferSize = 10; // Number of commands to buffer if the sketch is busy - requires dscReadSize + 2 bytes of memory per command +const byte dscReadSize = 16; // Maximum bytes of a Keybus command +#elif defined(ESP8266) +const byte dscPartitions = 8; +const byte dscZones = 8; +const byte dscBufferSize = 50; +const byte dscReadSize = 16; +#elif defined(ESP32) +const byte dscPartitions = 8; +const byte dscZones = 8; +const DRAM_ATTR byte dscBufferSize = 50; +const DRAM_ATTR byte dscReadSize = 16; +#endif + +// Exit delay target states +#define DSC_EXIT_STAY 1 +#define DSC_EXIT_AWAY 2 +#define DSC_EXIT_NO_ENTRY_DELAY 3 + + +class dscKeybusInterface { + + public: + + // Initializes writes as disabled by default + dscKeybusInterface(byte setClockPin, byte setReadPin, byte setWritePin = 255); + + // Interface control + void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default + bool loop(); // Returns true if valid panel data is available + void stop(); // Disables the clock hardware interrupt and data timer interrupt + void resetStatus(); // Resets the state of all status components as changed for sketches to get the current status + + // Writes a single key - nonblocking unless a previous write is in progress + void write(const char receivedKey); + + // Writes multiple keys from a char array + // + // By default, this is nonblocking unless there is a previous write in progress - in this case, the sketch must keep the char + // array defined at least until the write is complete. + // + // If the char array is ephemeral, check if the write is complete by checking writeReady or set blockingWrite to true to + // block until the write is complete. + void write(const char * receivedKeys, bool blockingWrite = false); + + // Write control + static byte writePartition; // Set to a partition number for virtual keypad + bool writeReady; // True if the library is ready to write a key + + // Prints output to the stream interface set in begin() + void printPanelBinary(bool printSpaces = true); // Includes spaces between bytes by default + void printPanelCommand(); // Prints the panel command as hex + void printPanelMessage(); // Prints the decoded panel message + void printModuleBinary(bool printSpaces = true); // Includes spaces between bytes by default + void printModuleMessage(); // Prints the decoded keypad or module message + + + // These can be configured in the sketch setup() before begin() + bool hideKeypadDigits; // Controls if keypad digits are hidden for publicly posted logs (default: false) + static bool processModuleData; // Controls if keypad and module data is processed and displayed (default: false) + bool displayTrailingBits; // Controls if bits read as the clock is reset are displayed, appears to be spurious data (default: false) + + // Panel time + bool timestampChanged; // True after the panel sends a timestamped message + byte hour, minute, day, month; + int year; + + // Sets panel time, the year can be sent as either 2 or 4 digits, returns true if the panel is ready to set the time + bool setTime(unsigned int year, byte month, byte day, byte hour, byte minute, const char* accessCode, byte timePartition = 1); + + // Status tracking + bool statusChanged; // True after any status change + bool pauseStatus; // Prevent status from showing as changed, set in sketch to control when to update status + bool keybusConnected, keybusChanged; // True if data is detected on the Keybus + byte accessCode[dscPartitions]; + bool accessCodeChanged[dscPartitions]; + bool accessCodePrompt; // True if the panel is requesting an access code + bool decimalInput; // True if the panel is requesting 3 digit input (for 0x6E readout) + bool trouble, troubleChanged; + bool powerTrouble, powerChanged; + bool batteryTrouble, batteryChanged; + bool keypadFireAlarm, keypadAuxAlarm, keypadPanicAlarm; + bool ready[dscPartitions], readyChanged[dscPartitions]; + bool disabled[dscPartitions], disabledChanged[dscPartitions]; + bool armed[dscPartitions], armedAway[dscPartitions], armedStay[dscPartitions]; + bool noEntryDelay[dscPartitions], armedChanged[dscPartitions]; + bool alarm[dscPartitions], alarmChanged[dscPartitions]; + bool exitDelay[dscPartitions], exitDelayChanged[dscPartitions]; + byte exitState[dscPartitions], exitStateChanged[dscPartitions]; + bool entryDelay[dscPartitions], entryDelayChanged[dscPartitions]; + bool fire[dscPartitions], fireChanged[dscPartitions]; + bool openZonesStatusChanged; + byte openZones[dscZones], openZonesChanged[dscZones]; // Zone status is stored in an array using 1 bit per zone, up to 64 zones + bool alarmZonesStatusChanged; + byte alarmZones[dscZones], alarmZonesChanged[dscZones]; // Zone alarm status is stored in an array using 1 bit per zone, up to 64 zones + bool pgmOutputsStatusChanged; + byte pgmOutputs[2], pgmOutputsChanged[2]; + byte panelVersion; + + /* panelData[] and moduleData[] store panel and keypad/module data in an array: command [0], stop bit by itself [1], + * followed by the remaining data. These can be accessed directly in the sketch to get data that is not already + * tracked in the library. See dscKeybusPrintData.cpp for the currently known DSC commands and data. + * + * panelData[] example: + * Byte 0 Byte 2 Byte 3 Byte 4 Byte 5 + * 00000101 0 10000001 00000001 10010001 11000111 [0x05] Partition 1: Ready Backlight - Partition ready | Partition 2: disabled + * ^ Byte 1 (stop bit) + */ + static byte panelData[dscReadSize]; + static volatile byte moduleData[dscReadSize]; + + // status[] and lights[] store the current status message and LED state for each partition. These can be accessed + // directly in the sketch to get data that is not already tracked in the library. See printPanelMessages() and + // printPanelLights() in dscKeybusPrintData.cpp to see how this data translates to the status message and LED status. + byte status[dscPartitions]; + byte lights[dscPartitions]; + + // Process keypad and module data, returns true if data is available + bool handleModule(); + + // True if dscBufferSize needs to be increased + static volatile bool bufferOverflow; + + // Timer interrupt function to capture data - declared as public for use by AVR Timer1 + static void dscDataInterrupt(); + + // Deprecated + bool handlePanel(); // Returns true if valid panel data is available. Relabeled to loop() + bool processRedundantData; // Controls if repeated periodic commands are processed and displayed (default: false) + + private: + + void processPanelStatus(); + void processPanelStatus0(byte partition, byte panelByte); + void processPanelStatus2(byte partition, byte panelByte); + void processPanelStatus4(byte partition, byte panelByte); + void processPanelStatus5(byte partition, byte panelByte); + void processPanel_0x16(); + void processPanel_0x27(); + void processPanel_0x2D(); + void processPanel_0x34(); + void processPanel_0x3E(); + void processPanel_0x87(); + void processPanel_0xA5(); + void processPanel_0xE6(); + void processPanel_0xE6_0x09(); + void processPanel_0xE6_0x0B(); + void processPanel_0xE6_0x0D(); + void processPanel_0xE6_0x0F(); + void processPanel_0xE6_0x1A(); + void processPanel_0xEB(); + void processReadyStatus(byte partitionIndex, bool status); + void processAlarmStatus(byte partitionIndex, bool status); + void processExitDelayStatus(byte partitionIndex, bool status); + void processEntryDelayStatus(byte partitionIndex, bool status); + void processZoneStatus(byte zonesByte, byte panelByte); + void processTime(byte panelByte); + void processAlarmZones(byte panelByte, byte startByte, byte zoneCountOffset, byte writeValue); + void processAlarmZonesStatus(byte zonesByte, byte zoneCount, byte writeValue); + void processArmed(byte partitionIndex, bool armedStatus); + void processPanelAccessCode(byte partitionIndex, byte dscCode, bool accessCodeIncrease = true); + + void printPanelPartitionStatus(byte startPartition, byte startByte, byte endByte); + void printPanelStatus0(byte panelByte); + void printPanelStatus1(byte panelByte); + void printPanelStatus2(byte panelByte); + void printPanelStatus3(byte panelByte); + void printPanelStatus4(byte panelByte); + void printPanelStatus5(byte panelByte); + void printPanelStatus14(byte panelByte); + void printPanelStatus16(byte panelByte); + void printPanelStatus17(byte panelByte); + void printPanelStatus18(byte panelByte); + void printPanelStatus1B(byte panelByte); + + void printPanelMessages(byte panelByte); + void printPanelLights(byte panelByte, bool printMessage = true); + void printPanelTime(byte panelByte); + void printPanelBeeps(byte panelByte); + void printPanelTone(byte panelByte); + void printPanelBuzzer(byte panelByte); + bool printPanelZones(byte inputByte, byte startZone); + void printPanelAccessCode(byte dscCode, bool accessCodeIncrease = true); + void printPanelBitNumbers(byte panelByte, byte startNumber, byte startBit = 0, byte stopBit = 7, bool printNone = true); + void printNumberSpace(byte number); + void printNumberOffset(byte panelByte, int numberOffset); + void printUnknownData(); + void printPartition(); + void printStatusLights(); + void printStatusLightsFlashing(); + void printZoneLights(bool lowerRange = true); + void printPanel_0x05(); + void printPanel_0x0A_0F(); + void printPanel_0x11(); + void printPanel_0x16(); + void printPanel_0x1B(); + void printPanel_0x1C(); + void printPanel_0x22_28_33_39(); + void printPanel_0x27(); + void printPanel_0x2D(); + void printPanel_0x34(); + void printPanel_0x3E(); + void printPanel_0x41(); + void printPanel_0x4C(); + void printPanel_0x57(); + void printPanel_0x58(); + void printPanel_0x5D_63(); + void printPanel_0x64(); + void printPanel_0x69(); + void printPanel_0x6E(); + void printPanel_0x70(); + void printPanel_0x75(); + void printPanel_0x7A(); + void printPanel_0x7F(); + void printPanel_0x82(); + void printPanel_0x87(); + void printPanel_0x8D(); + void printPanel_0x94(); + void printPanel_0x9E(); + void printPanel_0xA5(); + void printPanel_0xAA(); + void printPanel_0xB1(); + void printPanel_0xBB(); + void printPanel_0xC3(); + void printPanel_0xCE(); + void printPanel_0xD5(); + void printPanel_0xE6(); + void printPanel_0xE6_0x01_06_20_21(); + void printPanel_0xE6_0x08_0A_0C_0E(); + void printPanel_0xE6_0x09(); + void printPanel_0xE6_0x0B(); + void printPanel_0xE6_0x0D(); + void printPanel_0xE6_0x0F(); + void printPanel_0xE6_0x17(); + void printPanel_0xE6_0x18(); + void printPanel_0xE6_0x19(); + void printPanel_0xE6_0x1A(); + void printPanel_0xE6_0x1D(); + void printPanel_0xE6_0x1F(); + void printPanel_0xE6_0x2B(); + void printPanel_0xE6_0x2C(); + void printPanel_0xE6_0x41(); + void printPanel_0xEB(); + void printPanel_0xEC(); + + void printModule_0x77(); + void printModule_0xBB(); + void printModule_0xDD(); + void printModule_Status(); + void printModule_0x11(); + void printModule_0x41(); + void printModule_0x4C(); + void printModule_0x57(); + void printModule_0x58(); + void printModule_0x70(); + void printModule_0x94(); + void printModule_0xD5(); + bool printModule_Keys(); + void printModule_KeyCodes(byte keyByte); + void printModule_Expander(); + bool printModuleSlots(byte startCount, byte startByte, byte endByte, byte startMask, byte endMask, byte bitShift, byte matchValue, bool reverse = false); + + bool validCRC(); + void writeKeys(const char * writeKeysArray); + void setWriteKey(const char receivedKey); + static void dscClockInterrupt(); + static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); + + #if defined(ESP32) + static hw_timer_t * timer0; + static portMUX_TYPE timer0Mux; + #endif + + Stream* stream; + const char* writeKeysArray; + bool writeKeysPending; + bool writeArm[dscPartitions]; + bool queryResponse; + bool previousTrouble; + bool previousKeybus; + bool previousPower; + bool previousDisabled[dscPartitions]; + byte previousAccessCode[dscPartitions]; + byte previousLights[dscPartitions], previousStatus[dscPartitions]; + bool previousReady[dscPartitions]; + bool previousExitDelay[dscPartitions], previousEntryDelay[dscPartitions]; + byte previousExitState[dscPartitions]; + bool previousArmed[dscPartitions], previousArmedStay[dscPartitions]; + bool previousAlarm[dscPartitions]; + bool previousFire[dscPartitions]; + byte previousOpenZones[dscZones], previousAlarmZones[dscZones]; + byte previousPgmOutputs[2]; + bool keybusVersion1; + + static byte dscClockPin; + static byte dscReadPin; + static byte dscWritePin; + static byte writeByte, writeBit; + static bool virtualKeypad; + static char writeKey; + static byte panelBitCount, panelByteCount; + static volatile bool writeKeyPending; + static volatile bool writeAlarm, starKeyCheck, starKeyWait[dscPartitions]; + static volatile bool moduleDataCaptured; + static volatile unsigned long clockHighTime, keybusTime; + static volatile byte panelBufferLength; + static volatile byte panelBuffer[dscBufferSize][dscReadSize]; + static volatile byte panelBufferBitCount[dscBufferSize], panelBufferByteCount[dscBufferSize]; + static volatile byte moduleBitCount, moduleByteCount; + static volatile byte currentCmd, statusCmd, moduleCmd, moduleSubCmd; + static volatile byte isrPanelData[dscReadSize], isrPanelBitTotal, isrPanelBitCount, isrPanelByteCount; + static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; +}; + +#endif // dscKeybus_h \ No newline at end of file diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 631ea5f..824864f 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -17,51 +17,12 @@ along with this program. If not, see . */ -#include "dscKeybusInterface.h" - -byte dscKeybusInterface::dscClockPin; -byte dscKeybusInterface::dscReadPin; -byte dscKeybusInterface::dscWritePin; -char dscKeybusInterface::writeKey; -byte dscKeybusInterface::writePartition; -byte dscKeybusInterface::writeByte; -byte dscKeybusInterface::writeBit; -bool dscKeybusInterface::virtualKeypad; -bool dscKeybusInterface::processModuleData; -byte dscKeybusInterface::panelData[dscReadSize]; -byte dscKeybusInterface::panelByteCount; -byte dscKeybusInterface::panelBitCount; -volatile bool dscKeybusInterface::writeKeyPending; -volatile byte dscKeybusInterface::moduleData[dscReadSize]; -volatile bool dscKeybusInterface::moduleDataCaptured; -volatile byte dscKeybusInterface::moduleByteCount; -volatile byte dscKeybusInterface::moduleBitCount; -volatile bool dscKeybusInterface::writeAlarm; -volatile bool dscKeybusInterface::starKeyCheck; -volatile bool dscKeybusInterface::starKeyWait[dscPartitions]; -volatile bool dscKeybusInterface::bufferOverflow; -volatile byte dscKeybusInterface::panelBufferLength; -volatile byte dscKeybusInterface::panelBuffer[dscBufferSize][dscReadSize]; -volatile byte dscKeybusInterface::panelBufferBitCount[dscBufferSize]; -volatile byte dscKeybusInterface::panelBufferByteCount[dscBufferSize]; -volatile byte dscKeybusInterface::isrPanelData[dscReadSize]; -volatile byte dscKeybusInterface::isrPanelByteCount; -volatile byte dscKeybusInterface::isrPanelBitCount; -volatile byte dscKeybusInterface::isrPanelBitTotal; -volatile byte dscKeybusInterface::isrModuleData[dscReadSize]; -volatile byte dscKeybusInterface::isrModuleByteCount; -volatile byte dscKeybusInterface::isrModuleBitCount; -volatile byte dscKeybusInterface::isrModuleBitTotal; -volatile byte dscKeybusInterface::currentCmd; -volatile byte dscKeybusInterface::statusCmd; -volatile byte dscKeybusInterface::moduleCmd; -volatile byte dscKeybusInterface::moduleSubCmd; -volatile unsigned long dscKeybusInterface::clockHighTime; -volatile unsigned long dscKeybusInterface::keybusTime; +#include "dscKeybus.h" + #if defined(ESP32) -hw_timer_t *timer0 = NULL; -portMUX_TYPE timer0Mux = portMUX_INITIALIZER_UNLOCKED; +hw_timer_t * dscKeybusInterface::timer0 = NULL; +portMUX_TYPE dscKeybusInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; #endif @@ -761,16 +722,6 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { } -// Interrupt function called after 250us by dscClockInterrupt() using AVR Timer1, disables the timer and calls -// dscDataInterrupt() to read the data line -#if defined(__AVR__) -ISR(TIMER1_OVF_vect) { - TCCR1B = 0; // Disables Timer1 - dscKeybusInterface::dscDataInterrupt(); -} -#endif - - // Interrupt function called by AVR Timer1, esp8266 timer1, and esp32 timer0 after 250us to read the data line #if defined(__AVR__) void dscKeybusInterface::dscDataInterrupt() { diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index a246319..b16638e 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -20,318 +20,117 @@ #ifndef dscKeybusInterface_h #define dscKeybusInterface_h -#include - +// DSC PowerSeries +#ifndef dscClassicSeries +#include "dscKeybus.h" + +byte dscKeybusInterface::dscClockPin; +byte dscKeybusInterface::dscReadPin; +byte dscKeybusInterface::dscWritePin; +char dscKeybusInterface::writeKey; +byte dscKeybusInterface::writePartition; +byte dscKeybusInterface::writeByte; +byte dscKeybusInterface::writeBit; +bool dscKeybusInterface::virtualKeypad; +bool dscKeybusInterface::processModuleData; +byte dscKeybusInterface::panelData[dscReadSize]; +byte dscKeybusInterface::panelByteCount; +byte dscKeybusInterface::panelBitCount; +volatile bool dscKeybusInterface::writeKeyPending; +volatile byte dscKeybusInterface::moduleData[dscReadSize]; +volatile bool dscKeybusInterface::moduleDataCaptured; +volatile byte dscKeybusInterface::moduleByteCount; +volatile byte dscKeybusInterface::moduleBitCount; +volatile bool dscKeybusInterface::writeAlarm; +volatile bool dscKeybusInterface::starKeyCheck; +volatile bool dscKeybusInterface::starKeyWait[dscPartitions]; +volatile bool dscKeybusInterface::bufferOverflow; +volatile byte dscKeybusInterface::panelBufferLength; +volatile byte dscKeybusInterface::panelBuffer[dscBufferSize][dscReadSize]; +volatile byte dscKeybusInterface::panelBufferBitCount[dscBufferSize]; +volatile byte dscKeybusInterface::panelBufferByteCount[dscBufferSize]; +volatile byte dscKeybusInterface::isrPanelData[dscReadSize]; +volatile byte dscKeybusInterface::isrPanelByteCount; +volatile byte dscKeybusInterface::isrPanelBitCount; +volatile byte dscKeybusInterface::isrPanelBitTotal; +volatile byte dscKeybusInterface::isrModuleData[dscReadSize]; +volatile byte dscKeybusInterface::isrModuleByteCount; +volatile byte dscKeybusInterface::isrModuleBitCount; +volatile byte dscKeybusInterface::isrModuleBitTotal; +volatile byte dscKeybusInterface::currentCmd; +volatile byte dscKeybusInterface::statusCmd; +volatile byte dscKeybusInterface::moduleCmd; +volatile byte dscKeybusInterface::moduleSubCmd; +volatile unsigned long dscKeybusInterface::clockHighTime; +volatile unsigned long dscKeybusInterface::keybusTime; + +// Interrupt function called after 250us by dscClockInterrupt() using AVR Timer1, disables the timer and calls +// dscDataInterrupt() to read the data line #if defined(__AVR__) -const byte dscPartitions = 4; // Maximum number of partitions - requires 19 bytes of memory per partition -const byte dscZones = 4; // Maximum number of zone groups, 8 zones per group - requires 6 bytes of memory per zone group -const byte dscBufferSize = 10; // Number of commands to buffer if the sketch is busy - requires dscReadSize + 2 bytes of memory per command -const byte dscReadSize = 16; // Maximum bytes of a Keybus command -#elif defined(ESP8266) -const byte dscPartitions = 8; -const byte dscZones = 8; -const byte dscBufferSize = 50; -const byte dscReadSize = 16; -#elif defined(ESP32) -const byte dscPartitions = 8; -const byte dscZones = 8; -const DRAM_ATTR byte dscBufferSize = 50; -const DRAM_ATTR byte dscReadSize = 16; -#endif - -// Exit delay target states -#define DSC_EXIT_STAY 1 -#define DSC_EXIT_AWAY 2 -#define DSC_EXIT_NO_ENTRY_DELAY 3 - - -class dscKeybusInterface { - - public: - - // Initializes writes as disabled by default - dscKeybusInterface(byte setClockPin, byte setReadPin, byte setWritePin = 255); - - // Interface control - void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default - bool loop(); // Returns true if valid panel data is available - void stop(); // Disables the clock hardware interrupt and data timer interrupt - void resetStatus(); // Resets the state of all status components as changed for sketches to get the current status - - // Writes a single key - nonblocking unless a previous write is in progress - void write(const char receivedKey); - - // Writes multiple keys from a char array - // - // By default, this is nonblocking unless there is a previous write in progress - in this case, the sketch must keep the char - // array defined at least until the write is complete. - // - // If the char array is ephemeral, check if the write is complete by checking writeReady or set blockingWrite to true to - // block until the write is complete. - void write(const char * receivedKeys, bool blockingWrite = false); - - // Write control - static byte writePartition; // Set to a partition number for virtual keypad - bool writeReady; // True if the library is ready to write a key - - // Prints output to the stream interface set in begin() - void printPanelBinary(bool printSpaces = true); // Includes spaces between bytes by default - void printPanelCommand(); // Prints the panel command as hex - void printPanelMessage(); // Prints the decoded panel message - void printModuleBinary(bool printSpaces = true); // Includes spaces between bytes by default - void printModuleMessage(); // Prints the decoded keypad or module message - - - // These can be configured in the sketch setup() before begin() - bool hideKeypadDigits; // Controls if keypad digits are hidden for publicly posted logs (default: false) - static bool processModuleData; // Controls if keypad and module data is processed and displayed (default: false) - bool displayTrailingBits; // Controls if bits read as the clock is reset are displayed, appears to be spurious data (default: false) - - // Panel time - bool timestampChanged; // True after the panel sends a timestamped message - byte hour, minute, day, month; - int year; - - // Sets panel time, the year can be sent as either 2 or 4 digits, returns true if the panel is ready to set the time - bool setTime(unsigned int year, byte month, byte day, byte hour, byte minute, const char* accessCode, byte timePartition = 1); - - // Status tracking - bool statusChanged; // True after any status change - bool pauseStatus; // Prevent status from showing as changed, set in sketch to control when to update status - bool keybusConnected, keybusChanged; // True if data is detected on the Keybus - byte accessCode[dscPartitions]; - bool accessCodeChanged[dscPartitions]; - bool accessCodePrompt; // True if the panel is requesting an access code - bool decimalInput; // True if the panel is requesting 3 digit input (for 0x6E readout) - bool trouble, troubleChanged; - bool powerTrouble, powerChanged; - bool batteryTrouble, batteryChanged; - bool keypadFireAlarm, keypadAuxAlarm, keypadPanicAlarm; - bool ready[dscPartitions], readyChanged[dscPartitions]; - bool disabled[dscPartitions], disabledChanged[dscPartitions]; - bool armed[dscPartitions], armedAway[dscPartitions], armedStay[dscPartitions]; - bool noEntryDelay[dscPartitions], armedChanged[dscPartitions]; - bool alarm[dscPartitions], alarmChanged[dscPartitions]; - bool exitDelay[dscPartitions], exitDelayChanged[dscPartitions]; - byte exitState[dscPartitions], exitStateChanged[dscPartitions]; - bool entryDelay[dscPartitions], entryDelayChanged[dscPartitions]; - bool fire[dscPartitions], fireChanged[dscPartitions]; - bool openZonesStatusChanged; - byte openZones[dscZones], openZonesChanged[dscZones]; // Zone status is stored in an array using 1 bit per zone, up to 64 zones - bool alarmZonesStatusChanged; - byte alarmZones[dscZones], alarmZonesChanged[dscZones]; // Zone alarm status is stored in an array using 1 bit per zone, up to 64 zones - bool pgmOutputsStatusChanged; - byte pgmOutputs[2], pgmOutputsChanged[2]; - byte panelVersion; - - /* panelData[] and moduleData[] store panel and keypad/module data in an array: command [0], stop bit by itself [1], - * followed by the remaining data. These can be accessed directly in the sketch to get data that is not already - * tracked in the library. See dscKeybusPrintData.cpp for the currently known DSC commands and data. - * - * panelData[] example: - * Byte 0 Byte 2 Byte 3 Byte 4 Byte 5 - * 00000101 0 10000001 00000001 10010001 11000111 [0x05] Partition 1: Ready Backlight - Partition ready | Partition 2: disabled - * ^ Byte 1 (stop bit) - */ - static byte panelData[dscReadSize]; - static volatile byte moduleData[dscReadSize]; - - // status[] and lights[] store the current status message and LED state for each partition. These can be accessed - // directly in the sketch to get data that is not already tracked in the library. See printPanelMessages() and - // printPanelLights() in dscKeybusPrintData.cpp to see how this data translates to the status message and LED status. - byte status[dscPartitions]; - byte lights[dscPartitions]; - - // Process keypad and module data, returns true if data is available - bool handleModule(); - - // True if dscBufferSize needs to be increased - static volatile bool bufferOverflow; - - // Timer interrupt function to capture data - declared as public for use by AVR Timer1 - static void dscDataInterrupt(); - - // Deprecated - bool handlePanel(); // Returns true if valid panel data is available. Relabeled to loop() - bool processRedundantData; // Controls if repeated periodic commands are processed and displayed (default: false) - - private: - - void processPanelStatus(); - void processPanelStatus0(byte partition, byte panelByte); - void processPanelStatus2(byte partition, byte panelByte); - void processPanelStatus4(byte partition, byte panelByte); - void processPanelStatus5(byte partition, byte panelByte); - void processPanel_0x16(); - void processPanel_0x27(); - void processPanel_0x2D(); - void processPanel_0x34(); - void processPanel_0x3E(); - void processPanel_0x87(); - void processPanel_0xA5(); - void processPanel_0xE6(); - void processPanel_0xE6_0x09(); - void processPanel_0xE6_0x0B(); - void processPanel_0xE6_0x0D(); - void processPanel_0xE6_0x0F(); - void processPanel_0xE6_0x1A(); - void processPanel_0xEB(); - void processReadyStatus(byte partitionIndex, bool status); - void processAlarmStatus(byte partitionIndex, bool status); - void processExitDelayStatus(byte partitionIndex, bool status); - void processEntryDelayStatus(byte partitionIndex, bool status); - void processZoneStatus(byte zonesByte, byte panelByte); - void processTime(byte panelByte); - void processAlarmZones(byte panelByte, byte startByte, byte zoneCountOffset, byte writeValue); - void processAlarmZonesStatus(byte zonesByte, byte zoneCount, byte writeValue); - void processArmed(byte partitionIndex, bool armedStatus); - void processPanelAccessCode(byte partitionIndex, byte dscCode, bool accessCodeIncrease = true); - - void printPanelPartitionStatus(byte startPartition, byte startByte, byte endByte); - void printPanelStatus0(byte panelByte); - void printPanelStatus1(byte panelByte); - void printPanelStatus2(byte panelByte); - void printPanelStatus3(byte panelByte); - void printPanelStatus4(byte panelByte); - void printPanelStatus5(byte panelByte); - void printPanelStatus14(byte panelByte); - void printPanelStatus16(byte panelByte); - void printPanelStatus17(byte panelByte); - void printPanelStatus18(byte panelByte); - void printPanelStatus1B(byte panelByte); - - void printPanelMessages(byte panelByte); - void printPanelLights(byte panelByte, bool printMessage = true); - void printPanelTime(byte panelByte); - void printPanelBeeps(byte panelByte); - void printPanelTone(byte panelByte); - void printPanelBuzzer(byte panelByte); - bool printPanelZones(byte inputByte, byte startZone); - void printPanelAccessCode(byte dscCode, bool accessCodeIncrease = true); - void printPanelBitNumbers(byte panelByte, byte startNumber, byte startBit = 0, byte stopBit = 7, bool printNone = true); - void printNumberSpace(byte number); - void printNumberOffset(byte panelByte, int numberOffset); - void printUnknownData(); - void printPartition(); - void printStatusLights(); - void printStatusLightsFlashing(); - void printZoneLights(bool lowerRange = true); - void printPanel_0x05(); - void printPanel_0x0A_0F(); - void printPanel_0x11(); - void printPanel_0x16(); - void printPanel_0x1B(); - void printPanel_0x1C(); - void printPanel_0x22_28_33_39(); - void printPanel_0x27(); - void printPanel_0x2D(); - void printPanel_0x34(); - void printPanel_0x3E(); - void printPanel_0x41(); - void printPanel_0x4C(); - void printPanel_0x57(); - void printPanel_0x58(); - void printPanel_0x5D_63(); - void printPanel_0x64(); - void printPanel_0x69(); - void printPanel_0x6E(); - void printPanel_0x70(); - void printPanel_0x75(); - void printPanel_0x7A(); - void printPanel_0x7F(); - void printPanel_0x82(); - void printPanel_0x87(); - void printPanel_0x8D(); - void printPanel_0x94(); - void printPanel_0x9E(); - void printPanel_0xA5(); - void printPanel_0xAA(); - void printPanel_0xB1(); - void printPanel_0xBB(); - void printPanel_0xC3(); - void printPanel_0xCE(); - void printPanel_0xD5(); - void printPanel_0xE6(); - void printPanel_0xE6_0x01_06_20_21(); - void printPanel_0xE6_0x08_0A_0C_0E(); - void printPanel_0xE6_0x09(); - void printPanel_0xE6_0x0B(); - void printPanel_0xE6_0x0D(); - void printPanel_0xE6_0x0F(); - void printPanel_0xE6_0x17(); - void printPanel_0xE6_0x18(); - void printPanel_0xE6_0x19(); - void printPanel_0xE6_0x1A(); - void printPanel_0xE6_0x1D(); - void printPanel_0xE6_0x1F(); - void printPanel_0xE6_0x2B(); - void printPanel_0xE6_0x2C(); - void printPanel_0xE6_0x41(); - void printPanel_0xEB(); - void printPanel_0xEC(); - - void printModule_0x77(); - void printModule_0xBB(); - void printModule_0xDD(); - void printModule_Status(); - void printModule_0x11(); - void printModule_0x41(); - void printModule_0x4C(); - void printModule_0x57(); - void printModule_0x58(); - void printModule_0x70(); - void printModule_0x94(); - void printModule_0xD5(); - bool printModule_Keys(); - void printModule_KeyCodes(byte keyByte); - void printModule_Expander(); - bool printModuleSlots(byte startCount, byte startByte, byte endByte, byte startMask, byte endMask, byte bitShift, byte matchValue, bool reverse = false); - - bool validCRC(); - void writeKeys(const char * writeKeysArray); - void setWriteKey(const char receivedKey); - static void dscClockInterrupt(); - static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); - - Stream* stream; - const char* writeKeysArray; - bool writeKeysPending; - bool writeArm[dscPartitions]; - bool queryResponse; - bool previousTrouble; - bool previousKeybus; - bool previousPower; - bool previousDisabled[dscPartitions]; - byte previousAccessCode[dscPartitions]; - byte previousLights[dscPartitions], previousStatus[dscPartitions]; - bool previousReady[dscPartitions]; - bool previousExitDelay[dscPartitions], previousEntryDelay[dscPartitions]; - byte previousExitState[dscPartitions]; - bool previousArmed[dscPartitions], previousArmedStay[dscPartitions]; - bool previousAlarm[dscPartitions]; - bool previousFire[dscPartitions]; - byte previousOpenZones[dscZones], previousAlarmZones[dscZones]; - byte previousPgmOutputs[2]; - bool keybusVersion1; - - static byte dscClockPin; - static byte dscReadPin; - static byte dscWritePin; - static byte writeByte, writeBit; - static bool virtualKeypad; - static char writeKey; - static byte panelBitCount, panelByteCount; - static volatile bool writeKeyPending; - static volatile bool writeAlarm, starKeyCheck, starKeyWait[dscPartitions]; - static volatile bool moduleDataCaptured; - static volatile unsigned long clockHighTime, keybusTime; - static volatile byte panelBufferLength; - static volatile byte panelBuffer[dscBufferSize][dscReadSize]; - static volatile byte panelBufferBitCount[dscBufferSize], panelBufferByteCount[dscBufferSize]; - static volatile byte moduleBitCount, moduleByteCount; - static volatile byte currentCmd, statusCmd, moduleCmd, moduleSubCmd; - static volatile byte isrPanelData[dscReadSize], isrPanelBitTotal, isrPanelBitCount, isrPanelByteCount; - static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; -}; +ISR(TIMER1_OVF_vect) { + TCCR1B = 0; // Disables Timer1 + dscKeybusInterface::dscDataInterrupt(); +} +#endif // __AVR__ + +// DSC Classic Series +#elif defined dscClassicSeries +#include "dscClassic.h" + +byte dscClassicInterface::dscClockPin; +byte dscClassicInterface::dscReadPin; +byte dscClassicInterface::dscPC16Pin; +byte dscClassicInterface::dscWritePin; +char dscClassicInterface::writeKey; +byte dscClassicInterface::writePartition; +byte dscClassicInterface::writeByte; +byte dscClassicInterface::writeBit; +bool dscClassicInterface::virtualKeypad; +bool dscClassicInterface::processModuleData; +byte dscClassicInterface::panelData[dscReadSize]; +byte dscClassicInterface::pc16Data[dscReadSize]; +byte dscClassicInterface::panelByteCount; +byte dscClassicInterface::panelBitCount; +volatile bool dscClassicInterface::writeKeyPending; +volatile bool dscClassicInterface::writeKeyWait; +volatile byte dscClassicInterface::moduleData[dscReadSize]; +volatile bool dscClassicInterface::moduleDataCaptured; +volatile byte dscClassicInterface::moduleByteCount; +volatile byte dscClassicInterface::moduleBitCount; +volatile bool dscClassicInterface::writeAlarm; +volatile bool dscClassicInterface::starKeyDetected; +volatile bool dscClassicInterface::starKeyCheck; +volatile bool dscClassicInterface::starKeyWait; +volatile bool dscClassicInterface::bufferOverflow; +volatile byte dscClassicInterface::panelBufferLength; +volatile byte dscClassicInterface::panelBuffer[dscBufferSize][dscReadSize]; +volatile byte dscClassicInterface::pc16Buffer[dscBufferSize][dscReadSize]; +volatile byte dscClassicInterface::panelBufferBitCount[dscBufferSize]; +volatile byte dscClassicInterface::panelBufferByteCount[dscBufferSize]; +volatile byte dscClassicInterface::isrPanelData[dscReadSize]; +volatile byte dscClassicInterface::isrPC16Data[dscReadSize]; +volatile byte dscClassicInterface::isrPanelByteCount; +volatile byte dscClassicInterface::isrPanelBitCount; +volatile byte dscClassicInterface::isrPanelBitTotal; +volatile byte dscClassicInterface::isrModuleData[dscReadSize]; +volatile byte dscClassicInterface::isrModuleByteCount; +volatile byte dscClassicInterface::isrModuleBitCount; +volatile byte dscClassicInterface::isrModuleBitTotal; +volatile byte dscClassicInterface::moduleCmd; +volatile bool dscClassicInterface::readyLight; +volatile bool dscClassicInterface::lightBlink; +volatile unsigned long dscClassicInterface::clockHighTime; +volatile unsigned long dscClassicInterface::keybusTime; +volatile unsigned long dscClassicInterface::writeCompleteTime; + +// Interrupt function called after 250us by dscClockInterrupt() using AVR Timer1, disables the timer and calls +// dscDataInterrupt() to read the data line +#if defined(__AVR__) +ISR(TIMER1_OVF_vect) { + TCCR1B = 0; // Disables Timer1 + dscClassicInterface::dscDataInterrupt(); +} +#endif // __AVR__ +#endif // dscClassicSeries #endif // dscKeybusInterface_h diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index 9585154..92acbf6 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -29,7 +29,7 @@ along with this program. If not, see . */ - #include "dscKeybusInterface.h" + #include "dscKeybus.h" /* diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index 610c220..6e506ad 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -27,7 +27,7 @@ along with this program. If not, see . */ -#include "dscKeybusInterface.h" +#include "dscKeybus.h" // Resets the state of all status components as changed for sketches to get the current status From b2e0b964bd977b6d12328dfbe031737e6af920e2 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sat, 23 Jan 2021 22:17:54 -0600 Subject: [PATCH 02/49] Update Classic series wiring diagram --- README.md | 7 +++++-- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 11 +++++++---- .../Arduino/Homebridge-MQTT/Homebridge-MQTT.ino | 11 +++++++---- examples/Arduino/KeybusReader/KeybusReader.ino | 11 +++++++---- examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 13 ++++++++----- examples/Arduino/Status/Status.ino | 11 +++++++---- examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino | 6 +----- examples/Arduino/Unlocker/Unlocker.ino | 2 +- examples/esp32/Email/Email.ino | 10 ++++++---- .../esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 11 +++++++---- examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 11 +++++++---- examples/esp32/Homey/Homey.ino | 11 +++++++---- examples/esp32/KeybusReader/KeybusReader.ino | 11 +++++++---- examples/esp32/KeybusReaderIP/KeybusReaderIP.ino | 11 +++++++---- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 11 +++++++---- examples/esp32/Pushbullet/Pushbullet.ino | 9 ++++++--- examples/esp32/Status/Status.ino | 11 +++++++---- examples/esp32/Telegram/Telegram.ino | 11 +++++++---- examples/esp32/TimeSyncNTP/TimeSyncNTP.ino | 2 +- examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 9 ++++++--- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 9 ++++++--- examples/esp32/Unlocker/Unlocker.ino | 2 +- .../VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 11 +++++++---- .../esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino | 11 +++++++---- examples/esp8266/Email/Email.ino | 9 ++++++--- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 11 +++++++---- .../esp8266/Homebridge-MQTT/Homebridge-MQTT.ino | 11 +++++++---- examples/esp8266/Homey/Homey.ino | 11 +++++++---- examples/esp8266/KeybusReader/KeybusReader.ino | 11 +++++++---- examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino | 11 +++++++---- examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 11 +++++++---- examples/esp8266/Pushbullet/Pushbullet.ino | 9 ++++++--- examples/esp8266/Status/Status.ino | 11 +++++++---- examples/esp8266/Telegram/Telegram.ino | 11 +++++++---- examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino | 2 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 9 ++++++--- examples/esp8266/Unlocker/Unlocker.ino | 2 +- .../VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 11 +++++++---- .../esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino | 11 +++++++---- 39 files changed, 229 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 1d86f5e..9c7ab84 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # DSC Keybus Interface +![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, and direct control as a virtual keypad. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: @@ -265,8 +266,10 @@ DSC Green ----+--- 15k ohm resistor ---| +--- 10k ohm resistor --- Ground Classic series only, PGM configured for PC-16 output: - Arduino +--- dscPC16Pin (Arduino Uno: 4) -DSC PGM ------+--- 15k ohm resistor ---| +DSC PGM ------+--- 1k ohm resistor ---- DSC Aux(+) + | + | Arduino +--- dscPC16Pin (Arduino Uno: 4) + +--- 15k ohm resistor ---| | +--- 10k ohm resistor --- Ground | | esp8266/esp32 +--- dscPC16Pin (esp8266: D7, GPIO 13 / esp32: 17) diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 5732936..c05a0bc 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -177,11 +177,14 @@ entity: alarm_control_panel.security_partition_1 * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index 48bf11f..c1a58ff 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -168,11 +168,14 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/Arduino/KeybusReader/KeybusReader.ino b/examples/Arduino/KeybusReader/KeybusReader.ino index 70bf772..f901812 100644 --- a/examples/Arduino/KeybusReader/KeybusReader.ino +++ b/examples/Arduino/KeybusReader/KeybusReader.ino @@ -23,11 +23,14 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index f0f8570..8ebac8b 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -98,13 +98,16 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ - * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) + * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ * * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should diff --git a/examples/Arduino/Status/Status.ino b/examples/Arduino/Status/Status.ino index 5272cb6..66d1e7a 100644 --- a/examples/Arduino/Status/Status.ino +++ b/examples/Arduino/Status/Status.ino @@ -23,11 +23,14 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino index c94469e..d14ed13 100644 --- a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino @@ -25,11 +25,7 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin (Arduino Uno: 2-12) - * DSC PGM ------ 15k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground - * - * Virtual keypad: + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/Arduino/Unlocker/Unlocker.ino b/examples/Arduino/Unlocker/Unlocker.ino index 846804e..b08dcec 100644 --- a/examples/Arduino/Unlocker/Unlocker.ino +++ b/examples/Arduino/Unlocker/Unlocker.ino @@ -61,7 +61,7 @@ * DSC Green ---- 15k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad: + * Virtual keypad: * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) * Ground --- NPN emitter --/ diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index defa9fe..4b6ae4e 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -24,10 +24,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground - * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 9f12fff..e715072 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -176,11 +176,14 @@ entity: alarm_control_panel.security_partition_1 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index ffdee7e..a4a9ca0 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -164,11 +164,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index 72ccbd3..582c9d2 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -27,11 +27,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/KeybusReader/KeybusReader.ino b/examples/esp32/KeybusReader/KeybusReader.ino index 187331c..8279e53 100644 --- a/examples/esp32/KeybusReader/KeybusReader.ino +++ b/examples/esp32/KeybusReader/KeybusReader.ino @@ -23,11 +23,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index 4ed1d5d..f83f0e2 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -27,11 +27,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index f1389fe..95780e0 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -106,11 +106,14 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index 9fd205e..a955b62 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -27,9 +27,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * * Issues and (especially) pull requests are welcome: diff --git a/examples/esp32/Status/Status.ino b/examples/esp32/Status/Status.ino index 08c7e4f..1cd81d4 100644 --- a/examples/esp32/Status/Status.ino +++ b/examples/esp32/Status/Status.ino @@ -22,11 +22,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 898f73f..bc3c464 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -42,11 +42,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino index d1feeb1..ed7c090 100644 --- a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino @@ -22,7 +22,7 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index 35f9aaa..fbc1248 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -26,9 +26,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * * Issues and (especially) pull requests are welcome: diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index f051194..451a1d4 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -20,9 +20,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * * Issues and (especially) pull requests are welcome: diff --git a/examples/esp32/Unlocker/Unlocker.ino b/examples/esp32/Unlocker/Unlocker.ino index f505321..c1ed6ca 100644 --- a/examples/esp32/Unlocker/Unlocker.ino +++ b/examples/esp32/Unlocker/Unlocker.ino @@ -63,7 +63,7 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad: + * Virtual keypad: * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp32: 4,13,16-33) * Ground --- NPN emitter --/ diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index f809fff..cc4adb6 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -70,11 +70,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 0dfe442..a7d5425 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -44,11 +44,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: 17 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index 3b55307..a24b0d3 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -29,9 +29,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 217032a..802b7fc 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -184,11 +184,14 @@ entity: alarm_control_panel.security_partition_1 * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index ec2c83e..a5dc52a 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -170,11 +170,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index 73df204..53fea35 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -30,11 +30,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/KeybusReader/KeybusReader.ino b/examples/esp8266/KeybusReader/KeybusReader.ino index f2d8062..dc06cbe 100644 --- a/examples/esp8266/KeybusReader/KeybusReader.ino +++ b/examples/esp8266/KeybusReader/KeybusReader.ino @@ -24,11 +24,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index 401c673..f11b051 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -28,11 +28,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index bd9c6c6..1ec293d 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -107,11 +107,14 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 9d3f81d..64c2d22 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -33,9 +33,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface diff --git a/examples/esp8266/Status/Status.ino b/examples/esp8266/Status/Status.ino index 45ab1ce..09d4242 100644 --- a/examples/esp8266/Status/Status.ino +++ b/examples/esp8266/Status/Status.ino @@ -24,11 +24,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index 01fc5c9..6b7e27a 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -42,11 +42,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino index 5f28bfe..00fb628 100644 --- a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino @@ -22,7 +22,7 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index 1d41c4c..e45cce0 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -26,9 +26,12 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * * Issues and (especially) pull requests are welcome: * https://github.com/taligentx/dscKeybusInterface diff --git a/examples/esp8266/Unlocker/Unlocker.ino b/examples/esp8266/Unlocker/Unlocker.ino index c61eb57..ca223d0 100644 --- a/examples/esp8266/Unlocker/Unlocker.ino +++ b/examples/esp8266/Unlocker/Unlocker.ino @@ -69,7 +69,7 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * Virtual keypad: + * Virtual keypad: * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8) * Ground --- NPN emitter --/ diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 77713f2..32d8f7d 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -71,11 +71,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 412c196..dfbb96f 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -53,11 +53,14 @@ * DSC Green ---- 33k ohm resistor ---| * +--- 10k ohm resistor --- Ground * - * +--- dscPC16Pin // Default: D7, GPIO 13 - * DSC PGM ------ 33k ohm resistor ---| - * (Classic series only) +--- 10k ohm resistor --- Ground + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground * - * Virtual keypad (optional): + * Virtual keypad (optional): * DSC Green ---- NPN collector --\ * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 * Ground --- NPN emitter --/ From b24d055ceea399bc21747a48d356ee599d19087d Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sun, 24 Jan 2021 21:35:34 -0600 Subject: [PATCH 03/49] Add support for ESP32, ESP32-S2 in arduino-esp32 IDF 4.2 --- README.md | 2 +- src/dscClassic.cpp | 32 ++++++++++++++++++++++++++++---- src/dscClassic.h | 3 +++ src/dscKeybus.h | 2 ++ src/dscKeybusInterface.cpp | 32 ++++++++++++++++++++++++++++---- src/dscKeybusInterface.h | 4 ++++ 6 files changed, 66 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9c7ab84..e393df2 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ DSC Green ----+--- 15k ohm resistor ---| +--- 10k ohm resistor --- Ground Classic series only, PGM configured for PC-16 output: -DSC PGM ------+--- 1k ohm resistor ---- DSC Aux(+) +DSC PGM ------+--- 1k ohm resistor --- DSC Aux(+) | | Arduino +--- dscPC16Pin (Arduino Uno: 4) +--- 15k ohm resistor ---| diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp index 7cf4971..27f0f3e 100644 --- a/src/dscClassic.cpp +++ b/src/dscClassic.cpp @@ -20,9 +20,17 @@ #include "dscClassic.h" #if defined(ESP32) -hw_timer_t * dscClassicInterface::timer0 = NULL; portMUX_TYPE dscClassicInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; -#endif + +#if ESP_IDF_VERSION_MAJOR < 4 +hw_timer_t * dscClassicInterface::timer0 = NULL; + +#else // ESP-IDF 4+ +esp_timer_handle_t timer1; +const esp_timer_create_args_t timer1Parameters = { .callback = reinterpret_cast(&dscClassicInterface::dscDataInterrupt) }; + +#endif // ESP_IDF_VERSION_MAJOR +#endif // ESP32 dscClassicInterface::dscClassicInterface(byte setClockPin, byte setReadPin, byte setPC16Pin, byte setWritePin, const char * setAccessCode) { dscClockPin = setClockPin; @@ -64,12 +72,16 @@ void dscClassicInterface::begin(Stream &_stream) { // esp32 timer0 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timer0 = timerBegin(0, 80, true); timerStop(timer0); timerAttachInterrupt(timer0, &dscDataInterrupt, true); timerAlarmWrite(timer0, 250, true); timerAlarmEnable(timer0); - #endif + #else // IDF4+ + esp_timer_create(&timer1Parameters, &timer1); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 // Generates an interrupt when the Keybus clock rises or falls - requires a hardware interrupt pin on Arduino/AVR attachInterrupt(digitalPinToInterrupt(dscClockPin), dscClockInterrupt, CHANGE); @@ -89,9 +101,13 @@ void dscClassicInterface::stop() { // Disables esp32 timer0 #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timerAlarmDisable(timer0); timerEnd(timer0); - #endif + #else // ESP-IDF 4+ + esp_timer_stop(timer1); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 // Disables the Keybus clock pin interrupt detachInterrupt(digitalPinToInterrupt(dscClockPin)); @@ -1024,7 +1040,11 @@ void IRAM_ATTR dscClassicInterface::dscClockInterrupt() { // esp32 timer1 calls dscDataInterrupt() in 250us #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer0); + #else // IDF4+ + esp_timer_start_periodic(timer1, 250); + #endif portENTER_CRITICAL(&timer0Mux); #endif @@ -1077,7 +1097,11 @@ void dscClassicInterface::dscDataInterrupt() { void ICACHE_RAM_ATTR dscClassicInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscClassicInterface::dscDataInterrupt() { + #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer0); + #else // IDF 4+ + esp_timer_stop(timer1); + #endif portENTER_CRITICAL(&timer0Mux); #endif diff --git a/src/dscClassic.h b/src/dscClassic.h index 5736f9e..89fb952 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -37,6 +37,7 @@ const byte dscBufferSize = 50; #define DSC_EXIT_AWAY 2 #define DSC_EXIT_NO_ENTRY_DELAY 3 + class dscClassicInterface { public: @@ -158,7 +159,9 @@ class dscClassicInterface { static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); #if defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer0; + #endif static portMUX_TYPE timer0Mux; #endif diff --git a/src/dscKeybus.h b/src/dscKeybus.h index 5c76183..47d59f5 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -294,7 +294,9 @@ class dscKeybusInterface { static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); #if defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer0; + #endif static portMUX_TYPE timer0Mux; #endif diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 824864f..8ec0131 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -21,9 +21,17 @@ #if defined(ESP32) -hw_timer_t * dscKeybusInterface::timer0 = NULL; portMUX_TYPE dscKeybusInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; -#endif + +#if ESP_IDF_VERSION_MAJOR < 4 +hw_timer_t * dscKeybusInterface::timer0 = NULL; + +#else // ESP-IDF 4+ +esp_timer_handle_t timer0; +const esp_timer_create_args_t timer0Parameters = { .callback = reinterpret_cast(&dscKeybusInterface::dscDataInterrupt) }; + +#endif // ESP_IDF_VERSION_MAJOR +#endif // ESP32 dscKeybusInterface::dscKeybusInterface(byte setClockPin, byte setReadPin, byte setWritePin) { @@ -62,12 +70,16 @@ void dscKeybusInterface::begin(Stream &_stream) { // esp32 timer0 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timer0 = timerBegin(0, 80, true); timerStop(timer0); timerAttachInterrupt(timer0, &dscDataInterrupt, true); timerAlarmWrite(timer0, 250, true); timerAlarmEnable(timer0); - #endif + #else // IDF4+ + esp_timer_create(&timer0Parameters, &timer0); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 // Generates an interrupt when the Keybus clock rises or falls - requires a hardware interrupt pin on Arduino/AVR attachInterrupt(digitalPinToInterrupt(dscClockPin), dscClockInterrupt, CHANGE); @@ -87,9 +99,13 @@ void dscKeybusInterface::stop() { // Disables esp32 timer0 #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timerAlarmDisable(timer0); timerEnd(timer0); - #endif + #else // ESP-IDF 4+ + esp_timer_stop(timer0); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 // Disables the Keybus clock pin interrupt detachInterrupt(digitalPinToInterrupt(dscClockPin)); @@ -642,7 +658,11 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { // esp32 timer0 calls dscDataInterrupt() in 250us #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer0); + #else // IDF4+ + esp_timer_start_periodic(timer0, 250); + #endif portENTER_CRITICAL(&timer0Mux); #endif @@ -729,7 +749,11 @@ void dscKeybusInterface::dscDataInterrupt() { void ICACHE_RAM_ATTR dscKeybusInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { + #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer0); + #else // IDF 4+ + esp_timer_stop(timer0); + #endif portENTER_CRITICAL(&timer0Mux); #endif diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index b16638e..fdab9e2 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -20,6 +20,10 @@ #ifndef dscKeybusInterface_h #define dscKeybusInterface_h +#if defined ESP32 && ESP_IDF_VERSION_MAJOR >= 4 +#include "esp_timer.h" +#endif + // DSC PowerSeries #ifndef dscClassicSeries #include "dscKeybus.h" From 68ebd92879af66b4ab300ba9cc89c443c79410ef Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sun, 24 Jan 2021 23:08:24 -0600 Subject: [PATCH 04/49] Add support for ESP32, ESP32-S2 in arduino-esp32 IDF 4.2 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e393df2..25e531e 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ This library uses a combination of hardware and timer interrupts to accurately c - esp32: * Development boards: NodeMCU ESP-32S, Doit ESP32 Devkit v1, Wemos Lolin D32, etc. * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (v1.0.5-rc6 or newer required), dual cores, WiFi, and Bluetooth for ~$5USD shipped. + - esp32-s2: + * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (idf-release/v4.2 branch required), WiFi, and Bluetooth. * Possible features (PRs welcome!): - [DSC IT-100](https://cms.dsc.com/download.php?t=1&id=16238) emulation - Unlock 6-digit installer codes @@ -98,6 +100,7 @@ This library uses a combination of hardware and timer interrupts to accurately c ## Release notes * develop - New: DSC Classic series panel support: PC1500, PC1550 + - New: esp32-s2 microcontroller support * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT From a92733b21154b1c52f57e7f4d1523346550ef347 Mon Sep 17 00:00:00 2001 From: kricon Date: Sun, 31 Jan 2021 02:41:18 +0100 Subject: [PATCH 05/49] Blynk fix for partitions 2-8, Push notifications for power/battery/alarm Blynk examples now call pauseZones on partitions other than 1 #190 Memory/Programming leds now also work on partition 2, doesn't work for partitions 3-8 for reason taligentx stated in #182 Viewing event buffer now also works fine on partitions 2-8. Added push notification for battery/power trouble/restore and partition in alarm (partition you've selected in blynk project). Note that you must add widget:notification in your blynk project and enable notifications. Remember that it sends notification only when selected partition goes in alarm and not the others. Works when phone is locked and when Blynk app isn't open. --- .../VirtualKeypad-Blynk.ino | 18 +++++++++++++----- .../VirtualKeypad-Blynk.ino | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index cc4adb6..a20f9ee 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -93,7 +93,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 13, option 4 on). //#define dscClassicSeries #include @@ -415,10 +415,12 @@ void loop() { if (dsc.powerTrouble) { lcd.print(0, 0, "Power "); lcd.print(0, 1, "trouble "); + Blynk.notify("Power trouble"); } else { lcd.print(0, 0, "Power "); lcd.print(0, 1, "restored "); + Blynk.notify("Power restored"); } } @@ -427,10 +429,12 @@ void loop() { if (dsc.batteryTrouble) { lcd.print(0, 0, "Battery "); lcd.print(0, 1, "trouble "); + Blynk.notify("Battery trouble"); } else { lcd.print(0, 0, "Battery "); lcd.print(0, 1, "restored "); + Blynk.notify("Battery restored"); } } } @@ -479,7 +483,8 @@ void setStatus(byte partition, bool forceUpdate) { case 0x10: lcd.print(0, 0, "Keypad "); lcd.print(0, 1, "lockout "); break; case 0x11: lcd.print(0, 0, "Partition "); - lcd.print(0, 1, "in alarm "); break; + lcd.print(0, 1, "in alarm "); + Blynk.notify("Partition in alarm"); break; case 0x12: lcd.print(0, 0, "Battery check"); lcd.print(0, 1, "in progress "); break; case 0x14: lcd.print(0, 0, "Auto-arm "); @@ -870,11 +875,14 @@ void processStatus() { #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { - pauseZones(); - } + case 0x1B: + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones) pauseZones(); break; case 0x0A: + case 0x0F: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { pauseZones(); } diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 32d8f7d..e98a9e0 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -94,7 +94,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 13, option 4 on). //#define dscClassicSeries #include @@ -417,10 +417,12 @@ void loop() { if (dsc.powerTrouble) { lcd.print(0, 0, "Power "); lcd.print(0, 1, "trouble "); + Blynk.notify("Power trouble"); } else { lcd.print(0, 0, "Power "); lcd.print(0, 1, "restored "); + Blynk.notify("Power restored"); } } @@ -429,10 +431,12 @@ void loop() { if (dsc.batteryTrouble) { lcd.print(0, 0, "Battery "); lcd.print(0, 1, "trouble "); + Blynk.notify("Battery trouble"); } else { lcd.print(0, 0, "Battery "); lcd.print(0, 1, "restored "); + Blynk.notify("Battery restored"); } } } @@ -481,7 +485,8 @@ void setStatus(byte partition, bool forceUpdate) { case 0x10: lcd.print(0, 0, "Keypad "); lcd.print(0, 1, "lockout "); break; case 0x11: lcd.print(0, 0, "Partition "); - lcd.print(0, 1, "in alarm "); break; + lcd.print(0, 1, "in alarm "); + Blynk.notify("Partition in alarm"); break; case 0x12: lcd.print(0, 0, "Battery check"); lcd.print(0, 1, "in progress "); break; case 0x14: lcd.print(0, 0, "Auto-arm "); @@ -872,11 +877,14 @@ void processStatus() { #ifndef dscClassicSeries switch (dsc.panelData[0]) { case 0x05: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { - pauseZones(); - } + case 0x1B: + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones) pauseZones(); break; case 0x0A: + case 0x0F: if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { pauseZones(); } From 612b2caa9177f884b2bac6795b87b7e0364769ac Mon Sep 17 00:00:00 2001 From: kricon Date: Sun, 31 Jan 2021 04:29:08 +0100 Subject: [PATCH 06/49] Fixed issue where pauseZones would get called if real keypad set on different partition enter * key #198 --- .../VirtualKeypad-Blynk.ino | 32 +++++++++-------- .../VirtualKeypad-Blynk.ino | 34 +++++++++++-------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index a20f9ee..6349398 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -874,21 +874,25 @@ void setLights(byte partition, bool forceUpdate) { void processStatus() { #ifndef dscClassicSeries switch (dsc.panelData[0]) { - case 0x05: - case 0x1B: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones) pauseZones(); + case 0x05: //Enter (*) function key, enter (*) function key while armed, enter installer code, enter master code status messages for partitions 1-4 calls pauseZones + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 1) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones && dsc.writePartition == 2) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones && dsc.writePartition == 3) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones && dsc.writePartition == 4) pauseZones(); break; - case 0x0A: - case 0x0F: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { - pauseZones(); - } - if (pausedZones) { - processProgramZones(4, ledProgramZonesColor); - } + case 0x0A: //Call processProgramZones on partition 1 + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 1) pauseZones(); + if (pausedZones) processProgramZones(4, ledProgramZonesColor); + break; + case 0x0F: //Call processProgramZones on partition 2 + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 2) pauseZones(); + if (pausedZones) processProgramZones(4, ledProgramZonesColor); + break; + case 0x1B: //Enter (*) function key, enter (*) function key while armed, enter installer code, enter master code status messages for partitions 4-8 calls pauseZones + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 5) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones && dsc.writePartition == 6) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones && dsc.writePartition == 7) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones && dsc.writePartition == 8) pauseZones(); break; case 0x5D: if ((dsc.panelData[2] & 0x04) == 0x04) { // Alarm memory zones 1-32 diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index e98a9e0..608164e 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -108,7 +108,7 @@ const char* wifiSSID = ""; const char* wifiPassword = ""; const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* blynkAuthToken = ""; // Token generated from within the Blynk app -const char* blynkServer = ""; // Blynk local server address +const char* blynkServer = "192.168.0.30"; // Blynk local server address const int blynkPort = 8080; // Blynk local server port bool showLCDoutput = true; // Control if LCD programming output is displayed on VirtualPin20 @@ -876,21 +876,25 @@ void setLights(byte partition, bool forceUpdate) { void processStatus() { #ifndef dscClassicSeries switch (dsc.panelData[0]) { - case 0x05: - case 0x1B: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones) pauseZones(); - if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones) pauseZones(); + case 0x05: //Enter (*) function key, enter (*) function key while armed, enter installer code, enter master code status messages for partitions 1-4 calls pauseZones + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 1) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones && dsc.writePartition == 2) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones && dsc.writePartition == 3) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones && dsc.writePartition == 4) pauseZones(); break; - case 0x0A: - case 0x0F: - if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones) { - pauseZones(); - } - if (pausedZones) { - processProgramZones(4, ledProgramZonesColor); - } + case 0x0A: //Call processProgramZones on partition 1 + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 1) pauseZones(); + if (pausedZones) processProgramZones(4, ledProgramZonesColor); + break; + case 0x0F: //Call processProgramZones on partition 2 + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 2) pauseZones(); + if (pausedZones) processProgramZones(4, ledProgramZonesColor); + break; + case 0x1B: //Enter (*) function key, enter (*) function key while armed, enter installer code, enter master code status messages for partitions 4-8 calls pauseZones + if ((dsc.panelData[3] == 0x9E || dsc.panelData[3] == 0xA5 || dsc.panelData[3] == 0xB7 || dsc.panelData[3] == 0xB8) && !pausedZones && dsc.writePartition == 5) pauseZones(); + if ((dsc.panelData[5] == 0x9E || dsc.panelData[5] == 0xA5 || dsc.panelData[5] == 0xB7 || dsc.panelData[5] == 0xB8) && !pausedZones && dsc.writePartition == 6) pauseZones(); + if ((dsc.panelData[7] == 0x9E || dsc.panelData[7] == 0xA5 || dsc.panelData[7] == 0xB7 || dsc.panelData[7] == 0xB8) && !pausedZones && dsc.writePartition == 7) pauseZones(); + if ((dsc.panelData[9] == 0x9E || dsc.panelData[9] == 0xA5 || dsc.panelData[9] == 0xB7 || dsc.panelData[9] == 0xB8) && !pausedZones && dsc.writePartition == 8) pauseZones(); break; case 0x5D: if ((dsc.panelData[2] & 0x04) == 0x04) { // Alarm memory zones 1-32 From 7e9bfc474936883ff75ce025640c0f570976d4de Mon Sep 17 00:00:00 2001 From: kricon Date: Sun, 31 Jan 2021 04:37:10 +0100 Subject: [PATCH 07/49] Remove Blynk settings data accidentally left --- examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 608164e..b59e063 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -108,7 +108,7 @@ const char* wifiSSID = ""; const char* wifiPassword = ""; const char* accessCode = ""; // Classic series only, an access code is required to arm with the stay/away buttons. const char* blynkAuthToken = ""; // Token generated from within the Blynk app -const char* blynkServer = "192.168.0.30"; // Blynk local server address +const char* blynkServer = ""; // Blynk local server address const int blynkPort = 8080; // Blynk local server port bool showLCDoutput = true; // Control if LCD programming output is displayed on VirtualPin20 From 97c87af2004c568616a9cef3011e1d22f448674c Mon Sep 17 00:00:00 2001 From: kricon Date: Sun, 31 Jan 2021 06:29:37 +0100 Subject: [PATCH 08/49] Added Blynk push notifications for troubles and other partitions in alarm If power/battery trouble/restore occur, it will be pushed as notification and "Trouble status on" notification wont be pushed. Obviously, if power/battery trouble is active and new trouble occurs such as zone sensor low battery, "Trouble status on" won't be pushed as trouble status was already on. --- .../VirtualKeypad-Blynk.ino | 23 +++++++++++++++++-- .../VirtualKeypad-Blynk.ino | 23 +++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 6349398..32146ba 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -437,6 +437,26 @@ void loop() { Blynk.notify("Battery restored"); } } + + if (dsc.troubleChanged) { + dsc.troubleChanged = false; + if (dsc.trouble) Blynk.notify("Trouble status on"); + else Blynk.notify("Trouble status restored"); + } + + for (byte partition = 0; partition < dscPartitions; partition++) { + if (dsc.disabled[partition]) continue; + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; + if (dsc.alarm[partition]) { + char alarmPartition[19] = "Alarm: Partition "; + char partitionNumber[2]; + itoa(partition + 1, partitionNumber, 10); + strcat(alarmPartition, partitionNumber); + Blynk.notify(alarmPartition); + } + } + } } } @@ -483,8 +503,7 @@ void setStatus(byte partition, bool forceUpdate) { case 0x10: lcd.print(0, 0, "Keypad "); lcd.print(0, 1, "lockout "); break; case 0x11: lcd.print(0, 0, "Partition "); - lcd.print(0, 1, "in alarm "); - Blynk.notify("Partition in alarm"); break; + lcd.print(0, 1, "in alarm "); break; case 0x12: lcd.print(0, 0, "Battery check"); lcd.print(0, 1, "in progress "); break; case 0x14: lcd.print(0, 0, "Auto-arm "); diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index b59e063..fe77c68 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -439,6 +439,26 @@ void loop() { Blynk.notify("Battery restored"); } } + + if (dsc.troubleChanged) { + dsc.troubleChanged = false; + if (dsc.trouble) Blynk.notify("Trouble status on"); + else Blynk.notify("Trouble status restored"); + } + + for (byte partition = 0; partition < dscPartitions; partition++) { + if (dsc.disabled[partition]) continue; + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; + if (dsc.alarm[partition]) { + char alarmPartition[19] = "Alarm: Partition "; + char partitionNumber[2]; + itoa(partition + 1, partitionNumber, 10); + strcat(alarmPartition, partitionNumber); + Blynk.notify(alarmPartition); + } + } + } } } @@ -485,8 +505,7 @@ void setStatus(byte partition, bool forceUpdate) { case 0x10: lcd.print(0, 0, "Keypad "); lcd.print(0, 1, "lockout "); break; case 0x11: lcd.print(0, 0, "Partition "); - lcd.print(0, 1, "in alarm "); - Blynk.notify("Partition in alarm"); break; + lcd.print(0, 1, "in alarm "); break; case 0x12: lcd.print(0, 0, "Battery check"); lcd.print(0, 1, "in progress "); break; case 0x14: lcd.print(0, 0, "Auto-arm "); From 0a0058de849e44c0d0f2e5e0dc567ee674a81a3c Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sun, 31 Jan 2021 23:07:49 -0600 Subject: [PATCH 09/49] Merge ISR panel and module bit counters, fix panel bit counter skipping initial bit at clock reset, update keypad alarm key data with initial bit at clock reset --- README.md | 4 +- src/dscKeybus.h | 6 +- src/dscKeybusInterface.cpp | 182 +++++++++++++++++-------------------- src/dscKeybusInterface.h | 4 +- src/dscKeybusPrintData.cpp | 24 ++--- 5 files changed, 100 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 25e531e..c374756 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * Development boards: NodeMCU ESP-32S, Doit ESP32 Devkit v1, Wemos Lolin D32, etc. * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (v1.0.5-rc6 or newer required), dual cores, WiFi, and Bluetooth for ~$5USD shipped. - esp32-s2: - * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (idf-release/v4.2 branch required), WiFi, and Bluetooth. + * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (idf-release/v4.2 branch required) and WiFi. * Possible features (PRs welcome!): - [DSC IT-100](https://cms.dsc.com/download.php?t=1&id=16238) emulation - Unlock 6-digit installer codes @@ -107,7 +107,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: `Unlocker` example sketch - determines the panel installer code - New: `TimeSyncNTP` example sketch - uses NTP to automatically set the panel time - New: [ESPHome](https://esphome.io) integration example (located in the `extras` directory) - thanks to [Dilbert66](https://github.com/Dilbert66) for this contribution! - - New: `TinyGMS-SMS` example sketch - sends status via SMS with a GSM modem - thanks to [jvitkauskas](https://github.com/jvitkauskas) for this contribution! + - New: `TinyGSM-SMS` example sketch - sends status via SMS with a GSM modem - thanks to [jvitkauskas](https://github.com/jvitkauskas) for this contribution! - New: `KeybusReaderIP` example sketch enables Keybus data access over IP, thanks to [aboulfad](https://github.com/aboulfad) for this contribution! - New: esp32 microcontroller support - requires [Arduino-esp32](https://github.com/espressif/arduino-esp32) v1.0.5-rc6 or newer - New: Features for sketches: diff --git a/src/dscKeybus.h b/src/dscKeybus.h index 47d59f5..ba24a0c 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -270,9 +270,9 @@ class dscKeybusInterface { void printPanel_0xEB(); void printPanel_0xEC(); - void printModule_0x77(); void printModule_0xBB(); void printModule_0xDD(); + void printModule_0xEE(); void printModule_Status(); void printModule_0x11(); void printModule_0x41(); @@ -330,7 +330,7 @@ class dscKeybusInterface { static byte panelBitCount, panelByteCount; static volatile bool writeKeyPending; static volatile bool writeAlarm, starKeyCheck, starKeyWait[dscPartitions]; - static volatile bool moduleDataCaptured; + static volatile bool moduleDataDetected, moduleDataCaptured; static volatile unsigned long clockHighTime, keybusTime; static volatile byte panelBufferLength; static volatile byte panelBuffer[dscBufferSize][dscReadSize]; @@ -338,7 +338,7 @@ class dscKeybusInterface { static volatile byte moduleBitCount, moduleByteCount; static volatile byte currentCmd, statusCmd, moduleCmd, moduleSubCmd; static volatile byte isrPanelData[dscReadSize], isrPanelBitTotal, isrPanelBitCount, isrPanelByteCount; - static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; + static volatile byte isrModuleData[dscReadSize]; }; #endif // dscKeybus_h \ No newline at end of file diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 8ec0131..12f60fe 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -117,11 +117,8 @@ void dscKeybusInterface::stop() { isrPanelBitCount = 0; isrPanelByteCount = 0; - // Resets the keypad and module capture data and counters + // Resets the keypad and module capture data for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; - isrModuleBitTotal = 0; - isrModuleBitCount = 0; - isrModuleByteCount = 0; } @@ -523,7 +520,7 @@ void dscKeybusInterface::setWriteKey(const char receivedKey) { case '*': writeKey = 0x28; if (status[writePartition - 1] < 0x9E) starKeyCheck = true; break; case '#': writeKey = 0x2D; break; case 'F': - case 'f': writeKey = 0x77; writeAlarm = true; break; // Keypad fire alarm + case 'f': writeKey = 0xBB; writeAlarm = true; break; // Keypad fire alarm case 'b': case 'B': writeKey = 0x82; break; // Enter event buffer case '>': writeKey = 0x87; break; // Event buffer right arrow @@ -537,13 +534,13 @@ void dscKeybusInterface::setWriteKey(const char receivedKey) { case 'n': case 'N': writeKey = 0xB6; writeArm[writePartition - 1] = true; break; // Arm with no entry delay (night arm) case 'A': - case 'a': writeKey = 0xBB; writeAlarm = true; break; // Keypad auxiliary alarm + case 'a': writeKey = 0xDD; writeAlarm = true; break; // Keypad auxiliary alarm case 'c': case 'C': writeKey = 0xBB; break; // Door chime case 'r': case 'R': writeKey = 0xDA; break; // Reset case 'P': - case 'p': writeKey = 0xDD; writeAlarm = true; break; // Keypad panic alarm + case 'p': writeKey = 0xEE; writeAlarm = true; break; // Keypad panic alarm case 'x': case 'X': writeKey = 0xE1; break; // Exit case '[': writeKey = 0xD5; break; // Command output 1 @@ -667,16 +664,75 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { #endif static unsigned long previousClockHighTime; + static bool skipData = false; + + // Panel sends data while the clock is high if (digitalRead(dscClockPin) == HIGH) { if (virtualKeypad) digitalWrite(dscWritePin, LOW); // Restores the data line after a virtual keypad write previousClockHighTime = micros(); } + // Keypads and modules send data while the clock is low else { clockHighTime = micros() - previousClockHighTime; // Tracks the clock high time to find the reset between commands + // Saves data and resets counters after the clock cycle is complete (high for at least 1ms) + if (clockHighTime > 1000) { + keybusTime = millis(); + + // Skips incomplete and redundant data from status commands - these are sent constantly on the keybus at a high + // rate, so they are always skipped. Checking is required in the ISR to prevent flooding the buffer. + if (isrPanelBitTotal < 8) skipData = true; + else switch (isrPanelData[0]) { + static byte previousCmd05[dscReadSize]; + static byte previousCmd1B[dscReadSize]; + case 0x05: // Status: partitions 1-4 + if (redundantPanelData(previousCmd05, isrPanelData, isrPanelByteCount)) skipData = true; + break; + + case 0x1B: // Status: partitions 5-8 + if (redundantPanelData(previousCmd1B, isrPanelData, isrPanelByteCount)) skipData = true; + break; + } + + // Stores new panel data in the panel buffer + currentCmd = isrPanelData[0]; + if (panelBufferLength == dscBufferSize) bufferOverflow = true; + else if (!skipData && panelBufferLength < dscBufferSize) { + for (byte i = 0; i < dscReadSize; i++) panelBuffer[panelBufferLength][i] = isrPanelData[i]; + panelBufferBitCount[panelBufferLength] = isrPanelBitTotal; + panelBufferByteCount[panelBufferLength] = isrPanelByteCount; + panelBufferLength++; + } + + if (processModuleData) { + + // Stores new keypad and module data - this data is not buffered + if (moduleDataDetected) { + moduleCmd = isrPanelData[0]; + moduleSubCmd = isrPanelData[2]; + moduleDataDetected = false; + moduleDataCaptured = true; // Sets a flag for handleModule() + for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; + moduleBitCount = isrPanelBitTotal; + moduleByteCount = isrPanelByteCount; + } + + // Resets the keypad and module capture data + for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; + } + + // Resets the panel capture data and counters + for (byte i = 0; i < dscReadSize; i++) isrPanelData[i] = 0; + isrPanelBitTotal = 0; + isrPanelBitCount = 0; + isrPanelByteCount = 0; + skipData = false; + } + // Virtual keypad if (virtualKeypad) { + static bool writeStart = false; static bool writeRepeat = false; static bool writeCmd = false; @@ -689,7 +745,7 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { if ((writeAlarm && writeKeyPending) || writeRepeat) { // Writes the first bit by shifting the alarm key data right 7 bits and checking bit 0 - if (isrPanelBitTotal == 1) { + if (isrPanelBitTotal == 0) { if (!((writeKey >> 7) & 0x01)) { digitalWrite(dscWritePin, HIGH); } @@ -697,11 +753,11 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { } // Writes the remaining alarm key data - else if (writeStart && isrPanelBitTotal > 1 && isrPanelBitTotal <= 8) { - if (!((writeKey >> (8 - isrPanelBitTotal)) & 0x01)) digitalWrite(dscWritePin, HIGH); + else if (writeStart && isrPanelBitTotal <= 7) { + if (!((writeKey >> (7 - isrPanelBitTotal)) & 0x01)) digitalWrite(dscWritePin, HIGH); // Resets counters when the write is complete - if (isrPanelBitTotal == 8) { + if (isrPanelBitTotal == 7) { writeKeyPending = false; writeStart = false; writeAlarm = false; @@ -757,15 +813,11 @@ void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { portENTER_CRITICAL(&timer0Mux); #endif - static bool skipData = false; - // Panel sends data while the clock is high if (digitalRead(dscClockPin) == HIGH) { - // Stops processing Keybus data at the dscReadSize limit - if (isrPanelByteCount >= dscReadSize) skipData = true; - - else { + // Reads panel data and sets data counters + if (isrPanelByteCount < dscReadSize) { // Limits Keybus data bytes to dscReadSize if (isrPanelBitCount < 8) { // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 isrPanelData[isrPanelByteCount] <<= 1; @@ -774,17 +826,18 @@ void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { } } - if (isrPanelBitTotal == 8) { - - // Tests for a status command, used in dscClockInterrupt() to ensure keys are only written during a status command + // Tests for a status command, used in dscClockInterrupt() to ensure keys are only written during a status command + if (isrPanelBitTotal == 7) { switch (isrPanelData[0]) { case 0x05: case 0x0A: statusCmd = 0x05; break; case 0x1B: statusCmd = 0x1B; break; default: statusCmd = 0; break; } + } - // Stores the stop bit by itself in byte 1 - this aligns the Keybus bytes with panelData[] bytes + // Stores the stop bit by itself in byte 1 - this aligns the Keybus bytes with panelData[] bytes + if (isrPanelBitTotal == 8) { isrPanelBitCount = 0; isrPanelByteCount++; } @@ -806,96 +859,25 @@ void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { // Keypads and modules send data while the clock is low else { - static bool moduleDataDetected = false; // Keypad and module data is not buffered and skipped if the panel data buffer is filling - if (processModuleData && isrModuleByteCount < dscReadSize && panelBufferLength <= 1) { + if (processModuleData && isrPanelByteCount < dscReadSize && panelBufferLength <= 1) { // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 - if (isrModuleBitCount < 8) { - isrModuleData[isrModuleByteCount] <<= 1; + if (isrPanelBitCount < 8) { + isrModuleData[isrPanelByteCount] <<= 1; if (digitalRead(dscReadPin) == HIGH) { - isrModuleData[isrModuleByteCount] |= 1; + isrModuleData[isrPanelByteCount] |= 1; + } + else { + moduleDataDetected = true; // Keypads and modules send data by pulling the data line low } - else moduleDataDetected = true; // Keypads and modules send data by pulling the data line low } // Stores the stop bit by itself in byte 1 - this aligns the Keybus bytes with moduleData[] bytes - if (isrModuleBitTotal == 7) { + if (isrPanelBitTotal == 8) { isrModuleData[1] = 1; // Sets the stop bit manually to 1 in byte 1 - isrModuleBitCount = 0; - isrModuleByteCount += 2; - } - - // Increments the bit counter if the byte is incomplete - else if (isrModuleBitCount < 7) { - isrModuleBitCount++; - } - - // Byte is complete, set the counters for the next byte - else { - isrModuleBitCount = 0; - isrModuleByteCount++; - } - - isrModuleBitTotal++; - } - - // Saves data and resets counters after the clock cycle is complete (high for at least 1ms) - if (clockHighTime > 1000) { - keybusTime = millis(); - - // Skips incomplete and redundant data from status commands - these are sent constantly on the keybus at a high - // rate, so they are always skipped. Checking is required in the ISR to prevent flooding the buffer. - if (isrPanelBitTotal < 8) skipData = true; - else switch (isrPanelData[0]) { - static byte previousCmd05[dscReadSize]; - static byte previousCmd1B[dscReadSize]; - case 0x05: // Status: partitions 1-4 - if (redundantPanelData(previousCmd05, isrPanelData, isrPanelByteCount)) skipData = true; - break; - - case 0x1B: // Status: partitions 5-8 - if (redundantPanelData(previousCmd1B, isrPanelData, isrPanelByteCount)) skipData = true; - break; } - - // Stores new panel data in the panel buffer - currentCmd = isrPanelData[0]; - if (panelBufferLength == dscBufferSize) bufferOverflow = true; - else if (!skipData && panelBufferLength < dscBufferSize) { - for (byte i = 0; i < dscReadSize; i++) panelBuffer[panelBufferLength][i] = isrPanelData[i]; - panelBufferBitCount[panelBufferLength] = isrPanelBitTotal; - panelBufferByteCount[panelBufferLength] = isrPanelByteCount; - panelBufferLength++; - } - - if (processModuleData) { - - // Stores new keypad and module data - this data is not buffered - if (moduleDataDetected) { - moduleCmd = isrPanelData[0]; - moduleSubCmd = isrPanelData[2]; - moduleDataDetected = false; - moduleDataCaptured = true; // Sets a flag for handleModule() - for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; - moduleBitCount = isrModuleBitTotal; - moduleByteCount = isrModuleByteCount; - } - - // Resets the keypad and module capture data and counters - for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; - isrModuleBitTotal = 0; - isrModuleBitCount = 0; - isrModuleByteCount = 0; - } - - // Resets the panel capture data and counters - for (byte i = 0; i < dscReadSize; i++) isrPanelData[i] = 0; - isrPanelBitTotal = 0; - isrPanelBitCount = 0; - isrPanelByteCount = 0; - skipData = false; } } #if defined(ESP32) diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index fdab9e2..e358ff6 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -43,6 +43,7 @@ byte dscKeybusInterface::panelBitCount; volatile bool dscKeybusInterface::writeKeyPending; volatile byte dscKeybusInterface::moduleData[dscReadSize]; volatile bool dscKeybusInterface::moduleDataCaptured; +volatile bool dscKeybusInterface::moduleDataDetected; volatile byte dscKeybusInterface::moduleByteCount; volatile byte dscKeybusInterface::moduleBitCount; volatile bool dscKeybusInterface::writeAlarm; @@ -58,9 +59,6 @@ volatile byte dscKeybusInterface::isrPanelByteCount; volatile byte dscKeybusInterface::isrPanelBitCount; volatile byte dscKeybusInterface::isrPanelBitTotal; volatile byte dscKeybusInterface::isrModuleData[dscReadSize]; -volatile byte dscKeybusInterface::isrModuleByteCount; -volatile byte dscKeybusInterface::isrModuleBitCount; -volatile byte dscKeybusInterface::isrModuleBitTotal; volatile byte dscKeybusInterface::currentCmd; volatile byte dscKeybusInterface::statusCmd; volatile byte dscKeybusInterface::moduleCmd; diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index 92acbf6..a43e0ad 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -126,9 +126,9 @@ void dscKeybusInterface::printPanelMessage() { // Processes keypad and module notifications and responses to panel queries void dscKeybusInterface::printModuleMessage() { switch (moduleData[0]) { - case 0x77: printModule_0x77(); return; // Keypad fire alarm | Structure: complete | Content: complete - case 0xBB: printModule_0xBB(); return; // Keypad auxiliary alarm | Structure: complete | Content: complete - case 0xDD: printModule_0xDD(); return; // Keypad panic alarm | Structure: complete | Content: complete + case 0xBB: printModule_0xBB(); return; // Keypad fire alarm | Structure: complete | Content: complete + case 0xDD: printModule_0xDD(); return; // Keypad auxiliary alarm | Structure: complete | Content: complete + case 0xEE: printModule_0xEE(); return; // Keypad panic alarm | Structure: complete | Content: complete } stream->print(F("[Module/0x")); @@ -2832,11 +2832,11 @@ void dscKeybusInterface::printPanel_0xEC() { * Structure decoding: complete * Content decoding: complete * - * Byte 0: 01110111 + * Byte 0: 10111011 * - * 01110111 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Fire alarm + * 10111011 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Fire alarm */ -void dscKeybusInterface::printModule_0x77() { +void dscKeybusInterface::printModule_0xBB() { stream->print(F("[Keypad] Fire alarm")); } @@ -2846,11 +2846,11 @@ void dscKeybusInterface::printModule_0x77() { * Structure decoding: complete * Content decoding: complete * - * Byte 0: 10111011 + * Byte 0: 11011101 * - * 10111011 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Aux alarm + * 11011101 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Aux alarm */ -void dscKeybusInterface::printModule_0xBB() { +void dscKeybusInterface::printModule_0xDD() { stream->print(F("[Keypad] Auxiliary alarm")); } @@ -2860,11 +2860,11 @@ void dscKeybusInterface::printModule_0xBB() { * Structure decoding: complete * Content decoding: complete * - * Byte 0: 11011101 + * Byte 0: 11101110 * - * 11011101 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Panic alarm + * 11101110 1 11111111 11111111 11111111 11111111 11111111 11111111 [Keypad] Panic alarm */ -void dscKeybusInterface::printModule_0xDD() { +void dscKeybusInterface::printModule_0xEE() { stream->print(F("[Keypad] Panic alarm")); } From af721c2408ec91de217df99e192e3174ba9c2e33 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Wed, 3 Feb 2021 17:17:52 -0600 Subject: [PATCH 10/49] New: KeypadInterface sketch, directly interface keypads (without a DSC panel) --- README.md | 10 +- .../KeypadInterface/KeypadInterface.ino | 151 +++++++ .../esp32/KeypadInterface/KeypadInterface.ino | 152 +++++++ .../KeypadInterface/KeypadInterface.ino | 152 +++++++ keywords.txt | 20 + src/dscClassic.h | 2 +- src/dscKeybus.h | 3 +- src/dscKeybusInterface.h | 142 +++--- src/dscKeypad.cpp | 423 ++++++++++++++++++ src/dscKeypad.h | 91 ++++ 10 files changed, 1088 insertions(+), 58 deletions(-) create mode 100644 examples/Arduino/KeypadInterface/KeypadInterface.ino create mode 100644 examples/esp32/KeypadInterface/KeypadInterface.ino create mode 100644 examples/esp8266/KeypadInterface/KeypadInterface.ino create mode 100644 src/dscKeypad.cpp create mode 100644 src/dscKeypad.h diff --git a/README.md b/README.md index c374756..3b87b4c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, and direct control as a virtual keypad. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. +This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) * Notifications: [Telegram](https://www.telegram.org) bot, [PushBullet](https://www.pushbullet.com), [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app -* Installer code unlocking: automatic code search to unlock panels with unknown installer codes +* Keypad interface: Use keypads as physical input devices for any general purpose without needing a DSC panel. +* Installer code unlocking: Automatic code search to unlock panels with unknown installer codes See the [dscKeybusInterface-RTOS](https://github.com/taligentx/dscKeybusInterface-RTOS) repository for a port of this library to [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) - this enables a standalone esp8266 HomeKit accessory using [esp-homekit](https://github.com/maximkulkin/esp-homekit). @@ -60,6 +61,8 @@ This library uses a combination of hardware and timer interrupts to accurately c * Monitor PGM outputs 1-14 status * Virtual keypad: - Write keys to the panel for all partitions +* Keypad interface + - Use DSC keypads as physical input devices for any general purpose without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code * Direct Keybus interface: @@ -100,6 +103,7 @@ This library uses a combination of hardware and timer interrupts to accurately c ## Release notes * develop - New: DSC Classic series panel support: PC1500, PC1550 + - New: `KeypadInterface` example sketch - interfaces with DSC keypads as physical input devices for any general purpose without needing a DSC panel. - New: esp32-s2 microcontroller support * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch @@ -233,6 +237,8 @@ The included examples demonstrate how to use the library and can be used as-is o * **Unlocker**: Checks all possible 4-digit installer codes until a valid code is found, including handling keypad lockout if enabled. The valid code is output to serial as well as repeatedly flashed with the built-in LED. +* **KeypadInterface**: Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of DSC keypads as physical inputs for any general purpose. Note that this uses a different wiring setup from the standard Keybus interface, refer to the wiring diagram in the sketch. + * **KeybusReader**: Decodes and prints data from the Keybus to a serial interface, including reading from serial for the virtual keypad. This can be used to help decode the Keybus protocol and is also handy as a troubleshooting tool to verify that data is displayed without errors. For esp8266/esp32, `KeybusReaderIP` enables connectivity over WiFi. See [`src/dscKeybusPrintData.cpp`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybusPrintData.cpp) for all currently known Keybus protocol commands and messages. Issues and pull requests with additions/corrections are welcome! diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino new file mode 100644 index 0000000..5bd06db --- /dev/null +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -0,0 +1,151 @@ +/* + * DSC Keypad Interface 1.0 (Arduino) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarms + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- Ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // Arduino Uno: 3 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 15k ohm resistor ---+--- dscReadPin // Arduino Uno: 5 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Arduino Uno: 6 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include + +#define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscReadPin 5 // Arduino Uno: 2-12 +#define dscWritePin 6 // Arduino Uno: 2-12 + +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff; + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + dsc.begin(); + Serial.print(F("Keybus...")); + unsigned long keybusTime = millis(); + while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on + if (!digitalRead(dscReadPin)) keybusTime = millis(); + } + Serial.println(F("connected.")); + Serial.println(F("DSC Keybus Interface is online.")); +} + +void loop() { + + // Sets keypad lights via serial, send "-" before a light key to turn off + if (Serial.available() > 0) { + char input = Serial.read(); + switch (input) { + case '-': lightOff = true; break; + case '1': dsc.Zone1 = setLight(); break; + case '2': dsc.Zone2 = setLight(); break; + case '3': dsc.Zone3 = setLight(); break; + case '4': dsc.Zone4 = setLight(); break; + case '5': dsc.Zone5 = setLight(); break; + case '6': dsc.Zone6 = setLight(); break; + case '7': dsc.Zone7 = setLight(); break; + case '8': dsc.Zone8 = setLight(); break; + case 'r': + case 'R': dsc.Ready = setLight(); break; + case 'a': + case 'A': dsc.Armed = setLight(); break; + case 'm': + case 'M': dsc.Memory = setLight(); break; + case 'b': + case 'B': dsc.Bypass = setLight(); break; + case 't': + case 'T': dsc.Trouble = setLight(); break; + case 'p': + case 'P': dsc.Program = setLight(); break; + case 'f': + case 'F': dsc.Fire = setLight(); break; + case 'l': + case 'L': dsc.Backlight = setLight(); break; + default: break; + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.KeyAvailable) { + dsc.KeyAvailable = false; + switch (dsc.Key) { + case 0x00: Serial.println("0"); break; + case 0x05: Serial.println("1"); break; + case 0x0A: Serial.println("2"); break; + case 0x0F: Serial.println("3"); break; + case 0x11: Serial.println("4"); break; + case 0x16: Serial.println("5"); break; + case 0x1B: Serial.println("6"); break; + case 0x1C: Serial.println("7"); break; + case 0x22: Serial.println("8"); break; + case 0x27: Serial.println("9"); break; + case 0x28: Serial.println("*"); break; + case 0x2D: Serial.println("#"); break; + case 0x82: Serial.println(F("Enter")); break; + case 0xAF: Serial.println(F("Arm: Stay")); break; + case 0xB1: Serial.println(F("Arm: Away")); break; + case 0xBB: Serial.println(F("Door chime")); break; + case 0xDA: Serial.println(F("Reset")); break; + case 0xE1: Serial.println(F("Quick exit")); break; + case 0xF7: Serial.println(F("Menu navigation")); break; + case 0x0B: Serial.println(F("Fire alarm")); break; + case 0x0D: Serial.println(F("Aux alarm")); break; + case 0x0E: Serial.println(F("Panic alarm")); break; + } + } +} + + +// Sets keypad lights state +bool setLight() { + if (lightOff) { + lightOff = false; + return false; + } + else return true; +} diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino new file mode 100644 index 0000000..581ee0a --- /dev/null +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -0,0 +1,152 @@ +/* + * DSC Keypad Interface 1.0 (esp32) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarms + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- Ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // esp32: 18 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 33k ohm resistor ---+--- dscReadPin // esp32: 19 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // esp32: 21 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include + +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 + +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff; + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + dsc.begin(); + Serial.print(F("Keybus...")); + unsigned long keybusTime = millis(); + while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on + if (!digitalRead(dscReadPin)) keybusTime = millis(); + yield(); + } + Serial.println(F("connected.")); + Serial.println(F("DSC Keybus Interface is online.")); +} + +void loop() { + + // Sets keypad lights via serial, send "-" before a light key to turn off + if (Serial.available() > 0) { + char input = Serial.read(); + switch (input) { + case '-': lightOff = true; break; + case '1': dsc.Zone1 = setLight(); break; + case '2': dsc.Zone2 = setLight(); break; + case '3': dsc.Zone3 = setLight(); break; + case '4': dsc.Zone4 = setLight(); break; + case '5': dsc.Zone5 = setLight(); break; + case '6': dsc.Zone6 = setLight(); break; + case '7': dsc.Zone7 = setLight(); break; + case '8': dsc.Zone8 = setLight(); break; + case 'r': + case 'R': dsc.Ready = setLight(); break; + case 'a': + case 'A': dsc.Armed = setLight(); break; + case 'm': + case 'M': dsc.Memory = setLight(); break; + case 'b': + case 'B': dsc.Bypass = setLight(); break; + case 't': + case 'T': dsc.Trouble = setLight(); break; + case 'p': + case 'P': dsc.Program = setLight(); break; + case 'f': + case 'F': dsc.Fire = setLight(); break; + case 'l': + case 'L': dsc.Backlight = setLight(); break; + default: break; + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.KeyAvailable) { + dsc.KeyAvailable = false; + switch (dsc.Key) { + case 0x00: Serial.println("0"); break; + case 0x05: Serial.println("1"); break; + case 0x0A: Serial.println("2"); break; + case 0x0F: Serial.println("3"); break; + case 0x11: Serial.println("4"); break; + case 0x16: Serial.println("5"); break; + case 0x1B: Serial.println("6"); break; + case 0x1C: Serial.println("7"); break; + case 0x22: Serial.println("8"); break; + case 0x27: Serial.println("9"); break; + case 0x28: Serial.println("*"); break; + case 0x2D: Serial.println("#"); break; + case 0x82: Serial.println(F("Enter")); break; + case 0xAF: Serial.println(F("Arm: Stay")); break; + case 0xB1: Serial.println(F("Arm: Away")); break; + case 0xBB: Serial.println(F("Door chime")); break; + case 0xDA: Serial.println(F("Reset")); break; + case 0xE1: Serial.println(F("Quick exit")); break; + case 0xF7: Serial.println(F("Menu navigation")); break; + case 0x0B: Serial.println(F("Fire alarm")); break; + case 0x0D: Serial.println(F("Aux alarm")); break; + case 0x0E: Serial.println(F("Panic alarm")); break; + } + } +} + + +// Sets keypad lights state +bool setLight() { + if (lightOff) { + lightOff = false; + return false; + } + else return true; +} diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino new file mode 100644 index 0000000..524c296 --- /dev/null +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -0,0 +1,152 @@ +/* + * DSC Keypad Interface 1.0 (esp8266) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarms + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- Ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // esp8266: D1, GPIO 5 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 33k ohm resistor ---+--- dscReadPin // esp8266: D2, GPIO 4 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // esp8266: D8, GPIO 15 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include + +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscWritePin D8 // GPIO 15 + +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff; + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + dsc.begin(); + Serial.print(F("Keybus...")); + unsigned long keybusTime = millis(); + while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on + if (!digitalRead(dscReadPin)) keybusTime = millis(); + yield(); + } + Serial.println(F("connected.")); + Serial.println(F("DSC Keybus Interface is online.")); +} + +void loop() { + + // Sets keypad lights via serial, send "-" before a light key to turn off + if (Serial.available() > 0) { + char input = Serial.read(); + switch (input) { + case '-': lightOff = true; break; + case '1': dsc.Zone1 = setLight(); break; + case '2': dsc.Zone2 = setLight(); break; + case '3': dsc.Zone3 = setLight(); break; + case '4': dsc.Zone4 = setLight(); break; + case '5': dsc.Zone5 = setLight(); break; + case '6': dsc.Zone6 = setLight(); break; + case '7': dsc.Zone7 = setLight(); break; + case '8': dsc.Zone8 = setLight(); break; + case 'r': + case 'R': dsc.Ready = setLight(); break; + case 'a': + case 'A': dsc.Armed = setLight(); break; + case 'm': + case 'M': dsc.Memory = setLight(); break; + case 'b': + case 'B': dsc.Bypass = setLight(); break; + case 't': + case 'T': dsc.Trouble = setLight(); break; + case 'p': + case 'P': dsc.Program = setLight(); break; + case 'f': + case 'F': dsc.Fire = setLight(); break; + case 'l': + case 'L': dsc.Backlight = setLight(); break; + default: break; + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.KeyAvailable) { + dsc.KeyAvailable = false; + switch (dsc.Key) { + case 0x00: Serial.println("0"); break; + case 0x05: Serial.println("1"); break; + case 0x0A: Serial.println("2"); break; + case 0x0F: Serial.println("3"); break; + case 0x11: Serial.println("4"); break; + case 0x16: Serial.println("5"); break; + case 0x1B: Serial.println("6"); break; + case 0x1C: Serial.println("7"); break; + case 0x22: Serial.println("8"); break; + case 0x27: Serial.println("9"); break; + case 0x28: Serial.println("*"); break; + case 0x2D: Serial.println("#"); break; + case 0x82: Serial.println(F("Enter")); break; + case 0xAF: Serial.println(F("Arm: Stay")); break; + case 0xB1: Serial.println(F("Arm: Away")); break; + case 0xBB: Serial.println(F("Door chime")); break; + case 0xDA: Serial.println(F("Reset")); break; + case 0xE1: Serial.println(F("Quick exit")); break; + case 0xF7: Serial.println(F("Menu navigation")); break; + case 0x0B: Serial.println(F("Fire alarm")); break; + case 0x0D: Serial.println(F("Aux alarm")); break; + case 0x0E: Serial.println(F("Panic alarm")); break; + } + } +} + + +// Sets keypad lights state +bool setLight() { + if (lightOff) { + lightOff = false; + return false; + } + else return true; +} diff --git a/keywords.txt b/keywords.txt index b6e4ac8..95e52b6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,5 +1,6 @@ dscKeybusInterface KEYWORD1 dscClassicInterface KEYWORD1 +dscKeypadInterface KEYWORD1 dsc KEYWORD1 dscClockPin LITERAL1 @@ -10,6 +11,7 @@ dscPC16Pin LITERAL1 dscZones LITERAL1 dscPartitions LITERAL1 dscClassicSeries LITERAL1 +dscKeypad LITERAL1 hideKeypadDigits KEYWORD2 displayTrailingBits KEYWORD2 @@ -74,6 +76,24 @@ status KEYWORD2 panelData KEYWORD2 pc16Data KEYWORD2 panelVersion KEYWORD2 +Zone1 KEYWORD2 +Zone2 KEYWORD2 +Zone3 KEYWORD2 +Zone4 KEYWORD2 +Zone5 KEYWORD2 +Zone6 KEYWORD2 +Zone7 KEYWORD2 +Zone8 KEYWORD2 +Ready KEYWORD2 +Armed KEYWORD2 +Memory KEYWORD2 +Bypass KEYWORD2 +Trouble KEYWORD2 +Program KEYWORD2 +Fire KEYWORD2 +Backlight KEYWORD2 +KeyAvailable KEYWORD2 +Key KEYWORD2 printPanelBinary KEYWORD2 printPanelCommand KEYWORD2 diff --git a/src/dscClassic.h b/src/dscClassic.h index 89fb952..20c4f1d 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -208,4 +208,4 @@ class dscClassicInterface { static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; }; -#endif // dscClassic_h \ No newline at end of file +#endif // dscClassic_h diff --git a/src/dscKeybus.h b/src/dscKeybus.h index ba24a0c..868da31 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -81,7 +81,6 @@ class dscKeybusInterface { void printModuleBinary(bool printSpaces = true); // Includes spaces between bytes by default void printModuleMessage(); // Prints the decoded keypad or module message - // These can be configured in the sketch setup() before begin() bool hideKeypadDigits; // Controls if keypad digits are hidden for publicly posted logs (default: false) static bool processModuleData; // Controls if keypad and module data is processed and displayed (default: false) @@ -341,4 +340,4 @@ class dscKeybusInterface { static volatile byte isrModuleData[dscReadSize]; }; -#endif // dscKeybus_h \ No newline at end of file +#endif // dscKeybus_h diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index e358ff6..0562189 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -24,59 +24,8 @@ #include "esp_timer.h" #endif -// DSC PowerSeries -#ifndef dscClassicSeries -#include "dscKeybus.h" - -byte dscKeybusInterface::dscClockPin; -byte dscKeybusInterface::dscReadPin; -byte dscKeybusInterface::dscWritePin; -char dscKeybusInterface::writeKey; -byte dscKeybusInterface::writePartition; -byte dscKeybusInterface::writeByte; -byte dscKeybusInterface::writeBit; -bool dscKeybusInterface::virtualKeypad; -bool dscKeybusInterface::processModuleData; -byte dscKeybusInterface::panelData[dscReadSize]; -byte dscKeybusInterface::panelByteCount; -byte dscKeybusInterface::panelBitCount; -volatile bool dscKeybusInterface::writeKeyPending; -volatile byte dscKeybusInterface::moduleData[dscReadSize]; -volatile bool dscKeybusInterface::moduleDataCaptured; -volatile bool dscKeybusInterface::moduleDataDetected; -volatile byte dscKeybusInterface::moduleByteCount; -volatile byte dscKeybusInterface::moduleBitCount; -volatile bool dscKeybusInterface::writeAlarm; -volatile bool dscKeybusInterface::starKeyCheck; -volatile bool dscKeybusInterface::starKeyWait[dscPartitions]; -volatile bool dscKeybusInterface::bufferOverflow; -volatile byte dscKeybusInterface::panelBufferLength; -volatile byte dscKeybusInterface::panelBuffer[dscBufferSize][dscReadSize]; -volatile byte dscKeybusInterface::panelBufferBitCount[dscBufferSize]; -volatile byte dscKeybusInterface::panelBufferByteCount[dscBufferSize]; -volatile byte dscKeybusInterface::isrPanelData[dscReadSize]; -volatile byte dscKeybusInterface::isrPanelByteCount; -volatile byte dscKeybusInterface::isrPanelBitCount; -volatile byte dscKeybusInterface::isrPanelBitTotal; -volatile byte dscKeybusInterface::isrModuleData[dscReadSize]; -volatile byte dscKeybusInterface::currentCmd; -volatile byte dscKeybusInterface::statusCmd; -volatile byte dscKeybusInterface::moduleCmd; -volatile byte dscKeybusInterface::moduleSubCmd; -volatile unsigned long dscKeybusInterface::clockHighTime; -volatile unsigned long dscKeybusInterface::keybusTime; - -// Interrupt function called after 250us by dscClockInterrupt() using AVR Timer1, disables the timer and calls -// dscDataInterrupt() to read the data line -#if defined(__AVR__) -ISR(TIMER1_OVF_vect) { - TCCR1B = 0; // Disables Timer1 - dscKeybusInterface::dscDataInterrupt(); -} -#endif // __AVR__ - // DSC Classic Series -#elif defined dscClassicSeries +#if defined dscClassicSeries #include "dscClassic.h" byte dscClassicInterface::dscClockPin; @@ -134,5 +83,92 @@ ISR(TIMER1_OVF_vect) { } #endif // __AVR__ -#endif // dscClassicSeries + +// DSC Keypad Interface +#elif defined dscKeypad +#include "dscKeypad.h" + +byte dscKeypadInterface::dscClockPin; +byte dscKeypadInterface::dscReadPin; +byte dscKeypadInterface::dscWritePin; +int dscKeypadInterface::clockInterval; +volatile byte dscKeypadInterface::keyData; +volatile byte dscKeypadInterface::alarmKeyData; +volatile bool dscKeypadInterface::commandReady; +volatile bool dscKeypadInterface::moduleDataDetected; +volatile bool dscKeypadInterface::moduleDataCaptured; +volatile bool dscKeypadInterface::alarmKeyDetected; +volatile bool dscKeypadInterface::alarmKeyCaptured; +volatile bool dscKeypadInterface::alarmKeyResponsePending; +volatile byte dscKeypadInterface::clockCycleCount; +volatile byte dscKeypadInterface::clockCycleTotal; +volatile byte dscKeypadInterface::panelCommand[dscReadSize]; +volatile byte dscKeypadInterface::isrPanelBitTotal; +volatile byte dscKeypadInterface::isrPanelBitCount; +volatile byte dscKeypadInterface::panelCommandByteCount; +volatile byte dscKeypadInterface::isrModuleData[dscReadSize]; +volatile byte dscKeypadInterface::isrModuleBitTotal; +volatile byte dscKeypadInterface::isrModuleBitCount; +volatile byte dscKeypadInterface::isrModuleByteCount; +volatile byte dscKeypadInterface::panelCommandByteTotal; +volatile byte dscKeypadInterface::moduleData[dscReadSize]; + +#if defined(__AVR__) +ISR(TIMER1_OVF_vect) { + dscKeypadInterface::dscClockInterrupt(); +} +#endif // __AVR__ + + +// DSC PowerSeries +#else +#include "dscKeybus.h" + +byte dscKeybusInterface::dscClockPin; +byte dscKeybusInterface::dscReadPin; +byte dscKeybusInterface::dscWritePin; +char dscKeybusInterface::writeKey; +byte dscKeybusInterface::writePartition; +byte dscKeybusInterface::writeByte; +byte dscKeybusInterface::writeBit; +bool dscKeybusInterface::virtualKeypad; +bool dscKeybusInterface::processModuleData; +byte dscKeybusInterface::panelData[dscReadSize]; +byte dscKeybusInterface::panelByteCount; +byte dscKeybusInterface::panelBitCount; +volatile bool dscKeybusInterface::writeKeyPending; +volatile byte dscKeybusInterface::moduleData[dscReadSize]; +volatile bool dscKeybusInterface::moduleDataCaptured; +volatile bool dscKeybusInterface::moduleDataDetected; +volatile byte dscKeybusInterface::moduleByteCount; +volatile byte dscKeybusInterface::moduleBitCount; +volatile bool dscKeybusInterface::writeAlarm; +volatile bool dscKeybusInterface::starKeyCheck; +volatile bool dscKeybusInterface::starKeyWait[dscPartitions]; +volatile bool dscKeybusInterface::bufferOverflow; +volatile byte dscKeybusInterface::panelBufferLength; +volatile byte dscKeybusInterface::panelBuffer[dscBufferSize][dscReadSize]; +volatile byte dscKeybusInterface::panelBufferBitCount[dscBufferSize]; +volatile byte dscKeybusInterface::panelBufferByteCount[dscBufferSize]; +volatile byte dscKeybusInterface::isrPanelData[dscReadSize]; +volatile byte dscKeybusInterface::isrPanelByteCount; +volatile byte dscKeybusInterface::isrPanelBitCount; +volatile byte dscKeybusInterface::isrPanelBitTotal; +volatile byte dscKeybusInterface::isrModuleData[dscReadSize]; +volatile byte dscKeybusInterface::currentCmd; +volatile byte dscKeybusInterface::statusCmd; +volatile byte dscKeybusInterface::moduleCmd; +volatile byte dscKeybusInterface::moduleSubCmd; +volatile unsigned long dscKeybusInterface::clockHighTime; +volatile unsigned long dscKeybusInterface::keybusTime; + +// Interrupt function called after 250us by dscClockInterrupt() using AVR Timer1, disables the timer and calls +// dscDataInterrupt() to read the data line +#if defined(__AVR__) +ISR(TIMER1_OVF_vect) { + TCCR1B = 0; // Disables Timer1 + dscKeybusInterface::dscDataInterrupt(); +} +#endif // __AVR__ +#endif // dscClassicSeries, dscKeypadInterface #endif // dscKeybusInterface_h diff --git a/src/dscKeypad.cpp b/src/dscKeypad.cpp new file mode 100644 index 0000000..3895cca --- /dev/null +++ b/src/dscKeypad.cpp @@ -0,0 +1,423 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "dscKeypad.h" + + +#if defined(ESP32) +portMUX_TYPE dscKeypadInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; + +#if ESP_IDF_VERSION_MAJOR < 4 +hw_timer_t * dscKeypadInterface::timer0 = NULL; + +#else // ESP-IDF 4+ +esp_timer_handle_t timer2; +const esp_timer_create_args_t timer2Parameters = { .callback = reinterpret_cast(&dscKeypadInterface::dscClockInterrupt) }; + +#endif // ESP_IDF_VERSION_MAJOR +#endif // ESP32 + + +dscKeypadInterface::dscKeypadInterface(byte setClockPin, byte setReadPin, byte setWritePin) { + dscClockPin = setClockPin; + dscReadPin = setReadPin; + dscWritePin = setWritePin; + alarmKeyData = 0xFF; + commandReady = true; + keyData = 0xFF; + clockInterval = 57800; // Sets AVR timer 1 to trigger an overflow interrupt every ~500us to generate a 1kHz clock signal +} + + +void dscKeypadInterface::begin(Stream &_stream) { + pinMode(dscClockPin, OUTPUT); + pinMode(dscReadPin, INPUT); + pinMode(dscWritePin, OUTPUT); + digitalWrite(dscClockPin, LOW); + digitalWrite(dscWritePin, LOW); + stream = &_stream; + + // Platform-specific timers setup the Keybus 1kHz clock signal + + // Arduino/AVR Timer1 calls ISR(TIMER1_OVF_vect) + #if defined(__AVR__) + TCCR1A = 0; + TCCR1B = 0; + TCNT1 = clockInterval; + TCCR1B |= (1 << CS10); + + // esp8266 timer1 calls dscClockInterrupt() + #elif defined(ESP8266) + timer1_isr_init(); + timer1_attachInterrupt(dscClockInterrupt); + timer1_write(2500); + + // esp32 timer0 calls dscClockInterrupt() + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timer0 = timerBegin(0, 80, true); + timerStop(timer0); + timerAttachInterrupt(timer0, &dscClockInterrupt, true); + timerAlarmWrite(timer0, 500, true); + timerAlarmEnable(timer0); + #else // IDF4+ + esp_timer_create(&timer2Parameters, &timer2); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 + + intervalStart = millis(); +} + + +bool dscKeypadInterface::loop() { + + // Sets up the next panel command once the previous command is complete + if (commandReady && millis() - intervalStart >= commandInterval) { + commandReady = false; + + // Sets the startup command sequence + if (startupCycle) { + static byte startupCommand = 0x16; + switch (startupCommand) { + case 0x16: { + for (byte i = 0; i < 5; i++) panelCommand[i] = panelCommand16[i]; + panelCommandByteTotal = 5; + startupCommand = 0x5D; + break; + } + case 0x5D: { + delay(200); + for (byte i = 0; i < 7; i++) panelCommand[i] = panelCommand5D[i]; + panelCommandByteTotal = 7; + startupCommand = 0x4C; + break; + } + case 0x4C: { + for (byte i = 0; i < 12; i++) panelCommand[i] = panelCommand4C[i]; + panelCommandByteTotal = 12; + startupCommand = 0xB1; + break; + } + case 0xB1: { + for (byte i = 0; i < 10; i++) panelCommand[i] = panelCommandB1[i]; + panelCommandByteTotal = 10; + startupCommand = 0xA5; + break; + } + case 0xA5: { + for (byte i = 0; i < 8; i++) panelCommand[i] = panelCommandA5[i]; + panelCommandByteTotal = 8; + startupCommand = 0x05; + break; + } + case 0x05: { + for (byte i = 0; i < 5; i++) panelCommand[i] = panelCommand05[i]; + panelCommandByteTotal = 5; + startupCommand = 0xD5; + break; + } + case 0xD5: { + for (byte i = 0; i < 9; i++) panelCommand[i] = panelCommandD5[i]; + panelCommandByteTotal = 9; + startupCommand = 0x27; + break; + } + case 0x27: { + for (byte i = 0; i < 7; i++) panelCommand[i] = panelCommand27[i]; + panelCommandByteTotal = 7; + startupCycle = false; + break; + } + } + } + + // Sets the next panel command to 0x1C alarm key verification if an alarm key is pressed + else if (alarmKeyDetected) { + alarmKeyDetected = false; + alarmKeyResponsePending = true; + panelCommand[0] = 0x1C; + panelCommandByteTotal = 1; + } + + // Sets the next panel command + else if (!alarmKeyResponsePending) { + + // Sets lights + if (panelLights != previousLights) { + previousLights = panelLights; + panelCommand05[1] = panelLights; + panelCommand27[1] = panelLights; + } + + // Sets next panel command to 0xD5 keypad zone query on keypad zone notification + if (panelCommand[0] == 0x05 && !bitRead(moduleData[5], 2)) { + for (byte i = 0; i < 9; i++) panelCommand[i] = panelCommandD5[i]; + panelCommandByteTotal = 9; + } + + // Sets next panel command to 0x27 zones 1-8 status if a zone changed + else if (panelZones != previousZones) { + previousZones = panelZones; + panelCommand27[5] = panelZones; + + int dataSum = 0; + for (byte panelByte = 0; panelByte < 6; panelByte++) dataSum += panelCommand27[panelByte]; + panelCommand27[6] = dataSum % 256; + + for (byte i = 0; i < 7; i++) panelCommand[i] = panelCommand27[i]; + panelCommandByteTotal = 7; + } + + // Sets next panel command to 0x05 status command + else { + for (byte i = 0; i < 5; i++) panelCommand[i] = panelCommand05[i]; + panelCommandByteTotal = 5; + } + } + clockCycleCount = 0; + clockCycleTotal = (panelCommandByteTotal * 16) + 4; + + #if defined(__AVR__) + TIMSK1 |= (1 << TOIE1); // Enables AVR Timer 1 interrupt + #elif defined(ESP8266) + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timerStart(timer0); + #else // IDF4+ + esp_timer_start_periodic(timer2, 500); + #endif // ESP_IDF_VERSION_MAJOR + #endif + } + else if (!commandReady) intervalStart = millis(); + + // Sets panel lights + if (Ready) bitWrite(panelLights, 0, 1); + else bitWrite(panelLights, 0, 0); + if (Armed) bitWrite(panelLights, 1, 1); + else bitWrite(panelLights, 1, 0); + if (Memory) bitWrite(panelLights, 2, 1); + else bitWrite(panelLights, 2, 0); + if (Bypass) bitWrite(panelLights, 3, 1); + else bitWrite(panelLights, 3, 0); + if (Trouble) bitWrite(panelLights, 4, 1); + else bitWrite(panelLights, 4, 0); + if (Program) bitWrite(panelLights, 5, 1); + else bitWrite(panelLights, 5, 0); + if (Fire) bitWrite(panelLights, 6, 1); + else bitWrite(panelLights, 6, 0); + if (Backlight) bitWrite(panelLights, 7, 1); + else bitWrite(panelLights, 7, 0); + + // Sets zone lights + if (Zone1) bitWrite(panelZones, 0, 1); + else bitWrite(panelZones, 0, 0); + if (Zone2) bitWrite(panelZones, 1, 1); + else bitWrite(panelZones, 1, 0); + if (Zone3) bitWrite(panelZones, 2, 1); + else bitWrite(panelZones, 2, 0); + if (Zone4) bitWrite(panelZones, 3, 1); + else bitWrite(panelZones, 3, 0); + if (Zone5) bitWrite(panelZones, 4, 1); + else bitWrite(panelZones, 4, 0); + if (Zone6) bitWrite(panelZones, 5, 1); + else bitWrite(panelZones, 5, 0); + if (Zone7) bitWrite(panelZones, 6, 1); + else bitWrite(panelZones, 6, 0); + if (Zone8) bitWrite(panelZones, 7, 1); + else bitWrite(panelZones, 7, 0); + + if (moduleDataCaptured) { + moduleDataCaptured = false; + if (alarmKeyData != 0xFF) { + KeyAvailable = true; + switch (alarmKeyData) { + case 0xBB: Key = 0x0B; break; // Fire alarm + case 0xDD: Key = 0x0D; break; // Aux alarm + case 0xEE: Key = 0x0E; break; // Panic alarm + default: KeyAvailable = false; break; + } + alarmKeyData = 0xFF; + } + else if (keyData != 0xFF) { + KeyAvailable = true; + switch (keyData) { + case 0x00: Key = 0x00; break; // 0 + case 0x05: Key = 0x05; break; // 1 + case 0x0A: Key = 0x0A; break; // 2 + case 0x0F: Key = 0x0F; break; // 3 + case 0x11: Key = 0x11; break; // 4 + case 0x16: Key = 0x16; break; // 5 + case 0x1B: Key = 0x1B; break; // 6 + case 0x1C: Key = 0x1C; break; // 7 + case 0x22: Key = 0x22; break; // 8 + case 0x27: Key = 0x27; break; // 9 + case 0x28: Key = 0x28; break; // * + case 0x2D: Key = 0x2D; break; // # + case 0x82: Key = 0x82; break; // Enter + case 0x87: Key = 0x87; break; // Right arrow + case 0x88: Key = 0x88; break; // Left arrow + case 0xAF: Key = 0xAF; break; // Arm: Stay + case 0xB1: Key = 0xB1; break; // Arm: Away + case 0xBB: Key = 0xBB; break; // Door chime + case 0xDA: Key = 0xDA; break; // Reset + case 0xE1: Key = 0xE1; break; // Quick exit + case 0xF7: Key = 0xF7; break; // LCD keypad navigation + default: KeyAvailable = false; break; + } + keyData = 0xFF; + } + } + + return true; +} + + +#if defined(__AVR__) +void dscKeypadInterface::dscClockInterrupt() { +#elif defined(ESP8266) +void ICACHE_RAM_ATTR dscKeypadInterface::dscClockInterrupt() { +#elif defined(ESP32) +void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { +#endif + + // Toggles the clock pin for the length of a panel command + if (clockCycleCount < clockCycleTotal) { + static bool clockHigh = true; + if (clockHigh) { + clockHigh = false; + digitalWrite(dscClockPin, HIGH); + digitalWrite(dscWritePin, LOW); + } + else { + clockHigh = true; + digitalWrite(dscClockPin, LOW); + if (isrModuleByteCount < dscReadSize) { + + // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 + if (isrModuleBitCount < 8) { + isrModuleData[isrModuleByteCount] <<= 1; + if (digitalRead(dscReadPin) == HIGH) { + isrModuleData[isrModuleByteCount] |= 1; + } + else { + moduleDataDetected = true; // Keypads and modules send data by pulling the data line low + } + } + + // Stores the stop bit by itself in byte 1 - this aligns the Keybus bytes with moduleData[] bytes + if (isrModuleBitTotal == 8) { + isrModuleData[1] = 1; // Sets the stop bit manually to 1 in byte 1 + isrModuleBitCount = 0; + isrModuleByteCount++; + } + + // Increments the bit counter if the byte is incomplete + else if (isrModuleBitCount < 7) { + isrModuleBitCount++; + } + + // Byte is complete, set the counters for the next byte + else { + isrModuleBitCount = 0; + isrModuleByteCount++; + } + + isrModuleBitTotal++; + } + + // Write panel data + if (isrPanelBitTotal == 8) { + digitalWrite(dscWritePin, HIGH); // Stop bit + if (panelCommand[0] == 0x1C) { + alarmKeyResponsePending = false; + alarmKeyData = isrModuleData[0]; + } + isrPanelBitTotal++; + } + else if (isrPanelBitCount == 7) { + if (!bitRead(panelCommand[panelCommandByteCount], 0)) digitalWrite(dscWritePin, HIGH); + isrPanelBitCount = 0; + isrPanelBitTotal++; + panelCommandByteCount++; + } + else if (panelCommandByteCount < panelCommandByteTotal) { + switch (isrPanelBitCount) { + case 0: if (!bitRead(panelCommand[panelCommandByteCount], 7)) digitalWrite(dscWritePin, HIGH); break; + case 1: if (!bitRead(panelCommand[panelCommandByteCount], 6)) digitalWrite(dscWritePin, HIGH); break; + case 2: if (!bitRead(panelCommand[panelCommandByteCount], 5)) digitalWrite(dscWritePin, HIGH); break; + case 3: if (!bitRead(panelCommand[panelCommandByteCount], 4)) digitalWrite(dscWritePin, HIGH); break; + case 4: if (!bitRead(panelCommand[panelCommandByteCount], 3)) digitalWrite(dscWritePin, HIGH); break; + case 5: if (!bitRead(panelCommand[panelCommandByteCount], 2)) digitalWrite(dscWritePin, HIGH); break; + case 6: if (!bitRead(panelCommand[panelCommandByteCount], 1)) digitalWrite(dscWritePin, HIGH); break; + } + isrPanelBitCount++; + isrPanelBitTotal++; + } + } + clockCycleCount++; + } + + // Panel command complete + else { + digitalWrite(dscClockPin, LOW); + + // Checks for module data + if (moduleDataDetected) { + moduleDataDetected = false; + moduleDataCaptured = true; + for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; + + // Checks for an alarm key press + if (isrModuleData[0] != 0xFF && panelCommand[0] != 0x1C) { + alarmKeyDetected = true; + } + + // Checks for a partition 1 key + if (isrModuleData[2] != 0xFF && panelCommand[0] == 0x05) { + keyData = isrModuleData[2]; + } + } + + // Resets counters + for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; + isrModuleBitTotal = 0; + isrModuleBitCount = 0; + isrModuleByteCount = 0; + panelCommandByteCount = 0; + isrPanelBitTotal = 0; + isrPanelBitCount = 0; + commandReady = true; + #if defined(__AVR__) + TIMSK1 = 0; // Disables AVR Timer 1 interrupt + #elif defined(ESP8266) + timer1_disable(); + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timerStop(timer0); + #else // IDF4+ + esp_timer_stop(timer2); + #endif // ESP_IDF_VERSION_MAJOR + #endif + } + + #if defined(__AVR__) + TCNT1 = clockInterval; + #endif +} diff --git a/src/dscKeypad.h b/src/dscKeypad.h new file mode 100644 index 0000000..4001ac2 --- /dev/null +++ b/src/dscKeypad.h @@ -0,0 +1,91 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef dscKeypad_h +#define dscKeypad_h + +#include + +const byte dscReadSize = 16; // Maximum bytes of a Keybus command + +class dscKeypadInterface { + + public: + dscKeypadInterface(byte setClockPin, byte setReadPin, byte setWritePin); + + // Interface control + void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default + bool loop(); // Returns true if valid panel data is available + + // Keypad key + byte Key, KeyAvailable; + + bool Ready = true, Armed, Memory, Bypass, Trouble, Program, Fire, Backlight = true; + bool Zone1, Zone2, Zone3, Zone4, Zone5, Zone6, Zone7, Zone8; + + // Status tracking + bool keybusConnected, keybusChanged; // True if data is detected on the Keybus + + // Panel Keybus commands + byte panelCommand05[5] = {0x05, 0x81, 0x01, 0x10, 0xC7}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled + byte panelCommand16[5] = {0x16, 0x0E, 0x23, 0xF1, 0x38}; // Panel version: v2.3 | Zone wiring: NC | Code length: 4 digits | *8 programming: no + byte panelCommand27[7] = {0x27, 0x81, 0x01, 0x10, 0xC7, 0x00, 0x80}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled | Zones 1-8 open: none + byte panelCommand4C[12] = {0x4C, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + byte panelCommand5D[7] = {0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D}; // Partition 1 | Status lights flashing: none | Zones 1-32 flashing: none + byte panelCommandA5[8] = {0xA5, 0x18, 0x0E, 0xED, 0x80, 0x00, 0x00, 0x38}; + byte panelCommandD5[9] = {0xD5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + byte panelCommandB1[10] = {0xB1, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xAD}; + + /* + * moduleData[] stores keypad data in an array: command [0], stop bit by itself [1], followed by the + * remaining data. These can be accessed directly in the sketch to get data that is not already tracked + * in the library. See dscKeybusPrintData.cpp for the currently known DSC commands and data. + */ + static volatile byte moduleData[dscReadSize]; + + // Timer interrupt function to capture data - declared as public for use by AVR Timer1 + static void dscClockInterrupt(); + + private: + Stream* stream; + byte panelLights = 0x81, panelZones, previousLights = 0x81, previousZones; + bool startupCycle = true; + byte commandInterval = 5; // Sets the milliseconds between panel commands + unsigned long intervalStart; + + #if defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + static hw_timer_t * timer0; + #endif + static portMUX_TYPE timer0Mux; + #endif + + static int clockInterval; + static byte dscClockPin, dscReadPin, dscWritePin; + static volatile byte keyData; + static volatile byte alarmKeyData; + static volatile bool commandReady, moduleDataDetected, moduleDataCaptured; + static volatile bool alarmKeyDetected, alarmKeyCaptured, alarmKeyResponsePending; + static volatile byte clockCycleCount, clockCycleTotal; + static volatile byte panelCommand[dscReadSize], panelCommandByteCount, panelCommandByteTotal; + static volatile byte isrPanelBitTotal, isrPanelBitCount; + static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; +}; + +#endif // dscKeypad_h From 0deac0521b568b685bea9297db36bdf52d8ae320 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Wed, 3 Feb 2021 19:00:24 -0600 Subject: [PATCH 11/49] VirtualKeypad-Web - check for Keybus connectivity before writes #199 --- README.md | 8 ++- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 58 ++++++++++++++++++- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 58 ++++++++++++++++++- 3 files changed, 115 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3b87b4c..b9f814c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. +This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. + +This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) * Notifications: [Telegram](https://www.telegram.org) bot, [PushBullet](https://www.pushbullet.com), [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app -* Keypad interface: Use keypads as physical input devices for any general purpose without needing a DSC panel. +* Keypad interface: Use DSC keypads as physical input devices for any general purpose (without a DSC panel). * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes See the [dscKeybusInterface-RTOS](https://github.com/taligentx/dscKeybusInterface-RTOS) repository for a port of this library to [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) - this enables a standalone esp8266 HomeKit accessory using [esp-homekit](https://github.com/maximkulkin/esp-homekit). @@ -61,7 +63,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * Monitor PGM outputs 1-14 status * Virtual keypad: - Write keys to the panel for all partitions -* Keypad interface +* Keypad interface: - Use DSC keypads as physical input devices for any general purpose without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index a7d5425..a03c943 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -1,5 +1,5 @@ /* - * VirtualKeypad-Web 1.0 (esp32) + * VirtualKeypad-Web 1.4 (esp32) * * Provides a virtual keypad web interface using the esp32 as a standalone web server, including * alarm memory, programming zone lights, and viewing the event buffer. To access the event buffer, @@ -29,6 +29,7 @@ * the serial output or http://dsc.local (for clients and networks that support mDNS). * * Release notes: + * 1.4 - Fix crash when pressing keys while Keybus is disconnected * 1.0 - Initial release * * Wiring: @@ -176,6 +177,36 @@ void loop() { dsc.bufferOverflow = false; } + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) { + Serial.println(F("Keybus connected")); + forceUpdate = true; + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "connected"; + serializeJson(root, outas); + ws.textAll(outas); + } + } + else { + Serial.println(F("Keybus disconnected")); + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "disconnected"; + serializeJson(root, outas); + ws.textAll(outas); + } + } + } + setLights(partition); setStatus(partition); @@ -1538,9 +1569,30 @@ void resetZones() { void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + if (type == WS_EVT_CONNECT) { client->printf("{\"connected_id\": %u}", client->id()); - forceUpdate = true; + if (dsc.keybusConnected && ws.count()) { + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "connected"; + serializeJson(root, outas); + ws.textAll(outas); + } + forceUpdate = true; + } + else if (!dsc.keybusConnected && ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "disconnected"; + serializeJson(root, outas); + ws.textAll(outas); + } client->ping(); ws_ping_pong.restart(); } @@ -1583,7 +1635,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT char * const sep_at = strchr(tmp, '_'); if (sep_at != NULL) { *sep_at = '\0'; - dsc.write(sep_at + 1); + if (dsc.keybusConnected) dsc.write(sep_at + 1); } } } diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index dfbb96f..667b57c 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -1,5 +1,5 @@ /* - * VirtualKeypad-Web 1.3 (esp8266) + * VirtualKeypad-Web 1.4 (esp8266) * * Provides a virtual keypad web interface using the esp8266 as a standalone web server, including * alarm memory, programming zone lights, and viewing the event buffer. To access the event buffer, @@ -30,6 +30,7 @@ * the serial output or http://dsc.local (for clients and networks that support mDNS). * * Release notes: + * 1.4 - Fix crash when pressing keys while Keybus is disconnected * 1.3 - Add event buffer display * Display zone lights in alarm memory and programming * Added AC power status, reset, quick exit @@ -185,6 +186,36 @@ void loop() { dsc.bufferOverflow = false; } + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) { + Serial.println(F("Keybus connected")); + forceUpdate = true; + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "connected"; + serializeJson(root, outas); + ws.textAll(outas); + } + } + else { + Serial.println(F("Keybus disconnected")); + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "disconnected"; + serializeJson(root, outas); + ws.textAll(outas); + } + } + } + setLights(partition); setStatus(partition); @@ -1547,9 +1578,30 @@ void resetZones() { void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + if (type == WS_EVT_CONNECT) { client->printf("{\"connected_id\": %u}", client->id()); - forceUpdate = true; + if (dsc.keybusConnected && ws.count()) { + if (ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "connected"; + serializeJson(root, outas); + ws.textAll(outas); + } + forceUpdate = true; + } + else if (!dsc.keybusConnected && ws.count()) { + char outas[128]; + StaticJsonDocument<200> doc; + JsonObject root = doc.to(); + root["lcd_upper"] = "Keybus"; + root["lcd_lower"] = "disconnected"; + serializeJson(root, outas); + ws.textAll(outas); + } client->ping(); ws_ping_pong.restart(); } @@ -1592,7 +1644,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT char * const sep_at = strchr(tmp, '_'); if (sep_at != NULL) { *sep_at = '\0'; - dsc.write(sep_at + 1); + if (dsc.keybusConnected) dsc.write(sep_at + 1); } } } From f3cfc4125d5edef62d7e441a138ba5fb5e0f812f Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Wed, 3 Feb 2021 19:32:42 -0600 Subject: [PATCH 12/49] Update Classic series configuration notes --- README.md | 6 ++++-- examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- examples/Arduino/KeybusReader/KeybusReader.ino | 2 +- examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/Arduino/Status/Status.ino | 2 +- examples/esp32/Email/Email.ino | 2 +- examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- examples/esp32/Homey/Homey.ino | 2 +- examples/esp32/KeybusReader/KeybusReader.ino | 2 +- examples/esp32/KeybusReaderIP/KeybusReaderIP.ino | 2 +- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/esp32/Pushbullet/Pushbullet.ino | 2 +- examples/esp32/Status/Status.ino | 2 +- examples/esp32/Telegram/Telegram.ino | 2 +- examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 2 +- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 2 +- examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 2 +- examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino | 2 +- examples/esp8266/Email/Email.ino | 2 +- examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- examples/esp8266/Homey/Homey.ino | 2 +- examples/esp8266/KeybusReader/KeybusReader.ino | 2 +- examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino | 2 +- examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/esp8266/Pushbullet/Pushbullet.ino | 2 +- examples/esp8266/Status/Status.ino | 2 +- examples/esp8266/Telegram/Telegram.ino | 2 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 2 +- .../esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 2 +- examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino | 2 +- 33 files changed, 36 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b9f814c..d2767d0 100644 --- a/README.md +++ b/README.md @@ -343,8 +343,10 @@ Panel options affecting this interface, configured by `*8 + installer code` - se - AC power failure reporting delay: The default delay is 30 minutes and can be set to `000` to immediately report a power failure. -* PC1550 Classic series section `19`: - - PC16-OUT: Enable option 4 to set the PGM output to PC16 mode to send required panel status data on the Keybus. +* PC1500/PC1550 Classic series - the following configuration is required to get the security system status: + - Communicator: Enable in section `12`, option `1` to support PC16-OUT mode + - PC16-OUT: Enable section `13`, option `4` to set the PGM output to PC16-OUT mode to send required panel status data on the Keybus. + - PGM output: Enable section `24`, option `08` to set the PGM output to trigger while the system alarm is tripped (works together with PC16-OUT mode). ## Notes * For OTA updates on esp8266 and esp32, you may need to stop the interface using `dsc.stop();`: diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index c05a0bc..a41314a 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -200,7 +200,7 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index c1a58ff..e339009 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -191,7 +191,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/Arduino/KeybusReader/KeybusReader.ino b/examples/Arduino/KeybusReader/KeybusReader.ino index f901812..7e94cb8 100644 --- a/examples/Arduino/KeybusReader/KeybusReader.ino +++ b/examples/Arduino/KeybusReader/KeybusReader.ino @@ -46,7 +46,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index 8ebac8b..a21dcb0 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -121,7 +121,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/Arduino/Status/Status.ino b/examples/Arduino/Status/Status.ino index 66d1e7a..347ee47 100644 --- a/examples/Arduino/Status/Status.ino +++ b/examples/Arduino/Status/Status.ino @@ -46,7 +46,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index 4b6ae4e..f3ceced 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -37,7 +37,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index e715072..e2e5966 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -199,7 +199,7 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index a4a9ca0..3291719 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -187,7 +187,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index 582c9d2..b1da9ff 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -52,7 +52,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/KeybusReader/KeybusReader.ino b/examples/esp32/KeybusReader/KeybusReader.ino index 8279e53..d97d520 100644 --- a/examples/esp32/KeybusReader/KeybusReader.ino +++ b/examples/esp32/KeybusReader/KeybusReader.ino @@ -46,7 +46,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index f83f0e2..3614557 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -52,7 +52,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index 95780e0..1a89965 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -129,7 +129,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index a955b62..be62ec5 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -41,7 +41,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Status/Status.ino b/examples/esp32/Status/Status.ino index 1cd81d4..49d4fc2 100644 --- a/examples/esp32/Status/Status.ino +++ b/examples/esp32/Status/Status.ino @@ -45,7 +45,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index bc3c464..496920c 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -65,7 +65,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index fbc1248..bacc67d 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -42,7 +42,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries // Configures GSM modem model. Must be done before including TinyGsmClient library. diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 451a1d4..4bc053c 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -36,7 +36,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index cc4adb6..798edfe 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -93,7 +93,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index a03c943..0aefc40 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -70,7 +70,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index a24b0d3..5765be9 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -42,7 +42,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 802b7fc..be53533 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -207,7 +207,7 @@ entity: alarm_control_panel.security_partition_1 * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index a5dc52a..2bf45c3 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -193,7 +193,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index 53fea35..1b4b1f0 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -55,7 +55,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/KeybusReader/KeybusReader.ino b/examples/esp8266/KeybusReader/KeybusReader.ino index dc06cbe..32f4558 100644 --- a/examples/esp8266/KeybusReader/KeybusReader.ino +++ b/examples/esp8266/KeybusReader/KeybusReader.ino @@ -47,7 +47,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index f11b051..d9fbc5c 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -53,7 +53,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index 1ec293d..686450f 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -130,7 +130,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 64c2d22..7f9a4fe 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -46,7 +46,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Status/Status.ino b/examples/esp8266/Status/Status.ino index 09d4242..a7780f3 100644 --- a/examples/esp8266/Status/Status.ino +++ b/examples/esp8266/Status/Status.ino @@ -47,7 +47,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index 6b7e27a..ec7a8c1 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -65,7 +65,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index e45cce0..88b94d3 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -41,7 +41,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 32d8f7d..5bdb097 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -94,7 +94,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 667b57c..26071f5 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -79,7 +79,7 @@ * This example code is in the public domain. */ -// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration: *8 section 19, option 4 on). +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) //#define dscClassicSeries #include From 7d0ee635a9b346e96b98ebbab11c603945916f79 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 5 Feb 2021 11:26:31 -0600 Subject: [PATCH 13/49] KeypadInterface - add beep, buzzer, tone control, add MQTT example, add key data buffering #200 --- .../KeypadInterface-MQTT.ino | 350 +++++++++++++++++ .../KeypadInterface/KeypadInterface.ino | 237 +++++++++--- .../KeypadInterface-MQTT.ino | 351 ++++++++++++++++++ .../esp32/KeypadInterface/KeypadInterface.ino | 239 +++++++++--- .../KeypadInterface-MQTT.ino | 351 ++++++++++++++++++ .../KeypadInterface/KeypadInterface.ino | 239 +++++++++--- keywords.txt | 55 ++- src/dscKeybusInterface.cpp | 45 +-- src/dscKeybusInterface.h | 7 +- src/dscKeybusPrintData.cpp | 2 +- src/dscKeypad.cpp | 316 +++++++++++----- src/dscKeypad.h | 63 +++- 12 files changed, 1953 insertions(+), 302 deletions(-) create mode 100644 examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino create mode 100644 examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino create mode 100644 examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino new file mode 100644 index 0000000..ca131e3 --- /dev/null +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -0,0 +1,350 @@ +/* + * DSC Keypad Interface-MQTT 1.1 (Arduino with Ethernet) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * + * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- Arduino ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // Arduino Uno: 3 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 15k ohm resistor ---+--- dscReadPin // Arduino Uno: 5 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Arduino Uno: 6 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include +#include +#include +#include + +// Settings +byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network +const char* mqttServer = ""; // MQTT server domain name or IP address +const int mqttPort = 1883; // MQTT server port +const char* mqttUsername = ""; // Optional, leave blank if not required +const char* mqttPassword = ""; // Optional, leave blank if not required + +// MQTT topics +const char* mqttClientName = "dscKeypadInterface"; +const char* mqttKeyTopic = "dsc/Key"; // Sends keypad keys +const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send to the keypad + +// Configures the Keybus interface with the specified pins +#define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscReadPin 5 // Arduino Uno: 2-12 +#define dscWritePin 6 // Arduino Uno: 2-12 + +// Initialize components +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 50; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; +EthernetClient ipClient; +PubSubClient mqtt(mqttServer, mqttPort, ipClient); +unsigned long mqttPreviousTime; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + // Initializes ethernet with DHCP + Serial.print(F("Ethernet....")); + while(!Ethernet.begin(mac)) { + Serial.println(F("DHCP failed. Retrying...")); + delay(5000); + } + Serial.print(F("connected: ")); + Serial.println(Ethernet.localIP()); + + mqtt.setCallback(mqttCallback); + if (mqttConnect()) mqttPreviousTime = millis(); + else mqttPreviousTime = 0; + + Serial.print(F("Keybus...")); + dsc.begin(); + Serial.println(F("connected.")); + Serial.println(F("DSC Keypad Interface is online.")); +} + +void loop() { + mqttHandle(); + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { + case 0x00: mqtt.publish(mqttKeyTopic, "0", false); break; + case 0x05: mqtt.publish(mqttKeyTopic, "1", false); break; + case 0x0A: mqtt.publish(mqttKeyTopic, "2", false); break; + case 0x0F: mqtt.publish(mqttKeyTopic, "3", false); break; + case 0x11: mqtt.publish(mqttKeyTopic, "4", false); break; + case 0x16: mqtt.publish(mqttKeyTopic, "5", false); break; + case 0x1B: mqtt.publish(mqttKeyTopic, "6", false); break; + case 0x1C: mqtt.publish(mqttKeyTopic, "7", false); break; + case 0x22: mqtt.publish(mqttKeyTopic, "8", false); break; + case 0x27: mqtt.publish(mqttKeyTopic, "9", false); break; + case 0x28: mqtt.publish(mqttKeyTopic, "*", false); break; + case 0x2D: mqtt.publish(mqttKeyTopic, "#", false); break; + case 0x82: mqtt.publish(mqttKeyTopic, "Enter", false); break; + case 0xAF: mqtt.publish(mqttKeyTopic, "Arm: Stay", false); break; + case 0xB1: mqtt.publish(mqttKeyTopic, "Arm: Away", false); break; + case 0xBB: mqtt.publish(mqttKeyTopic, "Door chime", false); break; + case 0xDA: mqtt.publish(mqttKeyTopic, "Reset", false); break; + case 0xE1: mqtt.publish(mqttKeyTopic, "Quick exit", false); break; + case 0xF7: mqtt.publish(mqttKeyTopic, "Menu navigation", false); break; + case 0x0B: mqtt.publish(mqttKeyTopic, "Fire alarm", false); break; + case 0x0D: mqtt.publish(mqttKeyTopic, "Aux alarm", false); break; + case 0x0E: mqtt.publish(mqttKeyTopic, "Panic alarm", false); break; + } + mqtt.subscribe(mqttSubscribeTopic); + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; + } + else break; + } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); +} + + +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { + if (lightOff) { + lightOff = false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Handles messages received in the mqttSubscribeTopic +void mqttCallback(char* topic, byte* payload, unsigned int length) { + + // Handles unused parameters + (void)topic; + + for (unsigned int i = 0; i < length; i++) { + input[i] = payload[i]; + } + + input[length] = '\0'; + if (input[0] == '\0') inputReceived = false; + else inputReceived = true; +} + + +void mqttHandle() { + if (!mqtt.connected()) { + unsigned long mqttCurrentTime = millis(); + if (mqttCurrentTime - mqttPreviousTime > 5000) { + mqttPreviousTime = mqttCurrentTime; + if (mqttConnect()) { + Serial.println(F("MQTT disconnected, successfully reconnected.")); + mqttPreviousTime = 0; + mqtt.subscribe(mqttSubscribeTopic); + } + else Serial.println(F("MQTT disconnected, failed to reconnect.")); + } + } + else mqtt.loop(); +} + + +bool mqttConnect() { + Serial.print(F("MQTT....")); + if (mqtt.connect(mqttClientName, mqttUsername, mqttPassword)) { + Serial.print(F("connected: ")); + Serial.println(mqttServer); + mqtt.subscribe(mqttSubscribeTopic); + } + else { + Serial.print(F("connection error: ")); + Serial.println(mqttServer); + } + return mqtt.connected(); +} diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 5bd06db..b4306f4 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.0 (Arduino) + * DSC Keypad Interface 1.1 (Arduino) * * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of * DSC keypads as physical inputs for any general purpose. @@ -9,16 +9,23 @@ * lower voltages down to 7v may work for key presses (the LEDs will be dim). * * Supported features: - * - Read keypad key button presses, including fire/aux/panic alarms - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * * Wiring: * DSC Keypad R --- 12v DC * - * DSC Keypad B --- Ground + * DSC Keypad B --- Arduino ground * * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC * | @@ -50,12 +57,19 @@ #include +// Configures the Keybus interface with the specified pins #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); -bool lightOff; + +// Initialize components +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 50; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; + void setup() { Serial.begin(115200); @@ -63,57 +77,67 @@ void setup() { Serial.println(); Serial.println(); - dsc.begin(); Serial.print(F("Keybus...")); - unsigned long keybusTime = millis(); - while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on - if (!digitalRead(dscReadPin)) keybusTime = millis(); - } + dsc.begin(); Serial.println(F("connected.")); - Serial.println(F("DSC Keybus Interface is online.")); + Serial.println(F("DSC Keypad Interface is online.")); } void loop() { - // Sets keypad lights via serial, send "-" before a light key to turn off - if (Serial.available() > 0) { - char input = Serial.read(); - switch (input) { - case '-': lightOff = true; break; - case '1': dsc.Zone1 = setLight(); break; - case '2': dsc.Zone2 = setLight(); break; - case '3': dsc.Zone3 = setLight(); break; - case '4': dsc.Zone4 = setLight(); break; - case '5': dsc.Zone5 = setLight(); break; - case '6': dsc.Zone6 = setLight(); break; - case '7': dsc.Zone7 = setLight(); break; - case '8': dsc.Zone8 = setLight(); break; - case 'r': - case 'R': dsc.Ready = setLight(); break; - case 'a': - case 'A': dsc.Armed = setLight(); break; - case 'm': - case 'M': dsc.Memory = setLight(); break; - case 'b': - case 'B': dsc.Bypass = setLight(); break; - case 't': - case 'T': dsc.Trouble = setLight(); break; - case 'p': - case 'P': dsc.Program = setLight(); break; - case 'f': - case 'F': dsc.Fire = setLight(); break; - case 'l': - case 'L': dsc.Backlight = setLight(); break; - default: break; + inputSerial(); // Stores Serial data in input[], requires a newline character (NL, CR, or both) + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } } dsc.loop(); // Checks for a keypad key press - if (dsc.KeyAvailable) { - dsc.KeyAvailable = false; - switch (dsc.Key) { + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { case 0x00: Serial.println("0"); break; case 0x05: Serial.println("1"); break; case 0x0A: Serial.println("2"); break; @@ -136,16 +160,133 @@ void loop() { case 0x0B: Serial.println(F("Fire alarm")); break; case 0x0D: Serial.println(F("Aux alarm")); break; case 0x0E: Serial.println(F("Panic alarm")); break; + default: break; + } + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; } + else break; } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); } -// Sets keypad lights state -bool setLight() { +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { if (lightOff) { lightOff = false; - return false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Stores Serial data in input[], requires a newline character (NL, CR, or both) +void inputSerial() { + static byte inputCount = 0; + if (!inputReceived) { + while (Serial.available() > 0 && inputCount < inputLimit) { + input[inputCount] = Serial.read(); + if (input[inputCount] == '\n' || input[inputCount] == '\r') { + input[inputCount] = '\0'; + inputCount = 0; + inputReceived = true; + break; + } + else inputCount++; + } + if (input[0] == '\0') inputReceived = false; } - else return true; } diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino new file mode 100644 index 0000000..0e8b575 --- /dev/null +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -0,0 +1,351 @@ +/* + * DSC Keypad Interface-MQTT 1.1 (esp32) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * + * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- esp32 ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // esp32: 18 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 33k ohm resistor ---+--- dscReadPin // esp32: 19 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // esp32: 21 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* mqttServer = ""; // MQTT server domain name or IP address +const int mqttPort = 1883; // MQTT server port +const char* mqttUsername = ""; // Optional, leave blank if not required +const char* mqttPassword = ""; // Optional, leave blank if not required + +// MQTT topics +const char* mqttClientName = "dscKeypadInterface"; +const char* mqttKeyTopic = "dsc/Key"; // Sends keypad keys +const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send to the keypad + +// Configures the Keybus interface with the specified pins +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 + +// Initialize components +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 255; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; +WiFiClient ipClient; +PubSubClient mqtt(mqttServer, mqttPort, ipClient); +unsigned long mqttPreviousTime; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + mqtt.setCallback(mqttCallback); + if (mqttConnect()) mqttPreviousTime = millis(); + else mqttPreviousTime = 0; + + Serial.print(F("Keybus...")); + dsc.begin(); + Serial.println(F("connected.")); + Serial.println(F("DSC Keypad Interface is online.")); +} + +void loop() { + mqttHandle(); + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { + case 0x00: mqtt.publish(mqttKeyTopic, "0", false); break; + case 0x05: mqtt.publish(mqttKeyTopic, "1", false); break; + case 0x0A: mqtt.publish(mqttKeyTopic, "2", false); break; + case 0x0F: mqtt.publish(mqttKeyTopic, "3", false); break; + case 0x11: mqtt.publish(mqttKeyTopic, "4", false); break; + case 0x16: mqtt.publish(mqttKeyTopic, "5", false); break; + case 0x1B: mqtt.publish(mqttKeyTopic, "6", false); break; + case 0x1C: mqtt.publish(mqttKeyTopic, "7", false); break; + case 0x22: mqtt.publish(mqttKeyTopic, "8", false); break; + case 0x27: mqtt.publish(mqttKeyTopic, "9", false); break; + case 0x28: mqtt.publish(mqttKeyTopic, "*", false); break; + case 0x2D: mqtt.publish(mqttKeyTopic, "#", false); break; + case 0x82: mqtt.publish(mqttKeyTopic, "Enter", false); break; + case 0xAF: mqtt.publish(mqttKeyTopic, "Arm: Stay", false); break; + case 0xB1: mqtt.publish(mqttKeyTopic, "Arm: Away", false); break; + case 0xBB: mqtt.publish(mqttKeyTopic, "Door chime", false); break; + case 0xDA: mqtt.publish(mqttKeyTopic, "Reset", false); break; + case 0xE1: mqtt.publish(mqttKeyTopic, "Quick exit", false); break; + case 0xF7: mqtt.publish(mqttKeyTopic, "Menu navigation", false); break; + case 0x0B: mqtt.publish(mqttKeyTopic, "Fire alarm", false); break; + case 0x0D: mqtt.publish(mqttKeyTopic, "Aux alarm", false); break; + case 0x0E: mqtt.publish(mqttKeyTopic, "Panic alarm", false); break; + } + mqtt.subscribe(mqttSubscribeTopic); + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; + } + else break; + } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); +} + + +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { + if (lightOff) { + lightOff = false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Handles messages received in the mqttSubscribeTopic +void mqttCallback(char* topic, byte* payload, unsigned int length) { + + // Handles unused parameters + (void)topic; + + for (unsigned int i = 0; i < length; i++) { + input[i] = payload[i]; + } + + input[length] = '\0'; + if (input[0] == '\0') inputReceived = false; + else inputReceived = true; +} + + +void mqttHandle() { + if (!mqtt.connected()) { + unsigned long mqttCurrentTime = millis(); + if (mqttCurrentTime - mqttPreviousTime > 5000) { + mqttPreviousTime = mqttCurrentTime; + if (mqttConnect()) { + Serial.println(F("MQTT disconnected, successfully reconnected.")); + mqttPreviousTime = 0; + mqtt.subscribe(mqttSubscribeTopic); + } + else Serial.println(F("MQTT disconnected, failed to reconnect.")); + } + } + else mqtt.loop(); +} + + +bool mqttConnect() { + Serial.print(F("MQTT....")); + if (mqtt.connect(mqttClientName, mqttUsername, mqttPassword)) { + Serial.print(F("connected: ")); + Serial.println(mqttServer); + mqtt.subscribe(mqttSubscribeTopic); + } + else { + Serial.print(F("connection error: ")); + Serial.println(mqttServer); + } + return mqtt.connected(); +} diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index 581ee0a..0cbe79f 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.0 (esp32) + * DSC Keypad Interface 1.1 (esp32) * * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of * DSC keypads as physical inputs for any general purpose. @@ -9,16 +9,23 @@ * lower voltages down to 7v may work for key presses (the LEDs will be dim). * * Supported features: - * - Read keypad key button presses, including fire/aux/panic alarms - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * * Wiring: * DSC Keypad R --- 12v DC * - * DSC Keypad B --- Ground + * DSC Keypad B --- esp32 ground * * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC * | @@ -50,12 +57,19 @@ #include +// Configures the Keybus interface with the specified pins #define dscClockPin 18 // 4,13,16-39 #define dscReadPin 19 // 4,13,16-39 #define dscWritePin 21 // 4,13,16-33 dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); -bool lightOff; + +// Initialize components +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 50; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; + void setup() { Serial.begin(115200); @@ -63,58 +77,67 @@ void setup() { Serial.println(); Serial.println(); - dsc.begin(); Serial.print(F("Keybus...")); - unsigned long keybusTime = millis(); - while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on - if (!digitalRead(dscReadPin)) keybusTime = millis(); - yield(); - } + dsc.begin(); Serial.println(F("connected.")); - Serial.println(F("DSC Keybus Interface is online.")); + Serial.println(F("DSC Keypad Interface is online.")); } void loop() { - // Sets keypad lights via serial, send "-" before a light key to turn off - if (Serial.available() > 0) { - char input = Serial.read(); - switch (input) { - case '-': lightOff = true; break; - case '1': dsc.Zone1 = setLight(); break; - case '2': dsc.Zone2 = setLight(); break; - case '3': dsc.Zone3 = setLight(); break; - case '4': dsc.Zone4 = setLight(); break; - case '5': dsc.Zone5 = setLight(); break; - case '6': dsc.Zone6 = setLight(); break; - case '7': dsc.Zone7 = setLight(); break; - case '8': dsc.Zone8 = setLight(); break; - case 'r': - case 'R': dsc.Ready = setLight(); break; - case 'a': - case 'A': dsc.Armed = setLight(); break; - case 'm': - case 'M': dsc.Memory = setLight(); break; - case 'b': - case 'B': dsc.Bypass = setLight(); break; - case 't': - case 'T': dsc.Trouble = setLight(); break; - case 'p': - case 'P': dsc.Program = setLight(); break; - case 'f': - case 'F': dsc.Fire = setLight(); break; - case 'l': - case 'L': dsc.Backlight = setLight(); break; - default: break; + inputSerial(); // Stores Serial data in input[], requires a newline character (NL, CR, or both) + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } } dsc.loop(); // Checks for a keypad key press - if (dsc.KeyAvailable) { - dsc.KeyAvailable = false; - switch (dsc.Key) { + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { case 0x00: Serial.println("0"); break; case 0x05: Serial.println("1"); break; case 0x0A: Serial.println("2"); break; @@ -137,16 +160,134 @@ void loop() { case 0x0B: Serial.println(F("Fire alarm")); break; case 0x0D: Serial.println(F("Aux alarm")); break; case 0x0E: Serial.println(F("Panic alarm")); break; + default: break; + } + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; } + else break; } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); } -// Sets keypad lights state -bool setLight() { +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { if (lightOff) { lightOff = false; - return false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Stores Serial data in input[], requires a newline character (NL, CR, or both) +void inputSerial() { + static byte inputCount = 0; + if (!inputReceived) { + while (Serial.available() > 0 && inputCount < inputLimit) { + input[inputCount] = Serial.read(); + if (input[inputCount] == '\n' || input[inputCount] == '\r') { + input[inputCount] = '\0'; + inputCount = 0; + inputReceived = true; + break; + } + else inputCount++; + yield(); + } + if (input[0] == '\0') inputReceived = false; } - else return true; } diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino new file mode 100644 index 0000000..4d87ffb --- /dev/null +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -0,0 +1,351 @@ +/* + * DSC Keypad Interface-MQTT 1.1 (esp8266) + * + * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of + * DSC keypads as physical inputs for any general purpose. + * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * + * Supported features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * + * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone + * 1.0 - Initial release + * + * Wiring: + * DSC Keypad R --- 12v DC + * + * DSC Keypad B --- esp8266 ground + * + * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscClockPin // esp8266: D1, GPIO 5 + * Ground --- NPN emitter --/ + * + * DSC Keypad G ---+--- 1k ohm resistor --- 12v DC + * | + * +--- 33k ohm resistor ---+--- dscReadPin // esp8266: D2, GPIO 4 + * | | + * | +--- 10k ohm resistor --- Ground + * | + * +--- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // esp8266: D8, GPIO 15 + * Ground --- NPN emitter --/ + * + * The keypad interface uses NPN transistors to pull the clock and data lines low - most small + * signal NPN transistors should be suitable, for example: + * - 2N3904 + * - BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ +#define dscKeypad + +#include +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* mqttServer = ""; // MQTT server domain name or IP address +const int mqttPort = 1883; // MQTT server port +const char* mqttUsername = ""; // Optional, leave blank if not required +const char* mqttPassword = ""; // Optional, leave blank if not required + +// MQTT topics +const char* mqttClientName = "dscKeypadInterface"; +const char* mqttKeyTopic = "dsc/Key"; // Sends keypad keys +const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send to the keypad + +// Configures the Keybus interface with the specified pins +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscWritePin D8 // GPIO 15 + +// Initialize components +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 255; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; +WiFiClient ipClient; +PubSubClient mqtt(mqttServer, mqttPort, ipClient); +unsigned long mqttPreviousTime; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + mqtt.setCallback(mqttCallback); + if (mqttConnect()) mqttPreviousTime = millis(); + else mqttPreviousTime = 0; + + Serial.print(F("Keybus...")); + dsc.begin(); + Serial.println(F("connected.")); + Serial.println(F("DSC Keypad Interface is online.")); +} + +void loop() { + mqttHandle(); + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + } + + dsc.loop(); + + // Checks for a keypad key press + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { + case 0x00: mqtt.publish(mqttKeyTopic, "0", false); break; + case 0x05: mqtt.publish(mqttKeyTopic, "1", false); break; + case 0x0A: mqtt.publish(mqttKeyTopic, "2", false); break; + case 0x0F: mqtt.publish(mqttKeyTopic, "3", false); break; + case 0x11: mqtt.publish(mqttKeyTopic, "4", false); break; + case 0x16: mqtt.publish(mqttKeyTopic, "5", false); break; + case 0x1B: mqtt.publish(mqttKeyTopic, "6", false); break; + case 0x1C: mqtt.publish(mqttKeyTopic, "7", false); break; + case 0x22: mqtt.publish(mqttKeyTopic, "8", false); break; + case 0x27: mqtt.publish(mqttKeyTopic, "9", false); break; + case 0x28: mqtt.publish(mqttKeyTopic, "*", false); break; + case 0x2D: mqtt.publish(mqttKeyTopic, "#", false); break; + case 0x82: mqtt.publish(mqttKeyTopic, "Enter", false); break; + case 0xAF: mqtt.publish(mqttKeyTopic, "Arm: Stay", false); break; + case 0xB1: mqtt.publish(mqttKeyTopic, "Arm: Away", false); break; + case 0xBB: mqtt.publish(mqttKeyTopic, "Door chime", false); break; + case 0xDA: mqtt.publish(mqttKeyTopic, "Reset", false); break; + case 0xE1: mqtt.publish(mqttKeyTopic, "Quick exit", false); break; + case 0xF7: mqtt.publish(mqttKeyTopic, "Menu navigation", false); break; + case 0x0B: mqtt.publish(mqttKeyTopic, "Fire alarm", false); break; + case 0x0D: mqtt.publish(mqttKeyTopic, "Aux alarm", false); break; + case 0x0E: mqtt.publish(mqttKeyTopic, "Panic alarm", false); break; + } + mqtt.subscribe(mqttSubscribeTopic); + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; + } + else break; + } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); +} + + +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { + if (lightOff) { + lightOff = false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Handles messages received in the mqttSubscribeTopic +void mqttCallback(char* topic, byte* payload, unsigned int length) { + + // Handles unused parameters + (void)topic; + + for (unsigned int i = 0; i < length; i++) { + input[i] = payload[i]; + } + + input[length] = '\0'; + if (input[0] == '\0') inputReceived = false; + else inputReceived = true; +} + + +void mqttHandle() { + if (!mqtt.connected()) { + unsigned long mqttCurrentTime = millis(); + if (mqttCurrentTime - mqttPreviousTime > 5000) { + mqttPreviousTime = mqttCurrentTime; + if (mqttConnect()) { + Serial.println(F("MQTT disconnected, successfully reconnected.")); + mqttPreviousTime = 0; + mqtt.subscribe(mqttSubscribeTopic); + } + else Serial.println(F("MQTT disconnected, failed to reconnect.")); + } + } + else mqtt.loop(); +} + + +bool mqttConnect() { + Serial.print(F("MQTT....")); + if (mqtt.connect(mqttClientName, mqttUsername, mqttPassword)) { + Serial.print(F("connected: ")); + Serial.println(mqttServer); + mqtt.subscribe(mqttSubscribeTopic); + } + else { + Serial.print(F("connection error: ")); + Serial.println(mqttServer); + } + return mqtt.connected(); +} diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 524c296..f5f241e 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.0 (esp8266) + * DSC Keypad Interface 1.1 (esp8266) * * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of * DSC keypads as physical inputs for any general purpose. @@ -9,16 +9,23 @@ * lower voltages down to 7v may work for key presses (the LEDs will be dim). * * Supported features: - * - Read keypad key button presses, including fire/aux/panic alarms - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8 + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad beeps, 1-128: dsc.beep(3) + * - Set keypad buzzer in seconds, 1-255: dsc.tone(5) + * - Set keypad tone pattern with a number of beeps, an optional constant tone, and the interval in seconds between beeps: + * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) + * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) + * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * * Release notes: + * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * * Wiring: * DSC Keypad R --- 12v DC * - * DSC Keypad B --- Ground + * DSC Keypad B --- esp8266 ground * * DSC Keypad Y ---+--- 1k ohm resistor --- 12v DC * | @@ -50,12 +57,19 @@ #include +// Configures the Keybus interface with the specified pins #define dscClockPin D1 // GPIO 5 #define dscReadPin D2 // GPIO 4 #define dscWritePin D8 // GPIO 15 dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); -bool lightOff; + +// Initialize components +bool lightOff, lightBlink, inputReceived; +const byte inputLimit = 50; +char input[inputLimit]; +byte beepLength, buzzerLength, toneLength; + void setup() { Serial.begin(115200); @@ -63,58 +77,67 @@ void setup() { Serial.println(); Serial.println(); - dsc.begin(); Serial.print(F("Keybus...")); - unsigned long keybusTime = millis(); - while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on - if (!digitalRead(dscReadPin)) keybusTime = millis(); - yield(); - } + dsc.begin(); Serial.println(F("connected.")); - Serial.println(F("DSC Keybus Interface is online.")); + Serial.println(F("DSC Keypad Interface is online.")); } void loop() { - // Sets keypad lights via serial, send "-" before a light key to turn off - if (Serial.available() > 0) { - char input = Serial.read(); - switch (input) { - case '-': lightOff = true; break; - case '1': dsc.Zone1 = setLight(); break; - case '2': dsc.Zone2 = setLight(); break; - case '3': dsc.Zone3 = setLight(); break; - case '4': dsc.Zone4 = setLight(); break; - case '5': dsc.Zone5 = setLight(); break; - case '6': dsc.Zone6 = setLight(); break; - case '7': dsc.Zone7 = setLight(); break; - case '8': dsc.Zone8 = setLight(); break; - case 'r': - case 'R': dsc.Ready = setLight(); break; - case 'a': - case 'A': dsc.Armed = setLight(); break; - case 'm': - case 'M': dsc.Memory = setLight(); break; - case 'b': - case 'B': dsc.Bypass = setLight(); break; - case 't': - case 'T': dsc.Trouble = setLight(); break; - case 'p': - case 'P': dsc.Program = setLight(); break; - case 'f': - case 'F': dsc.Fire = setLight(); break; - case 'l': - case 'L': dsc.Backlight = setLight(); break; - default: break; + inputSerial(); // Stores Serial data in input[], requires a newline character (NL, CR, or both) + + /* + * Sets keypad status via serial with the listed keys. Light status uses custom + * values for control: off, on, blink (example: dsc.lightReady = blink;) + * + * Light on: Send the keys listed below. Turning on the armed light: "a" + * Light off: Send "-" before a light key to turn it off. Turning off the zone 4 light: "-4" + * Light blink: Send "!" before a light key to blink. Blinking the ready light: "!r" + * Beep: Send "b" followed by the number of beeps, 1-128. Setting 2 beeps: "b2" + * Buzzer: Send "z" followed by the buzzer length in seconds, 1-255. Setting the buzzer to 5 seconds: "z5" + * Tone pattern: Send "n" followed by the number of beeps 1-7, constant tone true "t" or false "f", interval between beeps 1-15s + * Setting a tone pattern with 2 beeps, no constant tone, 4 second interval: "n2f4" + * Setting a tone pattern with 3 beeps, constant tone, 2 second interval: "n3t2" + * Disabling the tone pattern: "n" + */ + if (inputReceived) { + inputReceived = false; + + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } } dsc.loop(); // Checks for a keypad key press - if (dsc.KeyAvailable) { - dsc.KeyAvailable = false; - switch (dsc.Key) { + if (dsc.keyAvailable) { + dsc.keyAvailable = false; + switch (dsc.key) { case 0x00: Serial.println("0"); break; case 0x05: Serial.println("1"); break; case 0x0A: Serial.println("2"); break; @@ -137,16 +160,134 @@ void loop() { case 0x0B: Serial.println(F("Fire alarm")); break; case 0x0D: Serial.println(F("Aux alarm")); break; case 0x0E: Serial.println(F("Panic alarm")); break; + default: break; + } + } +} + + +// Parse the number of beeps from the input +void sendBeeps(byte position) { + char inputNumber[4]; + byte beeps = 0; + beepLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[beepLength] = input[i]; + beepLength++; + if (beepLength >= 3) break; } + else break; } + + inputNumber[beepLength] = '\0'; + beeps = atoi(inputNumber); + if (beeps > 128) beeps = 128; + + dsc.beep(beeps); } -// Sets keypad lights state -bool setLight() { +// Parse the buzzer length in seconds from the input +void sendBuzzer(byte position) { + char inputNumber[4]; + byte buzzerSeconds = 0; + buzzerLength = 0; + + for (byte i = position + 1; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + inputNumber[buzzerLength] = input[i]; + buzzerLength++; + if (buzzerLength >= 3) break; + } + else break; + } + + inputNumber[buzzerLength] = '\0'; + buzzerSeconds = atoi(inputNumber); + dsc.buzzer(buzzerSeconds); +} + + +// Parse the tone pattern number of beeps, constant tone state, and interval in seconds from the input +void sendTone(byte position) { + byte beeps = 0, interval = 0, intervalLength = 0; + char beepNumber[2]; + bool toneState; + char intervalNumber[3]; + toneLength = 0; + + if (strlen(input) < 4) { + dsc.tone(0, false, 0); + return; + } + + // Parse beeps 0-7 + if (input[position + 1] >= '0' && input[position + 1] <= '9') { + beepNumber[0] = input[position + 1]; + beeps = atoi(beepNumber); + if (beeps > 7) beeps = 7; + toneLength++; + } + else return; + + // Parse constant tone value + switch (input[position + 2]) { + case 't': + case 'T': toneState = true; toneLength++; break; + case 'f': + case 'F': toneState = false; toneLength++; break; + default: toneLength--; return; + } + + // Parse interval + for (byte i = position + 3; i < strlen(input); i++) { + if (input[i] >= '0' && input[i] <= '9') { + intervalNumber[intervalLength] = input[i]; + intervalLength++; + toneLength++; + if (intervalLength >= 2) break; + } + else break; + } + intervalNumber[intervalLength] = '\0'; + interval = atoi(intervalNumber); + if (interval > 15) interval = 15; + + dsc.tone(beeps, toneState, interval); +} + + +// Sets keypad lights state - lights use custom values for control: off, on, blink (example: dsc.lightReady = blink;) +Light setLight() { if (lightOff) { lightOff = false; - return false; + return off; + } + else if (lightBlink) { + lightBlink = false; + return blink; + } + else return on; +} + + +// Stores Serial data in input[], requires a newline character (NL, CR, or both) +void inputSerial() { + static byte inputCount = 0; + if (!inputReceived) { + while (Serial.available() > 0 && inputCount < inputLimit) { + input[inputCount] = Serial.read(); + if (input[inputCount] == '\n' || input[inputCount] == '\r') { + input[inputCount] = '\0'; + inputCount = 0; + inputReceived = true; + break; + } + else inputCount++; + yield(); + } + if (input[0] == '\0') inputReceived = false; } - else return true; } diff --git a/keywords.txt b/keywords.txt index 95e52b6..ab824fe 100644 --- a/keywords.txt +++ b/keywords.txt @@ -76,24 +76,43 @@ status KEYWORD2 panelData KEYWORD2 pc16Data KEYWORD2 panelVersion KEYWORD2 -Zone1 KEYWORD2 -Zone2 KEYWORD2 -Zone3 KEYWORD2 -Zone4 KEYWORD2 -Zone5 KEYWORD2 -Zone6 KEYWORD2 -Zone7 KEYWORD2 -Zone8 KEYWORD2 -Ready KEYWORD2 -Armed KEYWORD2 -Memory KEYWORD2 -Bypass KEYWORD2 -Trouble KEYWORD2 -Program KEYWORD2 -Fire KEYWORD2 -Backlight KEYWORD2 -KeyAvailable KEYWORD2 -Key KEYWORD2 +lightReady KEYWORD2 +lightArmed KEYWORD2 +lightMemory KEYWORD2 +lightBypass KEYWORD2 +lightTrouble KEYWORD2 +lightProgram KEYWORD2 +lightFire KEYWORD2 +lightBacklight KEYWORD2 +blinkReady KEYWORD2 +blinkArmed KEYWORD2 +blinkMemory KEYWORD2 +blinkBypass KEYWORD2 +blinkTrouble KEYWORD2 +blinkProgram KEYWORD2 +blinkFire KEYWORD2 +blinkBacklight KEYWORD2 +lightZone1 KEYWORD2 +lightZone2 KEYWORD2 +lightZone3 KEYWORD2 +lightZone4 KEYWORD2 +lightZone5 KEYWORD2 +lightZone6 KEYWORD2 +lightZone7 KEYWORD2 +lightZone8 KEYWORD2 +blinkZone1 KEYWORD2 +blinkZone2 KEYWORD2 +blinkZone3 KEYWORD2 +blinkZone4 KEYWORD2 +blinkZone5 KEYWORD2 +blinkZone6 KEYWORD2 +blinkZone7 KEYWORD2 +blinkZone8 KEYWORD2 +keyAvailable KEYWORD2 +key KEYWORD2 +beep KEYWORD2 +tone KEYWORD2 +buzzer KEYWORD2 printPanelBinary KEYWORD2 printPanelCommand KEYWORD2 diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 12f60fe..014eb4e 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -519,34 +519,23 @@ void dscKeybusInterface::setWriteKey(const char receivedKey) { case '9': writeKey = 0x27; break; case '*': writeKey = 0x28; if (status[writePartition - 1] < 0x9E) starKeyCheck = true; break; case '#': writeKey = 0x2D; break; - case 'F': - case 'f': writeKey = 0xBB; writeAlarm = true; break; // Keypad fire alarm - case 'b': - case 'B': writeKey = 0x82; break; // Enter event buffer - case '>': writeKey = 0x87; break; // Event buffer right arrow - case '<': writeKey = 0x88; break; // Event buffer left arrow - case 'l': - case 'L': writeKey = 0xA5; break; // LCD keypad data request - case 's': - case 'S': writeKey = 0xAF; writeArm[writePartition - 1] = true; break; // Arm stay - case 'w': - case 'W': writeKey = 0xB1; writeArm[writePartition - 1] = true; break; // Arm away - case 'n': - case 'N': writeKey = 0xB6; writeArm[writePartition - 1] = true; break; // Arm with no entry delay (night arm) - case 'A': - case 'a': writeKey = 0xDD; writeAlarm = true; break; // Keypad auxiliary alarm - case 'c': - case 'C': writeKey = 0xBB; break; // Door chime - case 'r': - case 'R': writeKey = 0xDA; break; // Reset - case 'P': - case 'p': writeKey = 0xEE; writeAlarm = true; break; // Keypad panic alarm - case 'x': - case 'X': writeKey = 0xE1; break; // Exit - case '[': writeKey = 0xD5; break; // Command output 1 - case ']': writeKey = 0xDA; break; // Command output 2 - case '{': writeKey = 0x70; break; // Command output 3 - case '}': writeKey = 0xEC; break; // Command output 4 + case 'f': case 'F': writeKey = 0xBB; writeAlarm = true; break; // Keypad fire alarm + case 'b': case 'B': writeKey = 0x82; break; // Enter event buffer + case '>': writeKey = 0x87; break; // Event buffer right arrow + case '<': writeKey = 0x88; break; // Event buffer left arrow + case 'l': case 'L': writeKey = 0xA5; break; // LCD keypad data request + case 's': case 'S': writeKey = 0xAF; writeArm[writePartition - 1] = true; break; // Arm stay + case 'w': case 'W': writeKey = 0xB1; writeArm[writePartition - 1] = true; break; // Arm away + case 'n': case 'N': writeKey = 0xB6; writeArm[writePartition - 1] = true; break; // Arm with no entry delay (night arm) + case 'a': case 'A': writeKey = 0xDD; writeAlarm = true; break; // Keypad auxiliary alarm + case 'c': case 'C': writeKey = 0xBB; break; // Door chime + case 'r': case 'R': writeKey = 0xDA; break; // Reset + case 'p': case 'P': writeKey = 0xEE; writeAlarm = true; break; // Keypad panic alarm + case 'x': case 'X': writeKey = 0xE1; break; // Exit + case '[': writeKey = 0xD5; break; // Command output 1 + case ']': writeKey = 0xDA; break; // Command output 2 + case '{': writeKey = 0x70; break; // Command output 3 + case '}': writeKey = 0xEC; break; // Command output 4 default: { validKey = false; break; diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index 0562189..8a4fd03 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -86,6 +86,7 @@ ISR(TIMER1_OVF_vect) { // DSC Keypad Interface #elif defined dscKeypad + #include "dscKeypad.h" byte dscKeypadInterface::dscClockPin; @@ -93,12 +94,12 @@ byte dscKeypadInterface::dscReadPin; byte dscKeypadInterface::dscWritePin; int dscKeypadInterface::clockInterval; volatile byte dscKeypadInterface::keyData; -volatile byte dscKeypadInterface::alarmKeyData; +volatile byte dscKeypadInterface::keyBufferLength; +volatile byte dscKeypadInterface::keyBuffer[dscBufferSize]; +volatile bool dscKeypadInterface::bufferOverflow; volatile bool dscKeypadInterface::commandReady; volatile bool dscKeypadInterface::moduleDataDetected; -volatile bool dscKeypadInterface::moduleDataCaptured; volatile bool dscKeypadInterface::alarmKeyDetected; -volatile bool dscKeypadInterface::alarmKeyCaptured; volatile bool dscKeypadInterface::alarmKeyResponsePending; volatile byte dscKeypadInterface::clockCycleCount; volatile byte dscKeypadInterface::clockCycleTotal; diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index a43e0ad..b5717c4 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -92,7 +92,7 @@ void dscKeybusInterface::printPanelMessage() { case 0x57: printPanel_0x57(); return; // Wireless key query | Structure: complete | Content: *incomplete case 0x58: printPanel_0x58(); return; // Module status query | Structure: complete | Content: *incomplete case 0x5D: - case 0x63: printPanel_0x5D_63(); return; // Flash panel lights: status and zones 1-32, partition 2 | Structure: complete | Content: complete + case 0x63: printPanel_0x5D_63(); return; // Flash panel lights: status and zones 1-32, partitions 1-2 | Structure: complete | Content: complete case 0x64: printPanel_0x64(); return; // Beep, partition 1 | Structure: complete | Content: complete case 0x69: printPanel_0x69(); return; // Beep, partition 2 | Structure: complete | Content: complete case 0x6E: printPanel_0x6E(); return; // LCD keypad display | Structure: complete | Content: complete diff --git a/src/dscKeypad.cpp b/src/dscKeypad.cpp index 3895cca..2f4d7f1 100644 --- a/src/dscKeypad.cpp +++ b/src/dscKeypad.cpp @@ -38,7 +38,6 @@ dscKeypadInterface::dscKeypadInterface(byte setClockPin, byte setReadPin, byte s dscClockPin = setClockPin; dscReadPin = setReadPin; dscWritePin = setWritePin; - alarmKeyData = 0xFF; commandReady = true; keyData = 0xFF; clockInterval = 57800; // Sets AVR timer 1 to trigger an overflow interrupt every ~500us to generate a 1kHz clock signal @@ -82,6 +81,14 @@ void dscKeypadInterface::begin(Stream &_stream) { #endif // ESP32 intervalStart = millis(); + + unsigned long keybusTime = millis(); + while (millis() - keybusTime < 4000) { // Waits for the keypad to be powered on + if (!digitalRead(dscReadPin)) keybusTime = millis(); + #if defined(ESP8266) || defined(ESP32) + yield(); + #endif + } } @@ -184,6 +191,39 @@ bool dscKeypadInterface::loop() { panelCommandByteTotal = 7; } + else if (panelBlink != previousBlink || panelZonesBlink != previousZonesBlink) { + previousBlink = panelBlink; + previousZonesBlink = panelZonesBlink; + panelCommand5D[1] = panelBlink; + panelCommand5D[2] = panelZonesBlink; + + int dataSum = 0; + for (byte panelByte = 0; panelByte < 6; panelByte++) dataSum += panelCommand5D[panelByte]; + panelCommand5D[6] = dataSum % 256; + + for (byte i = 0; i < 7; i++) panelCommand[i] = panelCommand5D[i]; + panelCommandByteTotal = 7; + } + + // Sets next panel command to 0x64 beep if beep() is called + else if (setBeep) { + setBeep = false; + for (byte i = 0; i < 3; i++) panelCommand[i] = panelCommand64[i]; + panelCommandByteTotal = 3; + } + + else if (setTone) { + setTone = false; + for (byte i = 0; i < 3; i++) panelCommand[i] = panelCommand75[i]; + panelCommandByteTotal = 3; + } + + else if (setBuzzer) { + setBuzzer = false; + for (byte i = 0; i < 3; i++) panelCommand[i] = panelCommand7F[i]; + panelCommandByteTotal = 3; + } + // Sets next panel command to 0x05 status command else { for (byte i = 0; i < 5; i++) panelCommand[i] = panelCommand05[i]; @@ -208,87 +248,170 @@ bool dscKeypadInterface::loop() { else if (!commandReady) intervalStart = millis(); // Sets panel lights - if (Ready) bitWrite(panelLights, 0, 1); - else bitWrite(panelLights, 0, 0); - if (Armed) bitWrite(panelLights, 1, 1); - else bitWrite(panelLights, 1, 0); - if (Memory) bitWrite(panelLights, 2, 1); - else bitWrite(panelLights, 2, 0); - if (Bypass) bitWrite(panelLights, 3, 1); - else bitWrite(panelLights, 3, 0); - if (Trouble) bitWrite(panelLights, 4, 1); - else bitWrite(panelLights, 4, 0); - if (Program) bitWrite(panelLights, 5, 1); - else bitWrite(panelLights, 5, 0); - if (Fire) bitWrite(panelLights, 6, 1); - else bitWrite(panelLights, 6, 0); - if (Backlight) bitWrite(panelLights, 7, 1); - else bitWrite(panelLights, 7, 0); + panelLight(lightReady, 0); + panelLight(lightArmed, 1); + panelLight(lightMemory, 2); + panelLight(lightBypass, 3); + panelLight(lightTrouble, 4); + panelLight(lightProgram, 5); + panelLight(lightFire, 6); + panelLight(lightBacklight, 7); // Sets zone lights - if (Zone1) bitWrite(panelZones, 0, 1); - else bitWrite(panelZones, 0, 0); - if (Zone2) bitWrite(panelZones, 1, 1); - else bitWrite(panelZones, 1, 0); - if (Zone3) bitWrite(panelZones, 2, 1); - else bitWrite(panelZones, 2, 0); - if (Zone4) bitWrite(panelZones, 3, 1); - else bitWrite(panelZones, 3, 0); - if (Zone5) bitWrite(panelZones, 4, 1); - else bitWrite(panelZones, 4, 0); - if (Zone6) bitWrite(panelZones, 5, 1); - else bitWrite(panelZones, 5, 0); - if (Zone7) bitWrite(panelZones, 6, 1); - else bitWrite(panelZones, 6, 0); - if (Zone8) bitWrite(panelZones, 7, 1); - else bitWrite(panelZones, 7, 0); - - if (moduleDataCaptured) { - moduleDataCaptured = false; - if (alarmKeyData != 0xFF) { - KeyAvailable = true; - switch (alarmKeyData) { - case 0xBB: Key = 0x0B; break; // Fire alarm - case 0xDD: Key = 0x0D; break; // Aux alarm - case 0xEE: Key = 0x0E; break; // Panic alarm - default: KeyAvailable = false; break; - } - alarmKeyData = 0xFF; - } - else if (keyData != 0xFF) { - KeyAvailable = true; - switch (keyData) { - case 0x00: Key = 0x00; break; // 0 - case 0x05: Key = 0x05; break; // 1 - case 0x0A: Key = 0x0A; break; // 2 - case 0x0F: Key = 0x0F; break; // 3 - case 0x11: Key = 0x11; break; // 4 - case 0x16: Key = 0x16; break; // 5 - case 0x1B: Key = 0x1B; break; // 6 - case 0x1C: Key = 0x1C; break; // 7 - case 0x22: Key = 0x22; break; // 8 - case 0x27: Key = 0x27; break; // 9 - case 0x28: Key = 0x28; break; // * - case 0x2D: Key = 0x2D; break; // # - case 0x82: Key = 0x82; break; // Enter - case 0x87: Key = 0x87; break; // Right arrow - case 0x88: Key = 0x88; break; // Left arrow - case 0xAF: Key = 0xAF; break; // Arm: Stay - case 0xB1: Key = 0xB1; break; // Arm: Away - case 0xBB: Key = 0xBB; break; // Door chime - case 0xDA: Key = 0xDA; break; // Reset - case 0xE1: Key = 0xE1; break; // Quick exit - case 0xF7: Key = 0xF7; break; // LCD keypad navigation - default: KeyAvailable = false; break; - } - keyData = 0xFF; + zoneLight(lightZone1, 0); + zoneLight(lightZone2, 1); + zoneLight(lightZone3, 2); + zoneLight(lightZone4, 3); + zoneLight(lightZone5, 4); + zoneLight(lightZone6, 5); + zoneLight(lightZone7, 6); + zoneLight(lightZone8, 7); + + // Skips key processing if the key buffer is empty + if (keyBufferLength == 0) return false; + + // Copies data from the buffer to keyData + static byte keyBufferIndex = 1; + byte dataIndex = keyBufferIndex - 1; + keyData = keyBuffer[dataIndex]; + keyBufferIndex++; + + // Resets counters when the buffer is cleared + #if defined(ESP32) + portENTER_CRITICAL(&timer0Mux); + #else + noInterrupts(); + #endif + + if (keyBufferIndex > keyBufferLength) { + keyBufferIndex = 1; + keyBufferLength = 0; + } + + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #else + interrupts(); + #endif + + if (keyData != 0xFF) { + keyAvailable = true; + switch (keyData) { + case 0x00: key = 0x00; break; // 0 + case 0x05: key = 0x05; break; // 1 + case 0x0A: key = 0x0A; break; // 2 + case 0x0F: key = 0x0F; break; // 3 + case 0x11: key = 0x11; break; // 4 + case 0x16: key = 0x16; break; // 5 + case 0x1B: key = 0x1B; break; // 6 + case 0x1C: key = 0x1C; break; // 7 + case 0x22: key = 0x22; break; // 8 + case 0x27: key = 0x27; break; // 9 + case 0x28: key = 0x28; break; // * + case 0x2D: key = 0x2D; break; // # + case 0x82: key = 0x82; break; // Enter + case 0x87: key = 0x87; break; // Right arrow + case 0x88: key = 0x88; break; // Left arrow + case 0xAF: key = 0xAF; break; // Arm: Stay + case 0xB1: key = 0xB1; break; // Arm: Away + case 0xBB: key = 0xBB; break; // Door chime + case 0xDA: key = 0xDA; break; // Reset + case 0xE1: key = 0xE1; break; // Quick exit + case 0xF7: key = 0xF7; break; // LCD keypad navigation + case 0x0B: key = 0x0B; break; // Fire alarm + case 0x0D: key = 0x0D; break; // Aux alarm + case 0x0E: key = 0x0E; break; // Panic alarm + default: keyAvailable = false; break; // Skips other DSC key values and invalid data } + keyData = 0xFF; } return true; } +void dscKeypadInterface::panelLight(Light lightPanel, byte zoneBit) { + if (lightPanel == on) { + bitWrite(panelLights, zoneBit, 1); + bitWrite(panelBlink, zoneBit, 0); + } + else if (lightPanel == blink) bitWrite(panelBlink, zoneBit, 1); + else { + bitWrite(panelLights, zoneBit, 0); + bitWrite(panelBlink, zoneBit, 0); + } +} + + +void dscKeypadInterface::zoneLight(Light lightZone, byte zoneBit) { + if (lightZone == on ) { + bitWrite(panelZones, zoneBit, 1); + bitWrite(panelZonesBlink, zoneBit, 0); + } + else if (lightZone == blink) bitWrite(panelZonesBlink, zoneBit, 1); + else { + bitWrite(panelZones, zoneBit, 0); + bitWrite(panelZonesBlink, zoneBit, 0); + } +} + + + +void dscKeypadInterface::beep(byte beeps) { + if (!beeps) { + setBeep = false; + return; + } + + if (beeps >= 128) beeps = 255; + else beeps *= 2; + panelCommand64[1] = beeps; + + int dataSum = 0; + for (byte panelByte = 0; panelByte < 2; panelByte++) dataSum += panelCommand64[panelByte]; + panelCommand64[2] = dataSum % 256; + + setBeep = true; +} + + +void dscKeypadInterface::tone(byte beep, bool tone, byte interval) { + panelCommand75[1] = 0; + + if (tone >= 1) panelCommand75[1] |= 0x80; + + if (beep > 7) beep = 7; + if (beep >= 1) { + panelCommand75[1] |= beep << 4; + } + + if (interval > 15) interval = 15; + panelCommand75[1] |= interval; + + int dataSum = 0; + for (byte panelByte = 0; panelByte < 2; panelByte++) dataSum += panelCommand75[panelByte]; + panelCommand75[2] = dataSum % 256; + + setTone = true; +} + + +void dscKeypadInterface::buzzer(byte seconds) { + if (!seconds) { + setBuzzer = false; + return; + } + + panelCommand7F[1] = seconds; + + int dataSum = 0; + for (byte panelByte = 0; panelByte < 2; panelByte++) dataSum += panelCommand7F[panelByte]; + panelCommand7F[2] = dataSum % 256; + + setBuzzer = true; +} + + #if defined(__AVR__) void dscKeypadInterface::dscClockInterrupt() { #elif defined(ESP8266) @@ -343,29 +466,46 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { } // Write panel data + + // Panel command byte 0 complete if (isrPanelBitTotal == 8) { digitalWrite(dscWritePin, HIGH); // Stop bit + + // Checks for an alarm key sent during 0x1C alarm key verification command to save in the key buffer if (panelCommand[0] == 0x1C) { alarmKeyResponsePending = false; - alarmKeyData = isrModuleData[0]; + + if (isrModuleData[0] != 0xFF) { + if (keyBufferLength >= dscBufferSize) bufferOverflow = true; + else { + + // Converts the DSC alarm key value to handle a conflict with the door chime key (0xBB) + switch (isrModuleData[0]) { + case 0xBB: keyBuffer[keyBufferLength] = 0x0B; keyBufferLength++; break; // Fire alarm + case 0xDD: keyBuffer[keyBufferLength] = 0x0D; keyBufferLength++; break; // Aux alarm + case 0xEE: keyBuffer[keyBufferLength] = 0x0E; keyBufferLength++; break; // Panic alarm + default: break; + } + } + } } isrPanelBitTotal++; } + + // Panel command bytes bit 7 else if (isrPanelBitCount == 7) { if (!bitRead(panelCommand[panelCommandByteCount], 0)) digitalWrite(dscWritePin, HIGH); isrPanelBitCount = 0; isrPanelBitTotal++; panelCommandByteCount++; } + + // Panel command bytes bits 0-6 else if (panelCommandByteCount < panelCommandByteTotal) { - switch (isrPanelBitCount) { - case 0: if (!bitRead(panelCommand[panelCommandByteCount], 7)) digitalWrite(dscWritePin, HIGH); break; - case 1: if (!bitRead(panelCommand[panelCommandByteCount], 6)) digitalWrite(dscWritePin, HIGH); break; - case 2: if (!bitRead(panelCommand[panelCommandByteCount], 5)) digitalWrite(dscWritePin, HIGH); break; - case 3: if (!bitRead(panelCommand[panelCommandByteCount], 4)) digitalWrite(dscWritePin, HIGH); break; - case 4: if (!bitRead(panelCommand[panelCommandByteCount], 3)) digitalWrite(dscWritePin, HIGH); break; - case 5: if (!bitRead(panelCommand[panelCommandByteCount], 2)) digitalWrite(dscWritePin, HIGH); break; - case 6: if (!bitRead(panelCommand[panelCommandByteCount], 1)) digitalWrite(dscWritePin, HIGH); break; + byte bitCount = 0; + for (byte i = 7; i > 0; i--) { + if (isrPanelBitCount == bitCount && !bitRead(panelCommand[panelCommandByteCount], i)) digitalWrite(dscWritePin, HIGH); + bitCount++; } isrPanelBitCount++; isrPanelBitTotal++; @@ -381,17 +521,20 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { // Checks for module data if (moduleDataDetected) { moduleDataDetected = false; - moduleDataCaptured = true; for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; - // Checks for an alarm key press + // Checks for an alarm key press and sets a flag to send panel command 0x1C alarm key verification if (isrModuleData[0] != 0xFF && panelCommand[0] != 0x1C) { alarmKeyDetected = true; } - // Checks for a partition 1 key + // Checks for a partition 1 key to save in the key buffer if (isrModuleData[2] != 0xFF && panelCommand[0] == 0x05) { - keyData = isrModuleData[2]; + if (keyBufferLength >= dscBufferSize) bufferOverflow = true; + else { + keyBuffer[keyBufferLength] = isrModuleData[2]; + keyBufferLength++; + } } } @@ -404,6 +547,7 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { isrPanelBitTotal = 0; isrPanelBitCount = 0; commandReady = true; + #if defined(__AVR__) TIMSK1 = 0; // Disables AVR Timer 1 interrupt #elif defined(ESP8266) diff --git a/src/dscKeypad.h b/src/dscKeypad.h index 4001ac2..eb8de73 100644 --- a/src/dscKeypad.h +++ b/src/dscKeypad.h @@ -22,35 +22,46 @@ #include +#if defined(__AVR__) +const byte dscBufferSize = 10; // Number of keys to buffer if the sketch is busy +#elif defined(ESP8266) || defined (ESP32) +const byte dscBufferSize = 50; +#endif const byte dscReadSize = 16; // Maximum bytes of a Keybus command +enum Light {off, on, blink}; // Custom values for keypad lights status + class dscKeypadInterface { public: dscKeypadInterface(byte setClockPin, byte setReadPin, byte setWritePin); // Interface control - void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default - bool loop(); // Returns true if valid panel data is available + void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default + bool loop(); // Returns true if valid panel data is available + void beep(byte beeps = 0); // Keypad beep, 1-128 beeps + void tone(byte beep = 0, bool tone = false, byte interval = 0); // Keypad tone pattern, 1-7 beeps at 1-15s interval, with optional constant tone + void buzzer(byte seconds = 0); // Keypad buzzer, 1-255 seconds // Keypad key - byte Key, KeyAvailable; - - bool Ready = true, Armed, Memory, Bypass, Trouble, Program, Fire, Backlight = true; - bool Zone1, Zone2, Zone3, Zone4, Zone5, Zone6, Zone7, Zone8; + byte key, keyAvailable; - // Status tracking - bool keybusConnected, keybusChanged; // True if data is detected on the Keybus + // Keypad lights + Light lightReady = on, lightArmed, lightMemory, lightBypass, lightTrouble, lightProgram, lightFire, lightBacklight = on; + Light lightZone1, lightZone2, lightZone3, lightZone4, lightZone5, lightZone6, lightZone7, lightZone8; // Panel Keybus commands - byte panelCommand05[5] = {0x05, 0x81, 0x01, 0x10, 0xC7}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled - byte panelCommand16[5] = {0x16, 0x0E, 0x23, 0xF1, 0x38}; // Panel version: v2.3 | Zone wiring: NC | Code length: 4 digits | *8 programming: no - byte panelCommand27[7] = {0x27, 0x81, 0x01, 0x10, 0xC7, 0x00, 0x80}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled | Zones 1-8 open: none - byte panelCommand4C[12] = {0x4C, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - byte panelCommand5D[7] = {0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D}; // Partition 1 | Status lights flashing: none | Zones 1-32 flashing: none - byte panelCommandA5[8] = {0xA5, 0x18, 0x0E, 0xED, 0x80, 0x00, 0x00, 0x38}; - byte panelCommandD5[9] = {0xD5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - byte panelCommandB1[10] = {0xB1, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xAD}; + byte panelCommand05[5] = {0x05, 0x81, 0x01, 0x10, 0xC7}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled + byte panelCommand16[5] = {0x16, 0x0E, 0x23, 0xF1, 0x38}; // Panel version: v2.3 | Zone wiring: NC | Code length: 4 digits | *8 programming: no + byte panelCommand27[7] = {0x27, 0x81, 0x01, 0x10, 0xC7, 0x00, 0x80}; // Partition 1: Ready Backlight - Partition ready | Partition 2: disabled | Zones 1-8 open: none + byte panelCommand4C[12] = {0x4C, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; // Module tamper query + byte panelCommand5D[7] = {0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D}; // Partition 1 | Status lights flashing: none | Zones 1-32 flashing: none + byte panelCommand64[3] = {0x64, 0x0, 0x64}; // Beep pattern, 1-128 beeps + byte panelCommand75[3] = {0x75, 0x0, 0x75}; // Tone pattern, beeps at interval with optional constant tone + byte panelCommand7F[3] = {0x7F, 0x0, 0x7F}; // Buzzer, 1-255 seconds + byte panelCommandA5[8] = {0xA5, 0x18, 0x0E, 0xED, 0x80, 0x00, 0x00, 0x38}; // Date, time, system status messages - partitions 1-2 + byte panelCommandB1[10] = {0xB1, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xAD}; // Enabled zones 1-32 + byte panelCommandD5[9] = {0xD5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; // Keypad zone query /* * moduleData[] stores keypad data in an array: command [0], stop bit by itself [1], followed by the @@ -59,13 +70,24 @@ class dscKeypadInterface { */ static volatile byte moduleData[dscReadSize]; + // Key data buffer overflow, true if dscBufferSize needs to be increased + static volatile bool bufferOverflow; + // Timer interrupt function to capture data - declared as public for use by AVR Timer1 static void dscClockInterrupt(); private: + + void zoneLight(Light lightZone, byte zoneBit); + void panelLight(Light lightPanel, byte zoneBit); + Stream* stream; - byte panelLights = 0x81, panelZones, previousLights = 0x81, previousZones; + byte panelLights = 0x81, previousLights = 0x81; + byte panelBlink, previousBlink; + byte panelZones, previousZones; + byte panelZonesBlink, previousZonesBlink; bool startupCycle = true; + bool setBeep, setTone, setBuzzer; byte commandInterval = 5; // Sets the milliseconds between panel commands unsigned long intervalStart; @@ -79,9 +101,10 @@ class dscKeypadInterface { static int clockInterval; static byte dscClockPin, dscReadPin, dscWritePin; static volatile byte keyData; - static volatile byte alarmKeyData; - static volatile bool commandReady, moduleDataDetected, moduleDataCaptured; - static volatile bool alarmKeyDetected, alarmKeyCaptured, alarmKeyResponsePending; + static volatile byte keyBufferLength; + static volatile byte keyBuffer[dscBufferSize]; + static volatile bool commandReady, moduleDataDetected; + static volatile bool alarmKeyDetected, alarmKeyResponsePending; static volatile byte clockCycleCount, clockCycleTotal; static volatile byte panelCommand[dscReadSize], panelCommandByteCount, panelCommandByteTotal; static volatile byte isrPanelBitTotal, isrPanelBitCount; From 7b24fedeaebff89efc4234683d2c425f3f5ce639 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Tue, 9 Feb 2021 16:14:28 -0600 Subject: [PATCH 14/49] KeypadInterface - add Classic series keypad support (PC1500RK) #200 --- README.md | 6 +- .../KeypadInterface-MQTT.ino | 20 +- .../KeypadInterface/KeypadInterface.ino | 23 +- .../KeypadInterface-MQTT.ino | 20 +- .../esp32/KeypadInterface/KeypadInterface.ino | 23 +- .../KeypadInterface-MQTT.ino | 20 +- .../KeypadInterface/KeypadInterface.ino | 23 +- keywords.txt | 1 + src/dscClassicKeypad.cpp | 411 ++++++++++++++++++ src/dscClassicKeypad.h | 105 +++++ src/dscKeybusInterface.h | 42 ++ src/dscKeypad.cpp | 3 + 12 files changed, 664 insertions(+), 33 deletions(-) create mode 100644 src/dscClassicKeypad.cpp create mode 100644 src/dscClassicKeypad.h diff --git a/README.md b/README.md index d2767d0..7f20d13 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * Virtual keypad: - Write keys to the panel for all partitions * Keypad interface: - - Use DSC keypads as physical input devices for any general purpose without needing a DSC panel. + - Use DSC PowerSeries and Classic series keypads as physical input devices for any general purpose without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code * Direct Keybus interface: @@ -105,7 +105,7 @@ This library uses a combination of hardware and timer interrupts to accurately c ## Release notes * develop - New: DSC Classic series panel support: PC1500, PC1550 - - New: `KeypadInterface` example sketch - interfaces with DSC keypads as physical input devices for any general purpose without needing a DSC panel. + - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. - New: esp32-s2 microcontroller support * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch @@ -239,7 +239,7 @@ The included examples demonstrate how to use the library and can be used as-is o * **Unlocker**: Checks all possible 4-digit installer codes until a valid code is found, including handling keypad lockout if enabled. The valid code is output to serial as well as repeatedly flashed with the built-in LED. -* **KeypadInterface**: Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of DSC keypads as physical inputs for any general purpose. Note that this uses a different wiring setup from the standard Keybus interface, refer to the wiring diagram in the sketch. +* **KeypadInterface**: Interfaces directly to DSC PowerSeries and Classic series (tested with PC1500RK) keypads (without a DSC panel) to enable using these as physical inputs for any general purpose. Examples included for interfacing via serial and MQTT. Note that this uses a different wiring setup from the standard Keybus interface, refer to the wiring diagram in the example sketch. * **KeybusReader**: Decodes and prints data from the Keybus to a serial interface, including reading from serial for the virtual keypad. This can be used to help decode the Keybus protocol and is also handy as a troubleshooting tool to verify that data is displayed without errors. For esp8266/esp32, `KeybusReaderIP` enables connectivity over WiFi. diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index ca131e3..a969d7b 100644 --- a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface-MQTT 1.1 (Arduino with Ethernet) + * DSC Keypad Interface-MQTT 1.2 (Arduino with Ethernet) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include #include @@ -78,7 +86,11 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send #define dscWritePin 6 // Arduino Uno: 2-12 // Initialize components +#ifdef dscKeypad dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 50; char input[inputLimit]; diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index b4306f4..44ce431 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface 1.1 (Arduino) + * DSC Keypad Interface 1.2 (Arduino) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include @@ -62,9 +70,12 @@ #define dscReadPin 5 // Arduino Uno: 2-12 #define dscWritePin 6 // Arduino Uno: 2-12 -dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); - // Initialize components +#ifdef dscKeypad +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 50; char input[inputLimit]; diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 0e8b575..098ab0c 100644 --- a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface-MQTT 1.1 (esp32) + * DSC Keypad Interface-MQTT 1.2 (esp32) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include #include @@ -78,7 +86,11 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send #define dscWritePin 21 // 4,13,16-33 // Initialize components +#ifdef dscKeypad dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 255; char input[inputLimit]; diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index 0cbe79f..ad10a88 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface 1.1 (esp32) + * DSC Keypad Interface 1.2 (esp32) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include @@ -62,9 +70,12 @@ #define dscReadPin 19 // 4,13,16-39 #define dscWritePin 21 // 4,13,16-33 -dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); - // Initialize components +#ifdef dscKeypad +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 50; char input[inputLimit]; diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 4d87ffb..3284930 100644 --- a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface-MQTT 1.1 (esp8266) + * DSC Keypad Interface-MQTT 1.2 (esp8266) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include #include @@ -78,7 +86,11 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to send #define dscWritePin D8 // GPIO 15 // Initialize components +#ifdef dscKeypad dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 255; char input[inputLimit]; diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index f5f241e..2cbd336 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -1,14 +1,14 @@ /* - * DSC Keypad Interface 1.1 (esp8266) + * DSC Keypad Interface 1.2 (esp8266) * - * Interfaces directly to a DSC PowerSeries keypad (without a DSC panel) to enable use of - * DSC keypads as physical inputs for any general purpose. + * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to + * enable use of DSC keypads as physical inputs for any general purpose. * * This interface uses a different wiring setup from the standard Keybus interface, adding * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though * lower voltages down to 7v may work for key presses (the LEDs will be dim). * - * Supported features: + * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Backlight, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * - Set keypad beeps, 1-128: dsc.beep(3) @@ -18,7 +18,12 @@ * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) * + * Classic keypad features: + * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * * Release notes: + * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release * @@ -53,7 +58,10 @@ * * This example code is in the public domain. */ + +// Set the keypad type #define dscKeypad +//#define dscClassicKeypad #include @@ -62,9 +70,12 @@ #define dscReadPin D2 // GPIO 4 #define dscWritePin D8 // GPIO 15 -dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); - // Initialize components +#ifdef dscKeypad +dscKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicKeypadInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#endif bool lightOff, lightBlink, inputReceived; const byte inputLimit = 50; char input[inputLimit]; diff --git a/keywords.txt b/keywords.txt index ab824fe..b6f8387 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,6 +1,7 @@ dscKeybusInterface KEYWORD1 dscClassicInterface KEYWORD1 dscKeypadInterface KEYWORD1 +dscClassicKeypadInterface KEYWORD1 dsc KEYWORD1 dscClockPin LITERAL1 diff --git a/src/dscClassicKeypad.cpp b/src/dscClassicKeypad.cpp new file mode 100644 index 0000000..4baa358 --- /dev/null +++ b/src/dscClassicKeypad.cpp @@ -0,0 +1,411 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "dscClassicKeypad.h" + +#if defined(ESP32) +portMUX_TYPE dscClassicKeypadInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; + +#if ESP_IDF_VERSION_MAJOR < 4 +hw_timer_t * dscClassicKeypadInterface::timer0 = NULL; + +#else // ESP-IDF 4+ +esp_timer_handle_t timer3; +const esp_timer_create_args_t timer3Parameters = { .callback = reinterpret_cast(&dscClassicKeypadInterface::dscClockInterrupt) }; + +#endif // ESP_IDF_VERSION_MAJOR +#endif // ESP32 + + +dscClassicKeypadInterface::dscClassicKeypadInterface(byte setClockPin, byte setReadPin, byte setWritePin) { + dscClockPin = setClockPin; + dscReadPin = setReadPin; + dscWritePin = setWritePin; + commandReady = true; + keyData = 0xFF; + clockInterval = 50000; // Sets AVR timer 1 to trigger an overflow interrupt every ~1ms to generate a 500Hz clock signal + keyInterval = 150; + alarmKeyInterval = 1000; +} + + +void dscClassicKeypadInterface::begin(Stream &_stream) { + pinMode(dscClockPin, OUTPUT); + pinMode(dscReadPin, INPUT); + pinMode(dscWritePin, OUTPUT); + digitalWrite(dscClockPin, LOW); + digitalWrite(dscWritePin, LOW); + stream = &_stream; + + // Platform-specific timers setup the Keybus 1kHz clock signal + + // Arduino/AVR Timer1 calls ISR(TIMER1_OVF_vect) + #if defined(__AVR__) + TCCR1A = 0; + TCCR1B = 0; + TCNT1 = clockInterval; + TCCR1B |= (1 << CS10); + + // esp8266 timer1 calls dscClockInterrupt() + #elif defined(ESP8266) + timer1_isr_init(); + timer1_attachInterrupt(dscClockInterrupt); + timer1_write(5000); + + // esp32 timer0 calls dscClockInterrupt() + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timer0 = timerBegin(0, 80, true); + timerStop(timer0); + timerAttachInterrupt(timer0, &dscClockInterrupt, true); + timerAlarmWrite(timer0, 1000, true); + timerAlarmEnable(timer0); + #else // IDF4+ + esp_timer_create(&timer3Parameters, &timer3); + #endif // ESP_IDF_VERSION_MAJOR + #endif // ESP32 + + intervalStart = millis(); + + unsigned long keybusTime = millis(); + while (millis() - keybusTime < 100) { // Waits for the keypad to be powered on + if (!digitalRead(dscReadPin)) keybusTime = millis(); + #if defined(ESP8266) || defined(ESP32) + yield(); + #endif + } +} + + +bool dscClassicKeypadInterface::loop() { + + // Sets up the next panel command once the previous command is complete + if (commandReady && millis() - intervalStart >= commandInterval) { + commandReady = false; + + // Sets lights + if (panelLights != previousLights) { + previousLights = panelLights; + classicCommand[1] = panelLights; + } + + // Sets zones + if (panelZones != previousZones) { + previousZones = panelZones; + classicCommand[0] = panelZones; + } + + // Key beep + if (keyBeep) { + if (!beepStart) { + beepStart = true; + beepInterval = millis(); + bitWrite(classicCommand[1], 0, 1); + } + else if (millis() - beepInterval > 100) { + beepStart = false; + keyBeep = false; + bitWrite(classicCommand[1], 0, 0); + } + } + + // Sets next panel command + for (byte i = 0; i < 2; i++) panelCommand[i] = classicCommand[i]; + panelCommandByteTotal = 2; + + clockCycleCount = 0; + clockCycleTotal = panelCommandByteTotal * 16; + + #if defined(__AVR__) + TIMSK1 |= (1 << TOIE1); // Enables AVR Timer 1 interrupt + #elif defined(ESP8266) + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timerStart(timer0); + #else // IDF4+ + esp_timer_start_periodic(timer3, 1000); + #endif // ESP_IDF_VERSION_MAJOR + #endif + } + else if (!commandReady) intervalStart = millis(); + + // Sets panel lights + panelLight(lightReady, 7); + panelLight(lightArmed, 6); + panelLight(lightMemory, 5); + panelLight(lightBypass, 4); + panelLight(lightTrouble, 3); + + // Sets zone lights + zoneLight(lightZone1, 7); + zoneLight(lightZone2, 6); + zoneLight(lightZone3, 5); + zoneLight(lightZone4, 4); + zoneLight(lightZone5, 3); + zoneLight(lightZone6, 2); + + // Skips key processing if the key buffer is empty + if (keyBufferLength == 0) return false; + + // Copies data from the buffer to keyData + static byte keyBufferIndex = 1; + byte dataIndex = keyBufferIndex - 1; + keyData = keyBuffer[dataIndex]; + keyBufferIndex++; + + // Resets counters when the buffer is cleared + #if defined(ESP32) + portENTER_CRITICAL(&timer0Mux); + #else + noInterrupts(); + #endif + + if (keyBufferIndex > keyBufferLength) { + keyBufferIndex = 1; + keyBufferLength = 0; + } + + #if defined(ESP32) + portEXIT_CRITICAL(&timer0Mux); + #else + interrupts(); + #endif + + if (keyData != 0xFF) { + keyAvailable = true; + keyBeep = true; + switch (keyData) { + case 0xD7: key = 0x00; break; // 0 + case 0xBE: key = 0x05; break; // 1 + case 0xDE: key = 0x0A; break; // 2 + case 0xEE: key = 0x0F; break; // 3 + case 0xBD: key = 0x11; break; // 4 + case 0xDD: key = 0x16; break; // 5 + case 0xED: key = 0x1B; break; // 6 + case 0xBB: key = 0x1C; break; // 7 + case 0xDB: key = 0x22; break; // 8 + case 0xEB: key = 0x27; break; // 9 + case 0xB7: key = 0x28; break; // * + case 0xE7: key = 0x2D; break; // # + case 0x3F: key = 0x0B; break; // Fire alarm + case 0x5F: key = 0x0D; break; // Aux alarm + case 0x6F: key = 0x0E; break; // Panic alarm + default: keyAvailable = false; keyBeep = false; break; // Skips other DSC key values and invalid data + } + keyData = 0xFF; + } + + return true; +} + + +void dscClassicKeypadInterface::panelLight(Light lightPanel, byte zoneBit) { + if (lightPanel == on) { + bitWrite(panelLights, zoneBit, 1); + bitWrite(panelBlink, zoneBit, 0); + } + else if (lightPanel == blink) bitWrite(panelBlink, zoneBit, 1); + else { + bitWrite(panelLights, zoneBit, 0); + bitWrite(panelBlink, zoneBit, 0); + } +} + + +void dscClassicKeypadInterface::zoneLight(Light lightZone, byte zoneBit) { + if (lightZone == on ) { + bitWrite(panelZones, zoneBit, 1); + bitWrite(panelZonesBlink, zoneBit, 0); + } + else if (lightZone == blink) bitWrite(panelZonesBlink, zoneBit, 1); + else { + bitWrite(panelZones, zoneBit, 0); + bitWrite(panelZonesBlink, zoneBit, 0); + } +} + + + +void dscClassicKeypadInterface::beep(byte beeps) { + (void) beeps; +} + + +void dscClassicKeypadInterface::tone(byte beep, bool tone, byte interval) { + (void) beep; + (void) tone; + (void) interval; +} + + +void dscClassicKeypadInterface::buzzer(byte seconds) { + (void) seconds; +} + + +#if defined(__AVR__) +void dscClassicKeypadInterface::dscClockInterrupt() { +#elif defined(ESP8266) +void ICACHE_RAM_ATTR dscClassicKeypadInterface::dscClockInterrupt() { +#elif defined(ESP32) +void IRAM_ATTR dscClassicKeypadInterface::dscClockInterrupt() { +#endif + + // Toggles the clock pin for the length of a panel command + if (clockCycleCount < clockCycleTotal) { + static bool clockHigh = true; + if (clockHigh) { + clockHigh = false; + digitalWrite(dscClockPin, HIGH); + digitalWrite(dscWritePin, LOW); + } + else { + clockHigh = true; + digitalWrite(dscClockPin, LOW); + if (isrModuleByteCount < dscReadSize) { + + // Data is captured in each byte by shifting left by 1 bit and writing to bit 0 + if (isrModuleBitCount < 8) { + isrModuleData[isrModuleByteCount] <<= 1; + if (digitalRead(dscReadPin) == HIGH) { + isrModuleData[isrModuleByteCount] |= 1; + } + else { + moduleDataDetected = true; // Keypads and modules send data by pulling the data line low + } + } + + // Increments the bit counter if the byte is incomplete + if (isrModuleBitCount < 7) { + isrModuleBitCount++; + } + + // Byte is complete, set the counters for the next byte + else { + isrModuleBitCount = 0; + isrModuleByteCount++; + } + + isrModuleBitTotal++; + } + + // Write panel data + if (isrPanelBitCount == 7) { + if (!bitRead(panelCommand[panelCommandByteCount], 0)) digitalWrite(dscWritePin, HIGH); + isrPanelBitCount = 0; + isrPanelBitTotal++; + panelCommandByteCount++; + } + + // Panel command bytes bits 0-6 + else if (panelCommandByteCount < panelCommandByteTotal) { + byte bitCount = 0; + for (byte i = 7; i > 0; i--) { + if (isrPanelBitCount == bitCount && !bitRead(panelCommand[panelCommandByteCount], i)) digitalWrite(dscWritePin, HIGH); + bitCount++; + } + isrPanelBitCount++; + isrPanelBitTotal++; + } + } + clockCycleCount++; + } + + // Panel command complete + else { + digitalWrite(dscClockPin, LOW); + + // Checks for module data + if (moduleDataDetected) { + moduleDataDetected = false; + for (byte i = 0; i < dscReadSize; i++) moduleData[i] = isrModuleData[i]; + + if (isrModuleData[0] != 0xFF) { + + // Checks that alarm keys are pressed continuously for the alarmKeyInterval before setting the key + if (isrModuleData[0] == 0x3F || isrModuleData[0] == 0x5F || isrModuleData[0] == 0x6F) { + if (!alarmKeyDetected) { + alarmKeyDetected = true; + alarmKeyTime = millis(); + } + else if (millis() - alarmKeyTime > alarmKeyInterval) { + keyBuffer[keyBufferLength] = isrModuleData[0]; + keyBufferLength++; + alarmKeyDetected = false; + } + else { + keyBuffer[keyBufferLength] = 0xFF; + keyBufferLength++; + } + } + + // Checks for regular keys and debounces for keyInterval + else { + alarmKeyDetected = false; + alarmKeyTime = millis(); + + // Skips the debounce interval if a different key is pressed + if (keyBuffer[keyBufferLength] != isrModuleData[0]) { + keyBuffer[keyBufferLength] = isrModuleData[0]; + keyBufferLength++; + repeatInterval = millis(); + } + + // Sets the key + else if (millis() - repeatInterval > keyInterval) { + keyBuffer[keyBufferLength] = isrModuleData[0]; + keyBufferLength++; + repeatInterval = millis(); + } + } + } + } + else { + alarmKeyDetected = false; + alarmKeyTime = millis(); + } + + // Resets counters + for (byte i = 0; i < dscReadSize; i++) isrModuleData[i] = 0; + isrModuleBitTotal = 0; + isrModuleBitCount = 0; + isrModuleByteCount = 0; + panelCommandByteCount = 0; + isrPanelBitTotal = 0; + isrPanelBitCount = 0; + commandReady = true; + + #if defined(__AVR__) + TIMSK1 = 0; // Disables AVR Timer 1 interrupt + #elif defined(ESP8266) + timer1_disable(); + #elif defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + timerStop(timer0); + #else // IDF4+ + esp_timer_stop(timer3); + #endif // ESP_IDF_VERSION_MAJOR + #endif + } + + #if defined(__AVR__) + TCNT1 = clockInterval; + #endif +} diff --git a/src/dscClassicKeypad.h b/src/dscClassicKeypad.h new file mode 100644 index 0000000..8fc24bc --- /dev/null +++ b/src/dscClassicKeypad.h @@ -0,0 +1,105 @@ +/* + DSC Keybus Interface + + https://github.com/taligentx/dscKeybusInterface + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef dscClassicKeypad_h +#define dscClassicKeypad_h + +#include + +#if defined(__AVR__) +const byte dscBufferSize = 10; // Number of keys to buffer if the sketch is busy +#elif defined(ESP8266) || defined (ESP32) +const byte dscBufferSize = 50; +#endif +const byte dscReadSize = 2; // Maximum bytes of a Keybus command + +enum Light {off, on, blink}; // Custom values for keypad lights status + +class dscClassicKeypadInterface { + + public: + dscClassicKeypadInterface(byte setClockPin, byte setReadPin, byte setWritePin); + + // Interface control + void begin(Stream &_stream = Serial); // Initializes the stream output to Serial by default + bool loop(); // Returns true if valid panel data is available + void beep(byte beeps = 0); // Keypad beep, 1-128 beeps + void tone(byte beep = 0, bool tone = false, byte interval = 0); // Keypad tone pattern, 1-7 beeps at 1-15s interval, with optional constant tone + void buzzer(byte seconds = 0); // Keypad buzzer, 1-255 seconds + + // Keypad key + byte key, keyAvailable; + + // Keypad lights + Light lightReady = on, lightArmed, lightMemory, lightBypass, lightTrouble, lightProgram, lightFire, lightBacklight = on; + Light lightZone1, lightZone2, lightZone3, lightZone4, lightZone5, lightZone6, lightZone7, lightZone8; + + // Panel Keybus commands + byte classicCommand[2] = {0x00, 0x80}; + + /* + * moduleData[] stores keypad data in an array: command [0], stop bit by itself [1], followed by the + * remaining data. These can be accessed directly in the sketch to get data that is not already tracked + * in the library. See dscKeybusPrintData.cpp for the currently known DSC commands and data. + */ + static volatile byte moduleData[dscReadSize]; + + // Key data buffer overflow, true if dscBufferSize needs to be increased + static volatile bool bufferOverflow; + + // Timer interrupt function to capture data - declared as public for use by AVR Timer1 + static void dscClockInterrupt(); + + private: + + void zoneLight(Light lightZone, byte zoneBit); + void panelLight(Light lightPanel, byte zoneBit); + + Stream* stream; + byte panelLights = 0x80, previousLights = 0x80; + byte panelBlink, previousBlink; + byte panelZones, previousZones; + byte panelZonesBlink, previousZonesBlink; + bool startupCycle = true; + bool setBeep, setTone, setBuzzer; + byte commandInterval = 26; // Sets the milliseconds between panel commands + bool keyBeep, beepStart; + + #if defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 4 + static hw_timer_t * timer0; + #endif + static portMUX_TYPE timer0Mux; + #endif + + static int clockInterval; + static byte dscClockPin, dscReadPin, dscWritePin; + static volatile byte keyData; + static volatile byte keyBufferLength; + static volatile byte keyBuffer[dscBufferSize]; + static volatile bool commandReady, moduleDataDetected; + static volatile bool alarmKeyDetected, alarmKeyResponsePending; + static volatile byte clockCycleCount, clockCycleTotal; + static volatile byte panelCommand[dscReadSize], panelCommandByteCount, panelCommandByteTotal; + static volatile byte isrPanelBitTotal, isrPanelBitCount; + static volatile byte isrModuleData[dscReadSize], isrModuleBitTotal, isrModuleBitCount, isrModuleByteCount; + static volatile unsigned long intervalStart, beepInterval, repeatInterval, keyInterval, alarmKeyTime, alarmKeyInterval; +}; + +#endif // dscKeypad_h diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index 8a4fd03..166bb8b 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -120,6 +120,48 @@ ISR(TIMER1_OVF_vect) { } #endif // __AVR__ +// DSC Classic Keypad Interface +#elif defined dscClassicKeypad + +#include "dscClassicKeypad.h" + +byte dscClassicKeypadInterface::dscClockPin; +byte dscClassicKeypadInterface::dscReadPin; +byte dscClassicKeypadInterface::dscWritePin; +int dscClassicKeypadInterface::clockInterval; +volatile byte dscClassicKeypadInterface::keyData; +volatile byte dscClassicKeypadInterface::keyBufferLength; +volatile byte dscClassicKeypadInterface::keyBuffer[dscBufferSize]; +volatile bool dscClassicKeypadInterface::bufferOverflow; +volatile bool dscClassicKeypadInterface::commandReady; +volatile bool dscClassicKeypadInterface::moduleDataDetected; +volatile bool dscClassicKeypadInterface::alarmKeyDetected; +volatile bool dscClassicKeypadInterface::alarmKeyResponsePending; +volatile byte dscClassicKeypadInterface::clockCycleCount; +volatile byte dscClassicKeypadInterface::clockCycleTotal; +volatile byte dscClassicKeypadInterface::panelCommand[dscReadSize]; +volatile byte dscClassicKeypadInterface::isrPanelBitTotal; +volatile byte dscClassicKeypadInterface::isrPanelBitCount; +volatile byte dscClassicKeypadInterface::panelCommandByteCount; +volatile byte dscClassicKeypadInterface::isrModuleData[dscReadSize]; +volatile byte dscClassicKeypadInterface::isrModuleBitTotal; +volatile byte dscClassicKeypadInterface::isrModuleBitCount; +volatile byte dscClassicKeypadInterface::isrModuleByteCount; +volatile byte dscClassicKeypadInterface::panelCommandByteTotal; +volatile byte dscClassicKeypadInterface::moduleData[dscReadSize]; +volatile unsigned long dscClassicKeypadInterface::intervalStart; +volatile unsigned long dscClassicKeypadInterface::beepInterval; +volatile unsigned long dscClassicKeypadInterface::repeatInterval; +volatile unsigned long dscClassicKeypadInterface::keyInterval; +volatile unsigned long dscClassicKeypadInterface::alarmKeyTime; +volatile unsigned long dscClassicKeypadInterface::alarmKeyInterval; + +#if defined(__AVR__) +ISR(TIMER1_OVF_vect) { + dscClassicKeypadInterface::dscClockInterrupt(); +} +#endif // __AVR__ + // DSC PowerSeries #else diff --git a/src/dscKeypad.cpp b/src/dscKeypad.cpp index 2f4d7f1..e2ec6a9 100644 --- a/src/dscKeypad.cpp +++ b/src/dscKeypad.cpp @@ -18,6 +18,7 @@ */ #include "dscKeypad.h" +#if defined dscKeypad_h #if defined(ESP32) @@ -565,3 +566,5 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { TCNT1 = clockInterval; #endif } + +#endif // dscKeypad_h From 84667b0656e0d322f42877542f5eb4fd7da1342d Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sun, 14 Feb 2021 10:57:31 -0600 Subject: [PATCH 15/49] Support switching armed modes while armed for HomeBridge #126 --- README.md | 11 +++- .../Homebridge-MQTT/Homebridge-MQTT.ino | 50 ++++++++++++++++++- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 50 ++++++++++++++++++- .../Homebridge-MQTT/Homebridge-MQTT.ino | 50 ++++++++++++++++++- src/dscKeybus.h | 4 +- src/dscKeybusProcessData.cpp | 49 ++++++++++++++---- 6 files changed, 194 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 7f20d13..0c3053d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. +This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. @@ -363,7 +363,12 @@ Panel options affecting this interface, configured by `*8 + installer code` - se * PCB layouts are available in [`extras/PCB Layouts`](https://github.com/taligentx/dscKeybusInterface/tree/master/extras/PCB%20Layouts) - thanks to [sjlouw](https://github.com/sj-louw) for contributing these designs! -* Support for other platforms depends on adjusting the code to use their platform-specific timers. In addition to hardware interrupts to capture the DSC clock, this library uses platform-specific timer interrupts to capture the DSC data line in a non-blocking way 250μs after the clock changes (without using `delayMicroseconds()`). This is necessary because the clock and data are asynchronous - I've observed keypad data delayed up to 160μs after the clock falls. +* Support for other platforms depends on adjusting the code to use their platform-specific timers. In addition to hardware pin-change interrupts to capture the DSC clock, this library uses platform-specific timer interrupts to capture the DSC data line in a non-blocking way 250μs after the clock changes (without using `delayMicroseconds()`). This is necessary because the clock and data are asynchronous - I've observed keypad data delayed up to 160μs after the clock falls. + +* Resource utilization: + * Arduino: 1 hardware interrupt digital pin, 2 digital pins (+1 for Classic series), Timer1 interrupt + * esp8266: 3 digital pins (+1 for Classic series), timer1 interrupt + * esp32/esp32-s2: 3 digital pins (+1 for Classic series), timer0 interrupt ## Troubleshooting If you are running into issues: @@ -373,6 +378,8 @@ If you are running into issues: * If keys are not displayed in the output, verify the transistor pinout, base resistor, and wiring connections. 3. Run the `Status` example sketch and view the serial output to verify that the interface displays events from the security system correctly as partitions are armed, zones opened, etc. +For general discussions, feature requests, or how-to issues, you can [post in Discussions](https://github.com/taligentx/dscKeybusInterface/discussions), or [post an Issue](https://github.com/taligentx/dscKeybusInterface/issues) if it looks like an issue with the library code itself. + ## References [AVR Freaks - DSC Keybus Protocol](https://www.avrfreaks.net/forum/dsc-keybus-protocol): An excellent discussion on how data is sent on the Keybus. diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index e339009..e974ff6 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.4 (Arduino with Ethernet) + * Homebridge-MQTT 1.5 (Arduino with Ethernet) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -146,6 +146,7 @@ * Closed: "0" * * Release notes: + * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration * 1.2 - Resolved handling HomeKit target states @@ -454,7 +455,52 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { payloadIndex = 1; } - // Resets the HomeKit target state if attempting to change the armed mode while armed or not ready + // Sets night arm (no entry delay) while armed + if (payload[payloadIndex] == 'N' && dsc.armed[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "N", 0); + exitState = 'N'; + return; + } + + // Disables night arm while armed stay + if (payload[payloadIndex] == 'S' && dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Disables night arm while armed away + if (payload[payloadIndex] == 'A' && dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Changes from arm away to arm stay after the exit delay + if (payload[payloadIndex] == 'S' && dsc.armedAway[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("s"); + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Changes from arm stay to arm away after the exit delay + if (payload[payloadIndex] == 'A' && dsc.armedStay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("w"); + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Resets the HomeKit target state if attempting to change the armed mode while not ready if (payload[payloadIndex] != 'D' && !dsc.ready[partition]) { dsc.armedChanged[partition] = true; dsc.statusChanged = true; diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index 3291719..8f58429 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.4 (esp32) + * Homebridge-MQTT 1.5 (esp32) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration * 1.0 - Initial release @@ -451,7 +452,52 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { payloadIndex = 1; } - // Resets the HomeKit target state if attempting to change the armed mode while armed or not ready + // Sets night arm (no entry delay) while armed + if (payload[payloadIndex] == 'N' && dsc.armed[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "N", 0); + exitState = 'N'; + return; + } + + // Disables night arm while armed stay + if (payload[payloadIndex] == 'S' && dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Disables night arm while armed away + if (payload[payloadIndex] == 'A' && dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Changes from arm away to arm stay after the exit delay + if (payload[payloadIndex] == 'S' && dsc.armedAway[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("s"); + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Changes from arm stay to arm away after the exit delay + if (payload[payloadIndex] == 'A' && dsc.armedStay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("w"); + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Resets the HomeKit target state if attempting to change the armed mode while not ready if (payload[payloadIndex] != 'D' && !dsc.ready[partition]) { dsc.armedChanged[partition] = true; dsc.statusChanged = true; diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index 2bf45c3..5213a7f 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.4 (esp8266) + * Homebridge-MQTT 1.5 (esp8266) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors @@ -457,7 +458,52 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { payloadIndex = 1; } - // Resets the HomeKit target state if attempting to change the armed mode while armed or not ready + // Sets night arm (no entry delay) while armed + if (payload[payloadIndex] == 'N' && dsc.armed[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "N", 0); + exitState = 'N'; + return; + } + + // Disables night arm while armed stay + if (payload[payloadIndex] == 'S' && dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Disables night arm while armed away + if (payload[payloadIndex] == 'A' && dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Changes from arm away to arm stay after the exit delay + if (payload[payloadIndex] == 'S' && dsc.armedAway[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("s"); + publishState(mqttPartitionTopic, partition, "S", 0); + exitState = 'S'; + return; + } + + // Changes from arm stay to arm away after the exit delay + if (payload[payloadIndex] == 'A' && dsc.armedStay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("w"); + publishState(mqttPartitionTopic, partition, "A", 0); + exitState = 'A'; + return; + } + + // Resets the HomeKit target state if attempting to change the armed mode while not ready if (payload[payloadIndex] != 'D' && !dsc.ready[partition]) { dsc.armedChanged[partition] = true; dsc.statusChanged = true; diff --git a/src/dscKeybus.h b/src/dscKeybus.h index 868da31..e90c67a 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -158,6 +158,7 @@ class dscKeybusInterface { void processPanelStatus(); void processPanelStatus0(byte partition, byte panelByte); + void processPanelStatus1(byte partition, byte panelByte); void processPanelStatus2(byte partition, byte panelByte); void processPanelStatus4(byte partition, byte panelByte); void processPanelStatus5(byte partition, byte panelByte); @@ -179,6 +180,7 @@ class dscKeybusInterface { void processAlarmStatus(byte partitionIndex, bool status); void processExitDelayStatus(byte partitionIndex, bool status); void processEntryDelayStatus(byte partitionIndex, bool status); + void processNoEntryDelayStatus(byte partitionIndex, bool status); void processZoneStatus(byte zonesByte, byte panelByte); void processTime(byte panelByte); void processAlarmZones(byte panelByte, byte startByte, byte zoneCountOffset, byte writeValue); @@ -313,7 +315,7 @@ class dscKeybusInterface { bool previousReady[dscPartitions]; bool previousExitDelay[dscPartitions], previousEntryDelay[dscPartitions]; byte previousExitState[dscPartitions]; - bool previousArmed[dscPartitions], previousArmedStay[dscPartitions]; + bool previousArmed[dscPartitions], previousArmedStay[dscPartitions], previousNoEntryDelay[dscPartitions]; bool previousAlarm[dscPartitions]; bool previousFire[dscPartitions]; byte previousOpenZones[dscZones], previousAlarmZones[dscZones]; diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index 6e506ad..80f12fc 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -306,19 +306,18 @@ void dscKeybusInterface::processPanelStatus() { // Partition armed with no entry delay case 0x06: case 0x16: { - noEntryDelay[partitionIndex] = true; + armed[partitionIndex] = true; // Sets an armed mode if not already set, used if interface is initialized while the panel is armed - if (!armedStay[partitionIndex] && !armedAway[partitionIndex]) armedStay[partitionIndex] = true; - - armed[partitionIndex] = true; - if (armed[partitionIndex] != previousArmed[partitionIndex]) { - previousArmed[partitionIndex] = armed[partitionIndex]; - previousArmedStay[partitionIndex] = armedStay[partitionIndex]; - armedChanged[partitionIndex] = true; - if (!pauseStatus) statusChanged = true; + if (!armedStay[partitionIndex] && !armedAway[partitionIndex]) { + if (bitRead(lights[partitionIndex],3)) { + armedStay[partitionIndex] = true; + previousArmedStay[partitionIndex] = armedStay[partitionIndex]; + } + else armedAway[partitionIndex] = true; } + processNoEntryDelayStatus(partitionIndex, true); processReadyStatus(partitionIndex, false); break; } @@ -359,6 +358,7 @@ void dscKeybusInterface::processPanelStatus() { // Enter access code case 0x9F: { if (writeArm[partitionIndex]) { // Ensures access codes are only sent when an arm command is sent through this interface + writeArm[partitionIndex] = false; accessCodePrompt = true; if (!pauseStatus) statusChanged = true; } @@ -513,6 +513,7 @@ void dscKeybusInterface::processPanel_0xA5() { byte partition = panelData[3] >> 6; switch (panelData[5] & 0x03) { case 0x00: processPanelStatus0(partition, 6); break; + case 0x01: processPanelStatus1(partition, 6); break; case 0x02: processPanelStatus2(partition, 6); break; } } @@ -597,6 +598,7 @@ void dscKeybusInterface::processPanel_0xEB() { switch (panelData[7] & 0x07) { case 0x00: processPanelStatus0(partition, 8); break; + case 0x01: processPanelStatus1(partition, 8); break; case 0x02: processPanelStatus2(partition, 8); break; case 0x04: processPanelStatus4(partition, 8); break; case 0x05: processPanelStatus5(partition, 8); break; @@ -735,6 +737,21 @@ void dscKeybusInterface::processPanelStatus0(byte partition, byte panelByte) { } +void dscKeybusInterface::processPanelStatus1(byte partition, byte panelByte) { + if (partition == 0 || partition > dscPartitions) return; + byte partitionIndex = partition - 1; + + switch (panelData[panelByte]) { + + // Armed with no entry delay + case 0xD2: { + processNoEntryDelayStatus(partitionIndex, false); + return; + } + } +} + + void dscKeybusInterface::processPanelStatus2(byte partition, byte panelByte) { if (partition == 0 || partition > dscPartitions) return; byte partitionIndex = partition - 1; @@ -764,7 +781,7 @@ void dscKeybusInterface::processPanelStatus2(byte partition, byte panelByte) { return; } - if (panelData[panelByte] == 0xA5) { + if (panelData[0] == 0xA5) { switch (panelData[panelByte]) { // Activate stay/away zones @@ -779,7 +796,7 @@ void dscKeybusInterface::processPanelStatus2(byte partition, byte panelByte) { // Armed with no entry delay case 0x9C: { - noEntryDelay[partitionIndex] = true; + processNoEntryDelayStatus(partitionIndex, true); processReadyStatus(partitionIndex, false); return; } @@ -886,6 +903,16 @@ void dscKeybusInterface::processEntryDelayStatus(byte partitionIndex, bool statu } +void dscKeybusInterface::processNoEntryDelayStatus(byte partitionIndex, bool status) { + noEntryDelay[partitionIndex] = status; + if (noEntryDelay[partitionIndex] != previousNoEntryDelay[partitionIndex]) { + previousNoEntryDelay[partitionIndex] = noEntryDelay[partitionIndex]; + armedChanged[partitionIndex] = true; + if (!pauseStatus) statusChanged = true; + } +} + + void dscKeybusInterface::processZoneStatus(byte zonesByte, byte panelByte) { openZones[zonesByte] = panelData[panelByte]; byte zonesChanged = openZones[zonesByte] ^ previousOpenZones[zonesByte]; From ad5789692141ff9a956529b9589b53ed0cae6210 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Sun, 14 Feb 2021 13:11:02 -0600 Subject: [PATCH 16/49] Update decoding for no entry delay to differentiate away and stay arm --- README.md | 1 + examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 4 ++-- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 1 + examples/esp32/Pushbullet/Pushbullet.ino | 4 ++-- examples/esp32/Telegram/Telegram.ino | 4 ++-- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 4 ++-- examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 4 ++-- examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino | 4 ++-- examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 4 ++-- examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 1 + examples/esp8266/Pushbullet/Pushbullet.ino | 4 ++-- examples/esp8266/Telegram/Telegram.ino | 4 ++-- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 4 ++-- examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino | 4 ++-- examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino | 4 ++-- src/dscKeybusPrintData.cpp | 4 ++-- src/dscKeybusProcessData.cpp | 2 +- 17 files changed, 30 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0c3053d..7c343cc 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: DSC Classic series panel support: PC1500, PC1550 - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. - New: esp32-s2 microcontroller support + - Updated: `Homebridge-MQTT` support switching armed modes while armed * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index e2e5966..643e14b 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -535,7 +535,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x03: mqtt.publish(publishTopic, "Zones open", true); break; case 0x04: mqtt.publish(publishTopic, "Armed: Stay", true); break; case 0x05: mqtt.publish(publishTopic, "Armed: Away", true); break; - case 0x06: mqtt.publish(publishTopic, "Armed: No entry delay", true); break; + case 0x06: mqtt.publish(publishTopic, "Armed: Stay with no entry delay", true); break; case 0x07: mqtt.publish(publishTopic, "Failed to arm", true); break; case 0x08: mqtt.publish(publishTopic, "Exit delay in progress", true); break; case 0x09: mqtt.publish(publishTopic, "Arming with no entry delay", true); break; @@ -548,7 +548,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x12: mqtt.publish(publishTopic, "Battery check in progress"); break; case 0x14: mqtt.publish(publishTopic, "Auto-arm in progress", true); break; case 0x15: mqtt.publish(publishTopic, "Arming with bypassed zones", true); break; - case 0x16: mqtt.publish(publishTopic, "Armed: No entry delay", true); break; + case 0x16: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; case 0x17: mqtt.publish(publishTopic, "Power saving: Keypad blanked", true); break; case 0x19: mqtt.publish(publishTopic, "Disarmed: Alarm memory"); break; case 0x22: mqtt.publish(publishTopic, "Disarmed: Recent closing", true); break; diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index 1a89965..f0ea568 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -475,6 +475,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x03: mqtt.publish(publishTopic, "Zones open", true); break; case 0x04: mqtt.publish(publishTopic, "Armed stay", true); break; case 0x05: mqtt.publish(publishTopic, "Armed away", true); break; + case 0x06: mqtt.publish(publishTopic, "No entry delay", true); break; case 0x07: mqtt.publish(publishTopic, "Failed to arm", true); break; case 0x08: mqtt.publish(publishTopic, "Exit delay", true); break; case 0x09: mqtt.publish(publishTopic, "No entry delay", true); break; diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index be62ec5..cd0eeec 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -181,9 +181,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 496920c..92badef 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -200,9 +200,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 4bc053c..7907fd3 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -139,9 +139,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 4568958..e80d9c6 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -482,7 +482,7 @@ void setStatus(byte partition, bool forceUpdate) { case 0x05: lcd.print(0, 0, "Armed: "); lcd.print(0, 1, "Away "); if (pausedZones) resetZones(); break; - case 0x06: lcd.print(0, 0, "Armed: "); + case 0x06: lcd.print(0, 0, "Armed: Stay "); lcd.print(0, 1, "No entry delay "); if (pausedZones) resetZones(); break; case 0x07: lcd.print(0, 0, "Failed "); @@ -510,7 +510,7 @@ void setStatus(byte partition, bool forceUpdate) { lcd.print(0, 1, "in progress "); break; case 0x15: lcd.print(0, 0, "Arming with "); lcd.print(0, 1, "bypass zones "); break; - case 0x16: lcd.print(0, 0, "Armed: "); + case 0x16: lcd.print(0, 0, "Armed: Away "); lcd.print(0, 1, "No entry delay "); if (pausedZones) resetZones(); break; case 0x17: lcd.print(0, 0, "Power saving "); diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 0aefc40..551bd87 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -324,7 +324,7 @@ void setStatus(byte partition) { case 0x05: root["lcd_upper"] = "Armed: "; root["lcd_lower"] = "Away "; if (pausedZones) resetZones(); break; - case 0x06: root["lcd_upper"] = "Armed: "; + case 0x06: root["lcd_upper"] = "Armed: Stay "; root["lcd_lower"] = "No entry delay "; if (pausedZones) resetZones(); break; case 0x07: root["lcd_upper"] = "Failed "; @@ -352,7 +352,7 @@ void setStatus(byte partition) { root["lcd_lower"] = "in progress "; break; case 0x15: root["lcd_upper"] = "Arming with "; root["lcd_lower"] = "bypass zones "; break; - case 0x16: root["lcd_upper"] = "Armed: "; + case 0x16: root["lcd_upper"] = "Armed: Away "; root["lcd_lower"] = "No entry delay "; if (pausedZones) resetZones(); break; case 0x17: root["lcd_upper"] = "Power saving "; diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index be53533..7e5ae6f 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -542,7 +542,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x03: mqtt.publish(publishTopic, "Zones open", true); break; case 0x04: mqtt.publish(publishTopic, "Armed: Stay", true); break; case 0x05: mqtt.publish(publishTopic, "Armed: Away", true); break; - case 0x06: mqtt.publish(publishTopic, "Armed: No entry delay", true); break; + case 0x06: mqtt.publish(publishTopic, "Armed: Stay with no entry delay", true); break; case 0x07: mqtt.publish(publishTopic, "Failed to arm", true); break; case 0x08: mqtt.publish(publishTopic, "Exit delay in progress", true); break; case 0x09: mqtt.publish(publishTopic, "Arming with no entry delay", true); break; @@ -555,7 +555,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x12: mqtt.publish(publishTopic, "Battery check in progress"); break; case 0x14: mqtt.publish(publishTopic, "Auto-arm in progress", true); break; case 0x15: mqtt.publish(publishTopic, "Arming with bypassed zones", true); break; - case 0x16: mqtt.publish(publishTopic, "Armed: No entry delay", true); break; + case 0x06: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; case 0x17: mqtt.publish(publishTopic, "Power saving: Keypad blanked", true); break; case 0x19: mqtt.publish(publishTopic, "Disarmed: Alarm memory"); break; case 0x22: mqtt.publish(publishTopic, "Disarmed: Recent closing", true); break; diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index 686450f..8c57123 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -476,6 +476,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x03: mqtt.publish(publishTopic, "Zones open", true); break; case 0x04: mqtt.publish(publishTopic, "Armed stay", true); break; case 0x05: mqtt.publish(publishTopic, "Armed away", true); break; + case 0x06: mqtt.publish(publishTopic, "No entry delay", true); break; case 0x07: mqtt.publish(publishTopic, "Failed to arm", true); break; case 0x08: mqtt.publish(publishTopic, "Exit delay", true); break; case 0x09: mqtt.publish(publishTopic, "No entry delay", true); break; diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 7f9a4fe..57df83b 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -187,9 +187,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index ec7a8c1..ebcac0b 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -202,9 +202,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index 88b94d3..59c5094 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -145,9 +145,9 @@ void loop() { if (dsc.armed[partition]) { char messageContent[25]; - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); appendPartition(partition, messageContent); // Appends the message with the partition number diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 7612118..0f339de 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -484,7 +484,7 @@ void setStatus(byte partition, bool forceUpdate) { case 0x05: lcd.print(0, 0, "Armed: "); lcd.print(0, 1, "Away "); if (pausedZones) resetZones(); break; - case 0x06: lcd.print(0, 0, "Armed: "); + case 0x06: lcd.print(0, 0, "Armed: Stay "); lcd.print(0, 1, "No entry delay "); if (pausedZones) resetZones(); break; case 0x07: lcd.print(0, 0, "Failed "); @@ -512,7 +512,7 @@ void setStatus(byte partition, bool forceUpdate) { lcd.print(0, 1, "in progress "); break; case 0x15: lcd.print(0, 0, "Arming with "); lcd.print(0, 1, "bypass zones "); break; - case 0x16: lcd.print(0, 0, "Armed: "); + case 0x16: lcd.print(0, 0, "Armed: Away "); lcd.print(0, 1, "No entry delay "); if (pausedZones) resetZones(); break; case 0x17: lcd.print(0, 0, "Power saving "); diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 26071f5..87274a7 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -333,7 +333,7 @@ void setStatus(byte partition) { case 0x05: root["lcd_upper"] = "Armed: "; root["lcd_lower"] = "Away "; if (pausedZones) resetZones(); break; - case 0x06: root["lcd_upper"] = "Armed: "; + case 0x06: root["lcd_upper"] = "Armed: Stay "; root["lcd_lower"] = "No entry delay "; if (pausedZones) resetZones(); break; case 0x07: root["lcd_upper"] = "Failed "; @@ -361,7 +361,7 @@ void setStatus(byte partition) { root["lcd_lower"] = "in progress "; break; case 0x15: root["lcd_upper"] = "Arming with "; root["lcd_lower"] = "bypass zones "; break; - case 0x16: root["lcd_upper"] = "Armed: "; + case 0x16: root["lcd_upper"] = "Armed: Away "; root["lcd_lower"] = "No entry delay "; if (pausedZones) resetZones(); break; case 0x17: root["lcd_upper"] = "Power saving "; diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index b5717c4..d23d991 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -223,7 +223,7 @@ void dscKeybusInterface::printPanelMessages(byte panelByte) { case 0x03: stream->print(F("Zones open")); break; case 0x04: stream->print(F("Armed: Stay")); break; case 0x05: stream->print(F("Armed: Away")); break; - case 0x06: stream->print(F("Armed: No entry delay")); break; + case 0x06: stream->print(F("Armed: Stay with no entry delay")); break; case 0x07: stream->print(F("Failed to arm")); break; case 0x08: stream->print(F("Exit delay in progress")); break; case 0x09: stream->print(F("Arming: No entry delay")); break; @@ -236,7 +236,7 @@ void dscKeybusInterface::printPanelMessages(byte panelByte) { case 0x12: stream->print(F("Battery check in progress")); break; case 0x14: stream->print(F("Auto-arm in progress")); break; case 0x15: stream->print(F("Arming with bypassed zones")); break; - case 0x16: stream->print(F("Armed: No entry delay")); break; + case 0x16: stream->print(F("Armed: Away with no entry delay")); break; case 0x19: stream->print(F("Disarmed: Alarm memory")); break; case 0x22: stream->print(F("Disarmed: Recent closing")); break; case 0x2F: stream->print(F("Keypad LCD test")); break; diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index 80f12fc..970ea46 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -310,7 +310,7 @@ void dscKeybusInterface::processPanelStatus() { // Sets an armed mode if not already set, used if interface is initialized while the panel is armed if (!armedStay[partitionIndex] && !armedAway[partitionIndex]) { - if (bitRead(lights[partitionIndex],3)) { + if (panelData[messageByte] == 0x06) { armedStay[partitionIndex] = true; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; } From 00666d2c2187d81ccccf9196a0013281649104d3 Mon Sep 17 00:00:00 2001 From: kricon Date: Mon, 15 Feb 2021 02:27:55 +0100 Subject: [PATCH 17/49] TinyGSM-SMS Arduino example, KeypadInterface send LCD Messages --- .../KeypadInterface/KeypadInterface.ino | 62 ++-- examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino | 309 ++++++++++++++++++ .../esp32/KeypadInterface/KeypadInterface.ino | 62 ++-- .../KeypadInterface/KeypadInterface.ino | 62 ++-- 4 files changed, 414 insertions(+), 81 deletions(-) create mode 100644 examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 44ce431..3f4e961 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.2 (Arduino) + * DSC Keypad Interface 1.3a (Arduino) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -17,12 +17,18 @@ * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * - Set LCD keypad messages (on cmd 0x05/byte3) with entering HEX input into serial console: + * According to printPanelMessages in dscKeybusPrintData.cpp, through it doesn't seem to fully match + * Change Function keys 1-5 with entering: 0x70 - 0x74 + * Change LCD keypad time by entering: 0x2A (slight delay before LCD will show to input time data) + * Change LCD Brightness/contrast/buzzer level by entering: 0x29 and scrolling to desired setting then pressing (*) * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -114,33 +120,35 @@ void loop() { */ if (inputReceived) { inputReceived = false; - - for (byte i = 0; i < strlen(input); i++) { - switch (input[i]) { - case 'r': case 'R': dsc.lightReady = setLight(); break; - case 'a': case 'A': dsc.lightArmed = setLight(); break; - case 'm': case 'M': dsc.lightMemory = setLight(); break; - case 'y': case 'Y': dsc.lightBypass = setLight(); break; - case 't': case 'T': dsc.lightTrouble = setLight(); break; - case 'p': case 'P': dsc.lightProgram = setLight(); break; - case 'f': case 'F': dsc.lightFire = setLight(); break; - case 'l': case 'L': dsc.lightBacklight = setLight(); break; - case '1': dsc.lightZone1 = setLight(); break; - case '2': dsc.lightZone2 = setLight(); break; - case '3': dsc.lightZone3 = setLight(); break; - case '4': dsc.lightZone4 = setLight(); break; - case '5': dsc.lightZone5 = setLight(); break; - case '6': dsc.lightZone6 = setLight(); break; - case '7': dsc.lightZone7 = setLight(); break; - case '8': dsc.lightZone8 = setLight(); break; - case 'b': case 'B': sendBeeps(i); i += beepLength; break; - case 'n': case 'N': sendTone(i); i+= toneLength; break; - case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; - case '-': lightOff = true; break; - case '!': lightBlink = true; break; - default: break; + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support + else { //remove or comment this line for classic series support + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } - } + } //remove or comment this line for classic series support } dsc.loop(); diff --git a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino new file mode 100644 index 0000000..fd9d036 --- /dev/null +++ b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino @@ -0,0 +1,309 @@ +/* + * TinyGSM SMS Notification 0.1 (Arduino) + * + * Processes the security system status and demonstrates how to send an SMS text message when the status has + * changed. This example sends SMS text messages via a TinyGSM-compatible SIM800L module which is connected + * onto Arduino board. Make sure that Micro-SIM card have PIN Code turned off (disable SIM Lock from phone). + * + * Usage: + * 1. Install the TinyGSM library, available in the Arduino IDE Library Manager and the Platform.io Library + * Registry: https://github.com/vshymanskyy/TinyGSM + * 2. Set the destination phone numbers in the sketch settings. + * + * Release notes: + * 0.1 - Just converted from ESP32 to Arduino. NOT TESTED - waiting to receive the SIM800L module to test with + * + * Wiring: + * DSC Aux(+) --- Arduino Vin pin + * + * DSC Aux(-) --- Arduino Ground + * + * +--- dscClockPin (Arduino Uno: 2,3) + * DSC Yellow --- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin (Arduino Uno: 2-12) + * DSC Green ---- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin (Arduino Uno: 2-12) + * +-- 15k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * + * Connecting SIM800L module with Arduino Uno: + * SIM800 RX -+-- 5.6k ohm resistor -- SIM800RxPin (Arduino Uno: 2-12) + * | + * | + * +-- 10k ohm resistor -- Arduino Uno Ground + * + * SIM800 TX ---- SIM800TxPin (Arduino Uno: 2-12) + * + * SIM800 GND --- Arduino Uno Ground + * + * SIM800 Vcc --- (+) 3.7 - 4.4V power supply which can supply burst current of 2A + * (you can use LM2596 Buck converter set to output 4.2V and connected to Bell+ and Aux-) + * + * Virtual keypad (optional): + * DSC Green ---- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12) + * Ground --- NPN emitter --/ + * + * + * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should + * be suitable, for example: + * -- 2N3904 + * -- BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * Based on TinyGSM-SMS example for ESP32 by jvitkauskas: https://github.com/jvitkauskas + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +// Configures GSM modem model. Must be done before including TinyGsmClient library. +#define TINY_GSM_MODEM_SIM800 +#include +#include +#include + +// Settings +const char* sendToPhoneNumbers[] = { + "+1234567890", + "+2345678901" +}; + +#define phone_number_count (sizeof (sendToPhoneNumbers) / sizeof (const char *)) + +// Configures the GSM modem interface with the specified pins (eg. TTGO T-Call 1.3 v20190601). +#define MODEM_RST 5 +#define MODEM_PWRKEY 4 +#define MODEM_POWER_ON 23 +#define MODEM_TX 27 +#define MODEM_RX 26 + +// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the +// virtual keypad. +#define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 +#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 +#define dscReadPin 5 // Arduino Uno: 2-12 +#define dscWritePin 6 // Arduino Uno: 2-12 +#define SIM800RxPin 9 // Arduino Uno: 2-12 +#define SIM800TxPin 10 // Arduino Uno: 2-12 + +// Settings +bool notifyOnPowerTroubles = true; +bool notifyOnDisArming = true; +bool notifyOnTrouble = true; + +// Initialize components +SoftwareSerial serialSIM800(SIM800TxPin,SIM800RxPin); +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +TinyGsm modem(serialSIM800); + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + pinMode(MODEM_PWRKEY, OUTPUT); + pinMode(MODEM_POWER_ON, OUTPUT); + + // Turn on the Modem power first + digitalWrite(MODEM_POWER_ON, HIGH); + + // Pull down PWRKEY for more than 1 second according to manual requirements + digitalWrite(MODEM_PWRKEY, HIGH); + delay(100); + digitalWrite(MODEM_PWRKEY, LOW); + delay(1000); + digitalWrite(MODEM_PWRKEY, HIGH); + + serialSIM800.begin(115200); + + while (!modem.isNetworkConnected()) { + Serial.print(F("GSM...")); + while (!modem.restart()) { + Serial.print("."); + } + Serial.println(); + + Serial.print(F("Waiting for network...")); + if (modem.waitForNetwork(600000L) && modem.isNetworkConnected()) { + Serial.println(F("connected.")); + } + else { + Serial.println(F("connection error.")); + } + } + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + modem.maintain(); + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[34] = "Disarmed after alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Publishes armed/disarmed status + if (dsc.armedChanged[partition]) { + if (!notifyOnDisArming) continue; + if (dsc.armed[partition]) { + // Night armed away + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed away - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed away + else if (dsc.armedAway[partition]) { + char messageContent[25] = "Armed away: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Night armed stay + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed stay - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed stay + else if (dsc.armedStay[partition]) { + char messageContent[25] = "Armed stay: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Disarmed + else { + char messageContent[23] = "Disarmed: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + // Checks trouble status + if (dsc.troubleChanged) { + if (!notifyOnTrouble) return; + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + + // Checks for AC power status + if (dsc.powerChanged) { + if (!notifyOnPowerTroubles) return; + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } +} + +bool sendMessage(const char* messageContent) { + bool result = true; + + for (int i = 0; i < phone_number_count; i++) { + result &= modem.sendSMS(sendToPhoneNumbers[i], messageContent); + } + + return result; +} + +void appendPartition(byte sourceNumber, char* pushMessage) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(pushMessage, partitionNumber); +} diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index ad10a88..d21449d 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.2 (esp32) + * DSC Keypad Interface 1.3a (esp32) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -17,12 +17,18 @@ * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * - Set LCD keypad messages (on cmd 0x05/byte3) with entering HEX input into serial console: + * According to printPanelMessages in dscKeybusPrintData.cpp, through it doesn't seem to fully match + * Change Function keys 1-5 with entering: 0x70 - 0x74 + * Change LCD keypad time by entering: 0x2A (slight delay before LCD will show to input time data) + * Change LCD Brightness/contrast/buzzer level by entering: 0x29 and scrolling to desired setting then pressing (*) * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -114,33 +120,35 @@ void loop() { */ if (inputReceived) { inputReceived = false; - - for (byte i = 0; i < strlen(input); i++) { - switch (input[i]) { - case 'r': case 'R': dsc.lightReady = setLight(); break; - case 'a': case 'A': dsc.lightArmed = setLight(); break; - case 'm': case 'M': dsc.lightMemory = setLight(); break; - case 'y': case 'Y': dsc.lightBypass = setLight(); break; - case 't': case 'T': dsc.lightTrouble = setLight(); break; - case 'p': case 'P': dsc.lightProgram = setLight(); break; - case 'f': case 'F': dsc.lightFire = setLight(); break; - case 'l': case 'L': dsc.lightBacklight = setLight(); break; - case '1': dsc.lightZone1 = setLight(); break; - case '2': dsc.lightZone2 = setLight(); break; - case '3': dsc.lightZone3 = setLight(); break; - case '4': dsc.lightZone4 = setLight(); break; - case '5': dsc.lightZone5 = setLight(); break; - case '6': dsc.lightZone6 = setLight(); break; - case '7': dsc.lightZone7 = setLight(); break; - case '8': dsc.lightZone8 = setLight(); break; - case 'b': case 'B': sendBeeps(i); i += beepLength; break; - case 'n': case 'N': sendTone(i); i+= toneLength; break; - case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; - case '-': lightOff = true; break; - case '!': lightBlink = true; break; - default: break; + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support + else { //remove or comment this line for classic series support + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } - } + } //remove or comment this line for classic series support } dsc.loop(); diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 2cbd336..bc42788 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.2 (esp8266) + * DSC Keypad Interface 1.3a (esp8266) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -17,12 +17,18 @@ * 2 beeps, no constant tone, 4 second interval: dsc.tone(2, false, 4) * 3 beeps, constant tone, 2 second interval: dsc.tone(3, true, 2) * Disable the tone: dsc.tone() or dsc.tone(0, false, 0) + * - Set LCD keypad messages (on cmd 0x05/byte3) with entering HEX input into serial console: + * According to printPanelMessages in dscKeybusPrintData.cpp, through it doesn't seem to fully match + * Change Function keys 1-5 with entering: 0x70 - 0x74 + * Change LCD keypad time by entering: 0x2A (slight delay before LCD will show to input time data) + * Change LCD Brightness/contrast/buzzer level by entering: 0x29 and scrolling to desired setting then pressing (*) * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -114,33 +120,35 @@ void loop() { */ if (inputReceived) { inputReceived = false; - - for (byte i = 0; i < strlen(input); i++) { - switch (input[i]) { - case 'r': case 'R': dsc.lightReady = setLight(); break; - case 'a': case 'A': dsc.lightArmed = setLight(); break; - case 'm': case 'M': dsc.lightMemory = setLight(); break; - case 'y': case 'Y': dsc.lightBypass = setLight(); break; - case 't': case 'T': dsc.lightTrouble = setLight(); break; - case 'p': case 'P': dsc.lightProgram = setLight(); break; - case 'f': case 'F': dsc.lightFire = setLight(); break; - case 'l': case 'L': dsc.lightBacklight = setLight(); break; - case '1': dsc.lightZone1 = setLight(); break; - case '2': dsc.lightZone2 = setLight(); break; - case '3': dsc.lightZone3 = setLight(); break; - case '4': dsc.lightZone4 = setLight(); break; - case '5': dsc.lightZone5 = setLight(); break; - case '6': dsc.lightZone6 = setLight(); break; - case '7': dsc.lightZone7 = setLight(); break; - case '8': dsc.lightZone8 = setLight(); break; - case 'b': case 'B': sendBeeps(i); i += beepLength; break; - case 'n': case 'N': sendTone(i); i+= toneLength; break; - case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; - case '-': lightOff = true; break; - case '!': lightBlink = true; break; - default: break; + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support + else { //remove or comment this line for classic series support + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } } - } + } //remove or comment this line for classic series support } dsc.loop(); From 7e26f5e5594506bc42d546efad512cc588fc9d9c Mon Sep 17 00:00:00 2001 From: kricon Date: Mon, 15 Feb 2021 02:55:55 +0100 Subject: [PATCH 18/49] Changes to barely fit into Arduino Uno, redo 0xC3 cmd with updated info --- src/dscKeybusPrintData.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index d23d991..e3a25be 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -102,8 +102,8 @@ void dscKeybusInterface::printPanelMessage() { case 0x7F: printPanel_0x7F(); return; // Buzzer, partition 1 | Structure: complete | Content: complete case 0x82: printPanel_0x82(); return; // Buzzer, partition 2 | Structure: complete | Content: complete case 0x87: printPanel_0x87(); return; // PGM outputs | Structure: complete | Content: complete - case 0x8D: printPanel_0x8D(); return; // RF module programming - it sends data from panel to RF module after programming entry is done | Structure: *incomplete | Content: *incomplete - case 0x94: printPanel_0x94(); return; // Requesting and getting data from RF module to the panel | Structure: *incomplete | Content: *incomplete +// case 0x8D: printPanel_0x8D(); return; // RF module programming - it sends data from panel to RF module after programming entry is done | Structure: *incomplete | Content: *incomplete +// case 0x94: printPanel_0x94(); return; // Requesting and getting data from RF module to the panel | Structure: *incomplete | Content: *incomplete case 0x9E: printPanel_0x9E(); return; // DLS query | Structure: complete | Content: complete case 0xA5: printPanel_0xA5(); return; // Date, time, system status messages - partitions 1-2 | Structure: *incomplete | Content: *incomplete case 0xAA: printPanel_0xAA(); return; // Event buffer messages | Structure: complete | Content: *incomplete @@ -157,7 +157,7 @@ void dscKeybusInterface::printModuleMessage() { case 0x57: printModule_0x57(); return; // Wireless key query response | Structure: *incomplete | Content: *incomplete case 0x58: printModule_0x58(); return; // Module status query response | Structure: *incomplete | Content: *incomplete case 0x70: printModule_0x70(); return; // LCD keypad data entry | Structure: complete | Content: complete - case 0x94: printModule_0x94(); return; // Module programming response | Structure: *incomplete | Content: *incomplete +// case 0x94: printModule_0x94(); return; // Module programming response | Structure: *incomplete | Content: *incomplete case 0xD5: printModule_0xD5(); return; // Keypad zone query response | Structure: *incomplete | Content: *incomplete case 0x22: case 0x28: @@ -1919,10 +1919,11 @@ void dscKeybusInterface::printPanel_0x87() { * 10001101 0 00010001 00111001 00000000 00000111 11111111 11111111 11111111 11011011 [0x8D] Wls programming key response // Set RF jamming zone 07 in [804][93] subsection * Byte 0 1 2 3 4 5 6 7 8 9 */ +/* void dscKeybusInterface::printPanel_0x8D() { stream->print(F("Module programming entry")); } - +*/ /* * 0x94: Used to request programming data from modules @@ -1946,10 +1947,11 @@ void dscKeybusInterface::printPanel_0x8D() { * 10010100 0 00010001 00000000 00000000 10100101 00000000 00000000 00000000 01001100 11111100 [0x94] Unknown data * Byte 0 1 2 3 4 5 6 7 8 9 10 */ +/* void dscKeybusInterface::printPanel_0x94() { stream->print(F("Module programming request")); } - +*/ /* * 0x9E: DLS query @@ -2143,8 +2145,8 @@ void dscKeybusInterface::printPanel_0xBB() { * Content decoding: *incomplete * * Byte 2: bit 0-2 unknown - * Byte 2: bit 3 active when dialer attempt begin - * Byte 2: bit 4 dialer enabled (always true on old-gen?) + * Byte 2: bit 3 TLM available or communications disabled (no trouble) + * Byte 2: bit 4 TLM trouble or dialing attempt (with/without trouble) * Byte 2: bit 5 keypad lockout active * Byte 2: bit 6-7 unknown * Byte 3: Unknown, always observed as 11111111 @@ -2160,15 +2162,13 @@ void dscKeybusInterface::printPanel_0xBB() { void dscKeybusInterface::printPanel_0xC3() { if (panelData[3] == 0xFF) { - if (panelData[2] & 0x01 || panelData[2] & 0x02 || panelData[2] & 0x04 || panelData[2] & 0x40 || panelData[2] & 0x80) printUnknownData(); - else { - stream->print(F("Dialer: ")); - if (panelData[2] & 0x10) stream->print(F("enabled")); - else stream->print(F("disabled")); + + stream->print(F("TLM: ")); + if (panelData[2] & 0x10) stream->print(F("trouble/attempt")); + else stream->print(F("available/disabled")); if (panelData[2] & 0x08) stream->print(F(" | Dialer call attempt")); if (panelData[2] & 0x20) stream->print(F(" | Keypad lockout")); - } } else printUnknownData(); } @@ -3453,10 +3453,11 @@ void dscKeybusInterface::printModule_0x70() { * Structure decoding: *incomplete * Content decoding: *incomplete */ +/* void dscKeybusInterface::printModule_0x94() { stream->print(F("Module programming response")); } - +*/ /* * Module data during panel command: 0xD5 Keypad zone query From bfcee89a93f93052b6d7a57a752d4e217f78201d Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 21 May 2021 11:07:34 +0300 Subject: [PATCH 19/49] Update esp32 timer label --- src/dscClassic.cpp | 42 ++++++++++++++--------------- src/dscClassic.h | 4 +-- src/dscClassicKeypad.cpp | 24 ++++++++--------- src/dscClassicKeypad.h | 4 +-- src/dscKeybus.h | 4 +-- src/dscKeybusInterface.cpp | 54 +++++++++++++++++++------------------- src/dscKeypad.cpp | 24 ++++++++--------- src/dscKeypad.h | 4 +-- 8 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp index 27f0f3e..630a041 100644 --- a/src/dscClassic.cpp +++ b/src/dscClassic.cpp @@ -20,10 +20,10 @@ #include "dscClassic.h" #if defined(ESP32) -portMUX_TYPE dscClassicInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE dscClassicInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; #if ESP_IDF_VERSION_MAJOR < 4 -hw_timer_t * dscClassicInterface::timer0 = NULL; +hw_timer_t * dscClassicInterface::timer1 = NULL; #else // ESP-IDF 4+ esp_timer_handle_t timer1; @@ -70,14 +70,14 @@ void dscClassicInterface::begin(Stream &_stream) { timer1_attachInterrupt(dscDataInterrupt); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); - // esp32 timer0 calls dscDataInterrupt() from dscClockInterrupt() + // esp32 timer1 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timer0 = timerBegin(0, 80, true); - timerStop(timer0); - timerAttachInterrupt(timer0, &dscDataInterrupt, true); - timerAlarmWrite(timer0, 250, true); - timerAlarmEnable(timer0); + timer1 = timerBegin(1, 80, true); + timerStop(timer1); + timerAttachInterrupt(timer1, &dscDataInterrupt, true); + timerAlarmWrite(timer1, 250, true); + timerAlarmEnable(timer1); #else // IDF4+ esp_timer_create(&timer1Parameters, &timer1); #endif // ESP_IDF_VERSION_MAJOR @@ -99,11 +99,11 @@ void dscClassicInterface::stop() { timer1_disable(); timer1_detachInterrupt(); - // Disables esp32 timer0 + // Disables esp32 timer1 #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerAlarmDisable(timer0); - timerEnd(timer0); + timerAlarmDisable(timer1); + timerEnd(timer1); #else // ESP-IDF 4+ esp_timer_stop(timer1); #endif // ESP_IDF_VERSION_MAJOR @@ -138,7 +138,7 @@ bool dscClassicInterface::loop() { // Checks if Keybus data is detected and sets a status flag if data is not detected for 3s #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -147,7 +147,7 @@ bool dscClassicInterface::loop() { else keybusConnected = true; #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -178,7 +178,7 @@ bool dscClassicInterface::loop() { // Resets counters when the buffer is cleared #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -189,7 +189,7 @@ bool dscClassicInterface::loop() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -1041,11 +1041,11 @@ void IRAM_ATTR dscClassicInterface::dscClockInterrupt() { // esp32 timer1 calls dscDataInterrupt() in 250us #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStart(timer0); + timerStart(timer1); #else // IDF4+ esp_timer_start_periodic(timer1, 250); #endif - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #endif static unsigned long previousClockHighTime; @@ -1085,7 +1085,7 @@ void IRAM_ATTR dscClassicInterface::dscClockInterrupt() { } } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #endif } @@ -1098,11 +1098,11 @@ void ICACHE_RAM_ATTR dscClassicInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscClassicInterface::dscDataInterrupt() { #if ESP_IDF_VERSION_MAJOR < 4 - timerStop(timer0); + timerStop(timer1); #else // IDF 4+ esp_timer_stop(timer1); #endif - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #endif static bool skipData = false; @@ -1228,6 +1228,6 @@ void IRAM_ATTR dscClassicInterface::dscDataInterrupt() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #endif } diff --git a/src/dscClassic.h b/src/dscClassic.h index 20c4f1d..9a8025c 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -160,9 +160,9 @@ class dscClassicInterface { #if defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - static hw_timer_t * timer0; + static hw_timer_t * timer1; #endif - static portMUX_TYPE timer0Mux; + static portMUX_TYPE timer1Mux; #endif Stream* stream; diff --git a/src/dscClassicKeypad.cpp b/src/dscClassicKeypad.cpp index 4baa358..a35a159 100644 --- a/src/dscClassicKeypad.cpp +++ b/src/dscClassicKeypad.cpp @@ -20,10 +20,10 @@ #include "dscClassicKeypad.h" #if defined(ESP32) -portMUX_TYPE dscClassicKeypadInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE dscClassicKeypadInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; #if ESP_IDF_VERSION_MAJOR < 4 -hw_timer_t * dscClassicKeypadInterface::timer0 = NULL; +hw_timer_t * dscClassicKeypadInterface::timer1 = NULL; #else // ESP-IDF 4+ esp_timer_handle_t timer3; @@ -68,14 +68,14 @@ void dscClassicKeypadInterface::begin(Stream &_stream) { timer1_attachInterrupt(dscClockInterrupt); timer1_write(5000); - // esp32 timer0 calls dscClockInterrupt() + // esp32 timer1 calls dscClockInterrupt() #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timer0 = timerBegin(0, 80, true); - timerStop(timer0); - timerAttachInterrupt(timer0, &dscClockInterrupt, true); - timerAlarmWrite(timer0, 1000, true); - timerAlarmEnable(timer0); + timer1 = timerBegin(1, 80, true); + timerStop(timer1); + timerAttachInterrupt(timer1, &dscClockInterrupt, true); + timerAlarmWrite(timer1, 1000, true); + timerAlarmEnable(timer1); #else // IDF4+ esp_timer_create(&timer3Parameters, &timer3); #endif // ESP_IDF_VERSION_MAJOR @@ -138,7 +138,7 @@ bool dscClassicKeypadInterface::loop() { timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStart(timer0); + timerStart(timer1); #else // IDF4+ esp_timer_start_periodic(timer3, 1000); #endif // ESP_IDF_VERSION_MAJOR @@ -172,7 +172,7 @@ bool dscClassicKeypadInterface::loop() { // Resets counters when the buffer is cleared #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -183,7 +183,7 @@ bool dscClassicKeypadInterface::loop() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -398,7 +398,7 @@ void IRAM_ATTR dscClassicKeypadInterface::dscClockInterrupt() { timer1_disable(); #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStop(timer0); + timerStop(timer1); #else // IDF4+ esp_timer_stop(timer3); #endif // ESP_IDF_VERSION_MAJOR diff --git a/src/dscClassicKeypad.h b/src/dscClassicKeypad.h index 8fc24bc..640069f 100644 --- a/src/dscClassicKeypad.h +++ b/src/dscClassicKeypad.h @@ -83,9 +83,9 @@ class dscClassicKeypadInterface { #if defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - static hw_timer_t * timer0; + static hw_timer_t * timer1; #endif - static portMUX_TYPE timer0Mux; + static portMUX_TYPE timer1Mux; #endif static int clockInterval; diff --git a/src/dscKeybus.h b/src/dscKeybus.h index e90c67a..bdebcee 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -296,9 +296,9 @@ class dscKeybusInterface { #if defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - static hw_timer_t * timer0; + static hw_timer_t * timer1; #endif - static portMUX_TYPE timer0Mux; + static portMUX_TYPE timer1Mux; #endif Stream* stream; diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 014eb4e..67491ce 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -21,10 +21,10 @@ #if defined(ESP32) -portMUX_TYPE dscKeybusInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE dscKeybusInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; #if ESP_IDF_VERSION_MAJOR < 4 -hw_timer_t * dscKeybusInterface::timer0 = NULL; +hw_timer_t * dscKeybusInterface::timer1 = NULL; #else // ESP-IDF 4+ esp_timer_handle_t timer0; @@ -68,14 +68,14 @@ void dscKeybusInterface::begin(Stream &_stream) { timer1_attachInterrupt(dscDataInterrupt); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); - // esp32 timer0 calls dscDataInterrupt() from dscClockInterrupt() + // esp32 timer1 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timer0 = timerBegin(0, 80, true); - timerStop(timer0); - timerAttachInterrupt(timer0, &dscDataInterrupt, true); - timerAlarmWrite(timer0, 250, true); - timerAlarmEnable(timer0); + timer1 = timerBegin(1, 80, true); + timerStop(timer1); + timerAttachInterrupt(timer1, &dscDataInterrupt, true); + timerAlarmWrite(timer1, 250, true); + timerAlarmEnable(timer1); #else // IDF4+ esp_timer_create(&timer0Parameters, &timer0); #endif // ESP_IDF_VERSION_MAJOR @@ -97,11 +97,11 @@ void dscKeybusInterface::stop() { timer1_disable(); timer1_detachInterrupt(); - // Disables esp32 timer0 + // Disables esp32 timer1 #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerAlarmDisable(timer0); - timerEnd(timer0); + timerAlarmDisable(timer1); + timerEnd(timer1); #else // ESP-IDF 4+ esp_timer_stop(timer0); #endif // ESP_IDF_VERSION_MAJOR @@ -130,7 +130,7 @@ bool dscKeybusInterface::loop() { // Checks if Keybus data is detected and sets a status flag if data is not detected for 3s #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -139,7 +139,7 @@ bool dscKeybusInterface::loop() { else keybusConnected = true; #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -167,7 +167,7 @@ bool dscKeybusInterface::loop() { // Resets counters when the buffer is cleared #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -178,7 +178,7 @@ bool dscKeybusInterface::loop() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -247,7 +247,7 @@ bool dscKeybusInterface::handlePanel() { // Checks if Keybus data is detected and sets a status flag if data is not detected for 3s #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -256,7 +256,7 @@ bool dscKeybusInterface::handlePanel() { else keybusConnected = true; #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -284,7 +284,7 @@ bool dscKeybusInterface::handlePanel() { // Resets counters when the buffer is cleared #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -295,7 +295,7 @@ bool dscKeybusInterface::handlePanel() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -642,14 +642,14 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { #elif defined(ESP8266) timer1_write(1250); - // esp32 timer0 calls dscDataInterrupt() in 250us + // esp32 timer1 calls dscDataInterrupt() in 250us #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStart(timer0); + timerStart(timer1); #else // IDF4+ esp_timer_start_periodic(timer0, 250); #endif - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #endif static unsigned long previousClockHighTime; @@ -782,12 +782,12 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { } } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #endif } -// Interrupt function called by AVR Timer1, esp8266 timer1, and esp32 timer0 after 250us to read the data line +// Interrupt function called by AVR Timer1, esp8266 timer1, and esp32 timer1 after 250us to read the data line #if defined(__AVR__) void dscKeybusInterface::dscDataInterrupt() { #elif defined(ESP8266) @@ -795,11 +795,11 @@ void ICACHE_RAM_ATTR dscKeybusInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { #if ESP_IDF_VERSION_MAJOR < 4 - timerStop(timer0); + timerStop(timer1); #else // IDF 4+ esp_timer_stop(timer0); #endif - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #endif // Panel sends data while the clock is high @@ -870,6 +870,6 @@ void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { } } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #endif } diff --git a/src/dscKeypad.cpp b/src/dscKeypad.cpp index e2ec6a9..b8aba39 100644 --- a/src/dscKeypad.cpp +++ b/src/dscKeypad.cpp @@ -22,10 +22,10 @@ #if defined(ESP32) -portMUX_TYPE dscKeypadInterface::timer0Mux = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE dscKeypadInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; #if ESP_IDF_VERSION_MAJOR < 4 -hw_timer_t * dscKeypadInterface::timer0 = NULL; +hw_timer_t * dscKeypadInterface::timer1 = NULL; #else // ESP-IDF 4+ esp_timer_handle_t timer2; @@ -68,14 +68,14 @@ void dscKeypadInterface::begin(Stream &_stream) { timer1_attachInterrupt(dscClockInterrupt); timer1_write(2500); - // esp32 timer0 calls dscClockInterrupt() + // esp32 timer1 calls dscClockInterrupt() #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timer0 = timerBegin(0, 80, true); - timerStop(timer0); - timerAttachInterrupt(timer0, &dscClockInterrupt, true); - timerAlarmWrite(timer0, 500, true); - timerAlarmEnable(timer0); + timer1 = timerBegin(1, 80, true); + timerStop(timer1); + timerAttachInterrupt(timer1, &dscClockInterrupt, true); + timerAlarmWrite(timer1, 500, true); + timerAlarmEnable(timer1); #else // IDF4+ esp_timer_create(&timer2Parameters, &timer2); #endif // ESP_IDF_VERSION_MAJOR @@ -240,7 +240,7 @@ bool dscKeypadInterface::loop() { timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStart(timer0); + timerStart(timer1); #else // IDF4+ esp_timer_start_periodic(timer2, 500); #endif // ESP_IDF_VERSION_MAJOR @@ -279,7 +279,7 @@ bool dscKeypadInterface::loop() { // Resets counters when the buffer is cleared #if defined(ESP32) - portENTER_CRITICAL(&timer0Mux); + portENTER_CRITICAL(&timer1Mux); #else noInterrupts(); #endif @@ -290,7 +290,7 @@ bool dscKeypadInterface::loop() { } #if defined(ESP32) - portEXIT_CRITICAL(&timer0Mux); + portEXIT_CRITICAL(&timer1Mux); #else interrupts(); #endif @@ -555,7 +555,7 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { timer1_disable(); #elif defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - timerStop(timer0); + timerStop(timer1); #else // IDF4+ esp_timer_stop(timer2); #endif // ESP_IDF_VERSION_MAJOR diff --git a/src/dscKeypad.h b/src/dscKeypad.h index eb8de73..7983baf 100644 --- a/src/dscKeypad.h +++ b/src/dscKeypad.h @@ -93,9 +93,9 @@ class dscKeypadInterface { #if defined(ESP32) #if ESP_IDF_VERSION_MAJOR < 4 - static hw_timer_t * timer0; + static hw_timer_t * timer1; #endif - static portMUX_TYPE timer0Mux; + static portMUX_TYPE timer1Mux; #endif static int clockInterval; From fad1aaabaa2444b45cdf3ce7f8991c8f0693122a Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 21 May 2021 11:39:48 +0300 Subject: [PATCH 20/49] Update classic series decoding for PC2550 #196 --- examples/esp32/KeybusReaderIP/.DS_Store | Bin 6148 -> 0 bytes src/dscClassic.cpp | 12 +++++------- 2 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 examples/esp32/KeybusReaderIP/.DS_Store diff --git a/examples/esp32/KeybusReaderIP/.DS_Store b/examples/esp32/KeybusReaderIP/.DS_Store deleted file mode 100644 index 8b159c0a656bb82675266e041799205255448d0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu}T9$5Pg%RIMgDgV6$H!*o!PN7Ivawt!IK99vakucGvkO{-uu^3cuPbl{Q59GX~ z?(vs+#yKu+<+Rr(`VIZtSZn1B){2SNin-8My#J#v=`;6p!Oqd>%p08; Oe*{#Quu$MH6!-!;print(F("Memory ")); if (bitRead(panelData[1], 4)) stream->print(F("Bypass ")); if (bitRead(panelData[1], 3)) stream->print(F("Trouble ")); + if (bitRead(panelData[1], 2)) stream->print(F("Program ")); + if (bitRead(panelData[1], 1)) stream->print(F("Fire ")); if (bitRead(panelData[1], 0)) stream->print(F("Beep ")); } else stream->print(F("none ")); @@ -852,6 +849,7 @@ void dscClassicInterface::printPanelMessage() { if (bitRead(pc16Data[1], 7)) stream->print(F("Trouble ")); if (bitRead(pc16Data[1], 6)) stream->print(F("Armed with bypassed zones ")); if (bitRead(pc16Data[1], 5)) stream->print(F("Armed ")); + if (bitRead(pc16Data[1], 4)) stream->print(F("Armed (B) ")); if (bitRead(pc16Data[1], 3)) stream->print(F("Keypad Panic alarm ")); if (bitRead(pc16Data[1], 2)) stream->print(F("Keypad Aux alarm ")); if (bitRead(pc16Data[1], 1)) stream->print(F("Keypad Fire alarm ")); From 135087b4cf4556b16cdb6c182c031a4e887d328e Mon Sep 17 00:00:00 2001 From: kricon Date: Sat, 22 May 2021 05:54:45 +0200 Subject: [PATCH 21/49] Tested and updated TinyGSM-SMS Arduino sketch Redo the wiring, changed baud rate to 9600 as it won't work with 115200, tested and working fine on sim800l module --- examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino | 203 +++++++++---------- 1 file changed, 96 insertions(+), 107 deletions(-) diff --git a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino index fd9d036..837083a 100644 --- a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino @@ -1,5 +1,5 @@ /* - * TinyGSM SMS Notification 0.1 (Arduino) + * TinyGSM SMS Notification 1.0 (Arduino) * * Processes the security system status and demonstrates how to send an SMS text message when the status has * changed. This example sends SMS text messages via a TinyGSM-compatible SIM800L module which is connected @@ -11,7 +11,7 @@ * 2. Set the destination phone numbers in the sketch settings. * * Release notes: - * 0.1 - Just converted from ESP32 to Arduino. NOT TESTED - waiting to receive the SIM800L module to test with + * 1.0 - Just converted from ESP32 to Arduino. Tested with SIM800L module. * * Wiring: * DSC Aux(+) --- Arduino Vin pin @@ -35,12 +35,12 @@ * * * Connecting SIM800L module with Arduino Uno: - * SIM800 RX -+-- 5.6k ohm resistor -- SIM800RxPin (Arduino Uno: 2-12) + * SIM800 RX -+-- 5.6k ohm resistor -- SIM800TxPin (Arduino Uno: 2-12) * | * | * +-- 10k ohm resistor -- Arduino Uno Ground * - * SIM800 TX ---- SIM800TxPin (Arduino Uno: 2-12) + * SIM800 TX ---- SIM800RxPin (Arduino Uno: 2-12) * * SIM800 GND --- Arduino Uno Ground * @@ -83,13 +83,6 @@ const char* sendToPhoneNumbers[] = { #define phone_number_count (sizeof (sendToPhoneNumbers) / sizeof (const char *)) -// Configures the GSM modem interface with the specified pins (eg. TTGO T-Call 1.3 v20190601). -#define MODEM_RST 5 -#define MODEM_PWRKEY 4 -#define MODEM_POWER_ON 23 -#define MODEM_TX 27 -#define MODEM_RX 26 - // Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the // virtual keypad. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 @@ -100,12 +93,15 @@ const char* sendToPhoneNumbers[] = { #define SIM800TxPin 10 // Arduino Uno: 2-12 // Settings -bool notifyOnPowerTroubles = true; -bool notifyOnDisArming = true; +//NOTE: I kept getting "Keybus buffer overflow" when sketch was sending multiple messages at once (example: AC power trouble and trouble status ON) +bool notifyOnPartitionAlarm = true; +bool notifyOnPowerTroubles = false; +bool notifyOnKeypadAlarm = false; +bool notifyOnDisArming = false; bool notifyOnTrouble = true; // Initialize components -SoftwareSerial serialSIM800(SIM800TxPin,SIM800RxPin); +SoftwareSerial serialSIM800(SIM800RxPin,SIM800TxPin); #ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); #else @@ -114,25 +110,12 @@ dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); TinyGsm modem(serialSIM800); void setup() { - Serial.begin(115200); + Serial.begin(9600); delay(1000); Serial.println(); Serial.println(); - pinMode(MODEM_PWRKEY, OUTPUT); - pinMode(MODEM_POWER_ON, OUTPUT); - - // Turn on the Modem power first - digitalWrite(MODEM_POWER_ON, HIGH); - - // Pull down PWRKEY for more than 1 second according to manual requirements - digitalWrite(MODEM_PWRKEY, HIGH); - delay(100); - digitalWrite(MODEM_PWRKEY, LOW); - delay(1000); - digitalWrite(MODEM_PWRKEY, HIGH); - - serialSIM800.begin(115200); + serialSIM800.begin(9600); while (!modem.isNetworkConnected()) { Serial.print(F("GSM...")); @@ -178,93 +161,97 @@ void loop() { if (dsc.disabled[partition]) continue; // Checks alarm triggered status - if (dsc.alarmChanged[partition]) { - dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag - - if (dsc.alarm[partition]) { - char messageContent[19] = "Alarm: Partition "; - appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent); - } - else { - char messageContent[34] = "Disarmed after alarm: Partition "; - appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent); - } - } + if (notifyOnPartitionAlarm) { + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag - if (dsc.fireChanged[partition]) { - dsc.fireChanged[partition] = false; // Resets the fire status flag - - if (dsc.fire[partition]) { - char messageContent[24] = "Fire alarm: Partition "; - appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent); - } - else { - char messageContent[33] = "Fire alarm restored: Partition "; - appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent); - } - } - - // Publishes armed/disarmed status - if (dsc.armedChanged[partition]) { - if (!notifyOnDisArming) continue; - if (dsc.armed[partition]) { - // Night armed away - if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { - char messageContent[33] = "Armed away - night: Partition: "; + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } - - // Armed away - else if (dsc.armedAway[partition]) { - char messageContent[25] = "Armed away: Partition: "; + else { + char messageContent[34] = "Disarmed after alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } + } - // Night armed stay - else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { - char messageContent[33] = "Armed stay - night: Partition: "; + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } - - // Armed stay - else if (dsc.armedStay[partition]) { - char messageContent[25] = "Armed stay: Partition: "; + else { + char messageContent[33] = "Fire alarm restored: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } } + } + + // Publishes armed/disarmed status + if (notifyOnDisArming) { + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + // Night armed away + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed away - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed away + else if (dsc.armedAway[partition]) { + char messageContent[25] = "Armed away: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Night armed stay + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed stay - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed stay + else if (dsc.armedStay[partition]) { + char messageContent[25] = "Armed stay: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } - // Disarmed - else { - char messageContent[23] = "Disarmed: Partition: "; - appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent); + // Disarmed + else { + char messageContent[23] = "Disarmed: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } } } } + // Checks trouble status - if (dsc.troubleChanged) { - if (!notifyOnTrouble) return; - dsc.troubleChanged = false; // Resets the trouble status flag - if (dsc.trouble) sendMessage("Trouble status on"); - else sendMessage("Trouble status restored"); + if (notifyOnTrouble) { + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } } - // Checks for AC power status - if (dsc.powerChanged) { - if (!notifyOnPowerTroubles) return; - dsc.powerChanged = false; // Resets the battery trouble status flag - if (dsc.powerTrouble) sendMessage("AC power trouble"); - else sendMessage("AC power restored"); + if (notifyOnPowerTroubles) { + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } } - // Checks panel battery status if (dsc.batteryChanged) { dsc.batteryChanged = false; // Resets the battery trouble status flag @@ -272,22 +259,24 @@ void loop() { else sendMessage("Panel battery restored"); } - // Checks for keypad fire alarm status - if (dsc.keypadFireAlarm) { - dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag - sendMessage("Keypad Fire alarm"); - } + if (notifyOnKeypadAlarm) { + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } - // Checks for keypad aux auxiliary alarm status - if (dsc.keypadAuxAlarm) { - dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag - sendMessage("Keypad Aux alarm"); - } + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } - // Checks for keypad panic alarm status - if (dsc.keypadPanicAlarm) { - dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag - sendMessage("Keypad Panic alarm"); + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } } } } From 2d3171b07f9038a441771d9a89f6f14314e06bdb Mon Sep 17 00:00:00 2001 From: kricon Date: Sat, 22 May 2021 06:00:57 +0200 Subject: [PATCH 22/49] Added TinyGSM-SMS esp8266 example sketch Tested with NodeMCU and SIM800L module. --- examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino | 293 +++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino diff --git a/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino new file mode 100644 index 0000000..4b3f62a --- /dev/null +++ b/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino @@ -0,0 +1,293 @@ +/* + * TinyGSM SMS Notification 1.0 (esp8266) + * + * Processes the security system status and demonstrates how to send an SMS text message when the status has + * changed. This example sends SMS text messages via a TinyGSM-compatible SIM800L module which is connected + * onto ESP8266 board. Make sure that Micro-SIM card have PIN Code turned off (disable SIM Lock from phone). + * + * Usage: + * 1. Install the TinyGSM library, available in the Arduino IDE Library Manager and the Platform.io Library + * Registry: https://github.com/vshymanskyy/TinyGSM + * 2. Set the destination phone numbers in the sketch settings. + * + * Release notes: + * 1.0 - Just converted from Arduino to ESP8266. Tested with SIM800L module. + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp8266 development board 5v pin (NodeMCU, Wemos) + * + * DSC Aux(-) --- esp8266 Ground + * + * +--- dscClockPin // Default: D1, GPIO 5 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: D2, GPIO 4 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Connecting SIM800L module with ESP8266: + * SIM800 RX ---- SIM800TxPin // Default: D5, GPIO 14 + * + * SIM800 TX ---- SIM800RxPin // Default: D6, GPIO 12 + * + * SIM800 GND --- esp8266 development board ground (NodeMCU, Wemos) + * + * SIM800 Vcc --- (+) 3.7 - 4.4V power supply which can supply burst current of 2A + * (you can use LM2596 Buck converter set to output about 4V and connected to Bell+ and Aux-) + * + * Virtual keypad (optional): + * DSC Green ---- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: D8, GPIO 15 + * Ground --- NPN emitter --/ + * + * + * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should + * be suitable, for example: + * -- 2N3904 + * -- BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * Based on TinyGSM-SMS example for ESP32 by jvitkauskas: https://github.com/jvitkauskas + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +// Configures GSM modem model. Must be done before including TinyGsmClient library. +#define TINY_GSM_MODEM_SIM800 +#include +#include +#include + +// Settings +const char* sendToPhoneNumbers[] = { + "+1234567890", + "+2345678901" +}; + +#define phone_number_count (sizeof (sendToPhoneNumbers) / sizeof (const char *)) + +// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the +// virtual keypad. +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define SIM800TxPin D5 // GPIO 14 +#define SIM800RxPin D6 // GPIO 12 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +#define dscWritePin D8 // GPIO 15 + +// Settings +bool notifyOnPartitionAlarm = true; +bool notifyOnPowerTroubles = true; +bool notifyOnKeypadAlarm = true; +bool notifyOnDisArming = false; +bool notifyOnTrouble = true; + +// Initialize components +SoftwareSerial serialSIM800(SIM800RxPin,SIM800TxPin); +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +TinyGsm modem(serialSIM800); + +void setup() { + Serial.begin(9600); + delay(1000); + Serial.println(); + Serial.println(); + + serialSIM800.begin(9600); + + while (!modem.isNetworkConnected()) { + Serial.print(F("GSM...")); + while (!modem.restart()) { + Serial.print("."); + } + Serial.println(); + + Serial.print(F("Waiting for network...")); + if (modem.waitForNetwork(600000L) && modem.isNetworkConnected()) { + Serial.println(F("connected.")); + } + else { + Serial.println(F("connection error.")); + } + } + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + modem.maintain(); + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks alarm triggered status + if (notifyOnPartitionAlarm) { + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[34] = "Disarmed after alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + + // Publishes armed/disarmed status + if (notifyOnDisArming) { + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + // Night armed away + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed away - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed away + else if (dsc.armedAway[partition]) { + char messageContent[25] = "Armed away: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Night armed stay + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + char messageContent[33] = "Armed stay - night: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + + // Armed stay + else if (dsc.armedStay[partition]) { + char messageContent[25] = "Armed stay: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Disarmed + else { + char messageContent[23] = "Disarmed: Partition: "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + } + + // Checks trouble status + if (notifyOnTrouble) { + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + } + // Checks for AC power status + if (notifyOnPowerTroubles) { + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + } + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + if (notifyOnKeypadAlarm) { + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } + } +} + +bool sendMessage(const char* messageContent) { + bool result = true; + + for (int i = 0; i < phone_number_count; i++) { + result &= modem.sendSMS(sendToPhoneNumbers[i], messageContent); + } + + return result; +} + +void appendPartition(byte sourceNumber, char* pushMessage) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(pushMessage, partitionNumber); +} From c32d055bfb3801914609e9cf2ad2a57f9067f8bf Mon Sep 17 00:00:00 2001 From: kricon Date: Sat, 22 May 2021 06:20:07 +0200 Subject: [PATCH 23/49] Updated PC2550 classic series for KeypadInterface example Verified with Wemos D1 and PC2550RK keypad using KeypadInterface example Keypad beeps and flashing lights aren't yet supported on Classic keypad series PC2550 (and PC3000) can't distinguish Zone alarms on zones 5-8 and 9-16 --- src/dscClassic.cpp | 23 +++++++++++++++++++++-- src/dscClassic.h | 2 +- src/dscClassicKeypad.cpp | 4 ++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp index 27f0f3e..fbc644f 100644 --- a/src/dscClassic.cpp +++ b/src/dscClassic.cpp @@ -274,6 +274,22 @@ void dscClassicInterface::processPanelStatus() { troubleLight = false; bitWrite(lights[0], 4, 0); } + if (bitRead(panelData[1], 2)) { + programLight = true; + bitWrite(lights[0], 5, 1); + } + else { + programLight = false; + bitWrite(lights[0], 5, 0); + } + if (bitRead(panelData[1], 1)) { + fireLight = true; + bitWrite(lights[0], 6, 1); + } + else { + fireLight = false; + bitWrite(lights[0], 6, 0); + } if (bitRead(panelData[1], 0)) beep = true; else beep = false; @@ -843,6 +859,8 @@ void dscClassicInterface::printPanelMessage() { if (bitRead(panelData[1], 5)) stream->print(F("Memory ")); if (bitRead(panelData[1], 4)) stream->print(F("Bypass ")); if (bitRead(panelData[1], 3)) stream->print(F("Trouble ")); + if (bitRead(panelData[1], 2)) stream->print(F("Program ")); + if (bitRead(panelData[1], 1)) stream->print(F("Fire ")); if (bitRead(panelData[1], 0)) stream->print(F("Beep ")); } else stream->print(F("none ")); @@ -850,8 +868,9 @@ void dscClassicInterface::printPanelMessage() { stream->print(F("| Status: ")); if (pc16Data[1]) { if (bitRead(pc16Data[1], 7)) stream->print(F("Trouble ")); - if (bitRead(pc16Data[1], 6)) stream->print(F("Armed with bypassed zones ")); - if (bitRead(pc16Data[1], 5)) stream->print(F("Armed ")); + if (bitRead(pc16Data[1], 5)) stream->print(F("Armed A ")); + if (bitRead(pc16Data[1], 4)) stream->print(F("Armed B ")); + if (bitRead(pc16Data[1], 6)) stream->print(F("with bypassed zones ")); if (bitRead(pc16Data[1], 3)) stream->print(F("Keypad Panic alarm ")); if (bitRead(pc16Data[1], 2)) stream->print(F("Keypad Aux alarm ")); if (bitRead(pc16Data[1], 1)) stream->print(F("Keypad Fire alarm ")); diff --git a/src/dscClassic.h b/src/dscClassic.h index 20c4f1d..a57bf94 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -97,7 +97,7 @@ class dscClassicInterface { byte alarmZones[dscZones], alarmZonesChanged[dscZones]; // Zone alarm status is stored in an array using 1 bit per zone, up to 64 zones bool pgmOutputsStatusChanged; byte pgmOutputs[1], pgmOutputsChanged[1]; - bool armedLight, memoryLight, bypassLight, troubleLight, beep; + bool armedLight, memoryLight, bypassLight, troubleLight, programLight, fireLight, beep; static volatile bool readyLight, lightBlink; bool readyBlink, armedBlink, memoryBlink, bypassBlink, troubleBlink; diff --git a/src/dscClassicKeypad.cpp b/src/dscClassicKeypad.cpp index 4baa358..ad10bc2 100644 --- a/src/dscClassicKeypad.cpp +++ b/src/dscClassicKeypad.cpp @@ -152,6 +152,8 @@ bool dscClassicKeypadInterface::loop() { panelLight(lightMemory, 5); panelLight(lightBypass, 4); panelLight(lightTrouble, 3); + panelLight(lightProgram, 2); + panelLight(lightFire, 1); // Sets zone lights zoneLight(lightZone1, 7); @@ -160,6 +162,8 @@ bool dscClassicKeypadInterface::loop() { zoneLight(lightZone4, 4); zoneLight(lightZone5, 3); zoneLight(lightZone6, 2); + zoneLight(lightZone7, 1); + zoneLight(lightZone8, 0); // Skips key processing if the key buffer is empty if (keyBufferLength == 0) return false; From 199600bdb3981b0529268b642d422255d6619d5a Mon Sep 17 00:00:00 2001 From: kricon Date: Wed, 26 May 2021 03:34:54 +0200 Subject: [PATCH 24/49] Reset partition armed status flag to prevent sending multiple messages --- examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino | 3 ++- examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino index 837083a..8dc4f30 100644 --- a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino @@ -232,6 +232,7 @@ void loop() { appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag } } } @@ -250,7 +251,7 @@ void loop() { dsc.powerChanged = false; // Resets the battery trouble status flag if (dsc.powerTrouble) sendMessage("AC power trouble"); else sendMessage("AC power restored"); - } + } } // Checks panel battery status if (dsc.batteryChanged) { diff --git a/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino index 4b3f62a..2def9b1 100644 --- a/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp8266/TinyGSM-SMS/TinyGSM-SMS.ino @@ -227,6 +227,7 @@ void loop() { appendPartition(partition, messageContent); // Appends the message with the partition number sendMessage(messageContent); } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag } } } @@ -245,7 +246,7 @@ void loop() { dsc.powerChanged = false; // Resets the battery trouble status flag if (dsc.powerTrouble) sendMessage("AC power trouble"); else sendMessage("AC power restored"); - } + } } // Checks panel battery status if (dsc.batteryChanged) { From ead3477e55c7f2df3bd614ead5dcb25757be5bd7 Mon Sep 17 00:00:00 2001 From: shafr Date: Fri, 28 May 2021 17:05:28 +0200 Subject: [PATCH 25/49] added ci-dc stuff only for now --- .github/workflows/examples_arduino.yml | 52 +++++++++++++++++++++ .github/workflows/examples_esp32.yml | 63 ++++++++++++++++++++++++++ .github/workflows/examples_esp8266.yml | 60 ++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 .github/workflows/examples_arduino.yml create mode 100644 .github/workflows/examples_esp32.yml create mode 100644 .github/workflows/examples_esp8266.yml diff --git a/.github/workflows/examples_arduino.yml b/.github/workflows/examples_arduino.yml new file mode 100644 index 0000000..ec743f0 --- /dev/null +++ b/.github/workflows/examples_arduino.yml @@ -0,0 +1,52 @@ +name: Compile examples for Arduino + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino" + - "examples/Arduino/KeybusReader/KeybusReader.ino" + - "examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/Arduino/Status/Status.ino" + - "examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/Arduino/Unlocker/Unlocker.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/arduino-libraries/Ethernet \ + https://github.com/PaulStoffregen/Time + + - name: Run PlatformIO Examples + run: pio ci --board=uno + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/examples_esp32.yml b/.github/workflows/examples_esp32.yml new file mode 100644 index 0000000..bcab926 --- /dev/null +++ b/.github/workflows/examples_esp32.yml @@ -0,0 +1,63 @@ +name: Compile examples for ESP32 + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/esp32/Email/Email.ino" + - "examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino" + - "examples/esp32/Homey/Homey.ino" + - "examples/esp32/KeybusReader/KeybusReader.ino" + - "examples/esp32/KeybusReaderIP/KeybusReaderIP.ino" + - "examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/esp32/Pushbullet/Pushbullet.ino" + - "examples/esp32/Status/Status.ino" + - "examples/esp32/Telegram/Telegram.ino" + - "examples/esp32/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino" + - "examples/esp32/Twilio-SMS/Twilio-SMS.ino" + - "examples/esp32/Unlocker/Unlocker.ino" + - "examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" + - "examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ + https://github.com/blynkkk/blynk-library \ + https://github.com/me-no-dev/ESPAsyncWebServer \ + https://github.com/vshymanskyy/TinyGSM + + - name: Run PlatformIO Examples + run: pio ci --board=lolin32 + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/examples_esp8266.yml b/.github/workflows/examples_esp8266.yml new file mode 100644 index 0000000..f26906a --- /dev/null +++ b/.github/workflows/examples_esp8266.yml @@ -0,0 +1,60 @@ +name: Compile examples for ESP8266 + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/esp8266/Email/Email.ino" + - "examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/esp8266/Homey/Homey.ino" + - "examples/esp8266/KeybusReader/KeybusReader.ino" + - "examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino" + - "examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/esp8266/Pushbullet/Pushbullet.ino" + - "examples/esp8266/Status/Status.ino" + - "examples/esp8266/Telegram/Telegram.ino" + - "examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/esp8266/Twilio-SMS/Twilio-SMS.ino" + - "examples/esp8266/Unlocker/Unlocker.ino" + - "examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" + - "examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ + https://github.com/blynkkk/blynk-library \ + https://github.com/me-no-dev/ESPAsyncWebServer + + - name: Run PlatformIO Examples + run: pio ci --board=nodemcuv2 + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} From be0e1777a524e3af64f8e463ca45bcde867d6127 Mon Sep 17 00:00:00 2001 From: kricon Date: Wed, 2 Jun 2021 14:31:47 +0200 Subject: [PATCH 26/49] Fixed typo in ESP8266 HomeAssistant-MQTT example --- examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 7e5ae6f..92c1669 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -555,7 +555,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x12: mqtt.publish(publishTopic, "Battery check in progress"); break; case 0x14: mqtt.publish(publishTopic, "Auto-arm in progress", true); break; case 0x15: mqtt.publish(publishTopic, "Arming with bypassed zones", true); break; - case 0x06: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; + case 0x16: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; case 0x17: mqtt.publish(publishTopic, "Power saving: Keypad blanked", true); break; case 0x19: mqtt.publish(publishTopic, "Disarmed: Alarm memory"); break; case 0x22: mqtt.publish(publishTopic, "Disarmed: Recent closing", true); break; From 90543e466bb74455d81c2e0007e9a96ea653ee3e Mon Sep 17 00:00:00 2001 From: kricon Date: Wed, 2 Jun 2021 14:48:50 +0200 Subject: [PATCH 27/49] Updated KeypadInterface notes with support for PC2550 lights --- examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 3 ++- examples/Arduino/KeypadInterface/KeypadInterface.ino | 3 ++- examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 3 ++- examples/esp32/KeypadInterface/KeypadInterface.ino | 3 ++- examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 3 ++- examples/esp8266/KeypadInterface/KeypadInterface.ino | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index a969d7b..a4bb21b 100644 --- a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -20,9 +20,10 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 3f4e961..0f018af 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -25,10 +25,11 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 098ab0c..8d5d9c2 100644 --- a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -20,9 +20,10 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index d21449d..dfe8f56 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -25,10 +25,11 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 3284930..a687e4a 100644 --- a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -20,9 +20,10 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index bc42788..1ce3b28 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -25,10 +25,11 @@ * * Classic keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key - * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc + * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release From 4dfa089479844f832b19487254fba3d99fd74d4d Mon Sep 17 00:00:00 2001 From: shafr Date: Wed, 30 Jun 2021 13:45:53 +0200 Subject: [PATCH 28/49] ci/dc upgrade --- .github/workflows/compile_examples.yml | 167 +++++++++++++++++++++++++ .github/workflows/compile_library.yml | 53 ++++++++ .github/workflows/cpp_lint.yml | 26 ++++ .github/workflows/examples_arduino.yml | 52 -------- .github/workflows/examples_esp32.yml | 63 ---------- .github/workflows/examples_esp8266.yml | 60 --------- 6 files changed, 246 insertions(+), 175 deletions(-) create mode 100644 .github/workflows/compile_examples.yml create mode 100644 .github/workflows/compile_library.yml create mode 100644 .github/workflows/cpp_lint.yml delete mode 100644 .github/workflows/examples_arduino.yml delete mode 100644 .github/workflows/examples_esp32.yml delete mode 100644 .github/workflows/examples_esp8266.yml diff --git a/.github/workflows/compile_examples.yml b/.github/workflows/compile_examples.yml new file mode 100644 index 0000000..2a85dad --- /dev/null +++ b/.github/workflows/compile_examples.yml @@ -0,0 +1,167 @@ +name: Compile examples for Arduino + +on: [push] + +jobs: + arduino: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino" + - "examples/Arduino/KeybusReader/KeybusReader.ino" + - "examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/Arduino/Status/Status.ino" + - "examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/Arduino/Unlocker/Unlocker.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/arduino-libraries/Ethernet \ + https://github.com/PaulStoffregen/Time + + - name: Run PlatformIO Examples + run: pio ci --board=uno + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} + + esp8266: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/esp8266/Email/Email.ino" + - "examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/esp8266/Homey/Homey.ino" + - "examples/esp8266/KeybusReader/KeybusReader.ino" + - "examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino" + - "examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/esp8266/Pushbullet/Pushbullet.ino" + - "examples/esp8266/Status/Status.ino" + - "examples/esp8266/Telegram/Telegram.ino" + - "examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/esp8266/Twilio-SMS/Twilio-SMS.ino" + - "examples/esp8266/Unlocker/Unlocker.ino" + - "examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" + - "examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ + https://github.com/blynkkk/blynk-library \ + https://github.com/me-no-dev/ESPAsyncWebServer + + - name: Run PlatformIO Examples + run: pio ci --board=nodemcuv2 + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} + + esp32: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - "examples/esp32/Email/Email.ino" + - "examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" + - "examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino" + - "examples/esp32/Homey/Homey.ino" + - "examples/esp32/KeybusReader/KeybusReader.ino" + - "examples/esp32/KeybusReaderIP/KeybusReaderIP.ino" + - "examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino" + - "examples/esp32/Pushbullet/Pushbullet.ino" + - "examples/esp32/Status/Status.ino" + - "examples/esp32/Telegram/Telegram.ino" + - "examples/esp32/TimeSyncNTP/TimeSyncNTP.ino" + - "examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino" + - "examples/esp32/Twilio-SMS/Twilio-SMS.ino" + - "examples/esp32/Unlocker/Unlocker.ino" + - "examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" + - "examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install 3rd party dependecies + run: | + pio lib -g install \ + https://github.com/SofaPirate/Chrono \ + https://github.com/bblanchon/ArduinoJson \ + https://github.com/taligentx/dscKeybusInterface \ + https://github.com/knolleary/pubsubclient \ + https://github.com/athombv/homey-arduino-library \ + https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ + https://github.com/blynkkk/blynk-library \ + https://github.com/me-no-dev/ESPAsyncWebServer \ + https://github.com/vshymanskyy/TinyGSM + + - name: Run PlatformIO Examples + run: pio ci --board=lolin32 + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/compile_library.yml b/.github/workflows/compile_library.yml new file mode 100644 index 0000000..86b20de --- /dev/null +++ b/.github/workflows/compile_library.yml @@ -0,0 +1,53 @@ +name: Compile Library + +on: + push: + paths-ignore: + - '.github/workflows/cpp_lint.yml' + - '.github/workflows/compile_examples.yml' + - 'examples/**' + pull_request: + paths-ignore: + - '.github/workflows/cpp_lint.yml' + - '.github/workflows/compile_examples.yml' + - 'examples/**' + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + board: + - "nodemcuv2" + - "lolin32" + - "uno" + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + + - name: Create main file + run: | + echo "#include " >> src/main.ino + echo "void setup() {}" >> src/main.ino + echo "void loop() {}" >> src/main.ino + + - name: Run PlatformIO + run: pio ci --board=${{ matrix.board }} src diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml new file mode 100644 index 0000000..ed81cc7 --- /dev/null +++ b/.github/workflows/cpp_lint.yml @@ -0,0 +1,26 @@ +name: cpplint + +on: + pull_request: + paths-ignore: + - '.github/workflows/compile_*.yml' +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: cpplint + uses: reviewdog/action-cpplint@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-check + flags: --linelength=100 + filter: "-whitespace/tab\ + ,-readability/braces\ + ,-whitespace/braces\ + ,-whitespace/comments\ + ,-whitespace/indent\ + ,-whitespace/newline\ + ,-whitespace/operators\ + ,-whitespace/parens\ + " \ No newline at end of file diff --git a/.github/workflows/examples_arduino.yml b/.github/workflows/examples_arduino.yml deleted file mode 100644 index ec743f0..0000000 --- a/.github/workflows/examples_arduino.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Compile examples for Arduino - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino" - - "examples/Arduino/KeybusReader/KeybusReader.ino" - - "examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/Arduino/Status/Status.ino" - - "examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/Arduino/Unlocker/Unlocker.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/arduino-libraries/Ethernet \ - https://github.com/PaulStoffregen/Time - - - name: Run PlatformIO Examples - run: pio ci --board=uno - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/examples_esp32.yml b/.github/workflows/examples_esp32.yml deleted file mode 100644 index bcab926..0000000 --- a/.github/workflows/examples_esp32.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Compile examples for ESP32 - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/esp32/Email/Email.ino" - - "examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino" - - "examples/esp32/Homey/Homey.ino" - - "examples/esp32/KeybusReader/KeybusReader.ino" - - "examples/esp32/KeybusReaderIP/KeybusReaderIP.ino" - - "examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/esp32/Pushbullet/Pushbullet.ino" - - "examples/esp32/Status/Status.ino" - - "examples/esp32/Telegram/Telegram.ino" - - "examples/esp32/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino" - - "examples/esp32/Twilio-SMS/Twilio-SMS.ino" - - "examples/esp32/Unlocker/Unlocker.ino" - - "examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" - - "examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ - https://github.com/blynkkk/blynk-library \ - https://github.com/me-no-dev/ESPAsyncWebServer \ - https://github.com/vshymanskyy/TinyGSM - - - name: Run PlatformIO Examples - run: pio ci --board=lolin32 - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/examples_esp8266.yml b/.github/workflows/examples_esp8266.yml deleted file mode 100644 index f26906a..0000000 --- a/.github/workflows/examples_esp8266.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Compile examples for ESP8266 - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/esp8266/Email/Email.ino" - - "examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/esp8266/Homey/Homey.ino" - - "examples/esp8266/KeybusReader/KeybusReader.ino" - - "examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino" - - "examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/esp8266/Pushbullet/Pushbullet.ino" - - "examples/esp8266/Status/Status.ino" - - "examples/esp8266/Telegram/Telegram.ino" - - "examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/esp8266/Twilio-SMS/Twilio-SMS.ino" - - "examples/esp8266/Unlocker/Unlocker.ino" - - "examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" - - "examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ - https://github.com/blynkkk/blynk-library \ - https://github.com/me-no-dev/ESPAsyncWebServer - - - name: Run PlatformIO Examples - run: pio ci --board=nodemcuv2 - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} From fe47f40f35ed1c7d474a0d72ddd5377c0eae1df0 Mon Sep 17 00:00:00 2001 From: shafr Date: Wed, 30 Jun 2021 13:49:46 +0200 Subject: [PATCH 29/49] ci/dc upgrade --- .github/workflows/compile_examples.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compile_examples.yml b/.github/workflows/compile_examples.yml index 2a85dad..7aedd49 100644 --- a/.github/workflows/compile_examples.yml +++ b/.github/workflows/compile_examples.yml @@ -1,6 +1,14 @@ -name: Compile examples for Arduino +name: Compile Examples -on: [push] +on: + push: + paths-ignore: + - '.github/workflows/cpp_lint.yml' + - '.github/workflows/compile_library.yml' + pull_request: + paths-ignore: + - '.github/workflows/cpp_lint.yml' + - '.github/workflows/compile_library.yml' jobs: arduino: From 41289f2c252753e7a63604978b8e9d1c2045f399 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 23 Dec 2021 10:08:29 -0600 Subject: [PATCH 30/49] Update Pushbullet example HTTPS root certificate #259 --- examples/esp32/Pushbullet/Pushbullet.ino | 57 ++++++++++++---------- examples/esp8266/Pushbullet/Pushbullet.ino | 57 ++++++++++++---------- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index cd0eeec..95849ba 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -1,5 +1,5 @@ /* - * Pushbullet Push Notification 1.4 (esp32) + * Pushbullet Push Notification 1.5 (esp32) * * Processes the security system status and demonstrates how to send a push notification when the status has changed. * This example sends notifications via Pushbullet: https://www.pushbullet.com @@ -11,6 +11,7 @@ * 4. Upload the sketch. * * Release notes: + * 1.5 - Update HTTPS root certificate for api.pushbullet.com * 1.4 - Add HTTPS certificate validation, add customizable message prefix * 1.0 - Initial release * @@ -58,32 +59,38 @@ const char* messagePrefix = "[Security system] "; // Set a prefix for all messa #define dscReadPin 19 // 4,13,16-39 #define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 -// HTTPS root certificate for api.pushbullet.com: GlobalSign Root CA - R2, expires 2021.12.15 +// HTTPS root certificate for api.pushbullet.com: Google Trust Services GTS Root R1, expires 2036.06.21 const char pushbulletCertificateRoot[] = R"=EOF=( -----BEGIN CERTIFICATE----- -MIIESjCCAzKgAwIBAgINAeO0nXfN9AwGGRa24zANBgkqhkiG9w0BAQsFADBMMSAw -HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs -U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy -MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg -U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxRDIwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCy2Xvh4dc/HJFy//kQzYcVeXS3PkeLsmFV/Qw2xn53Qjqy -+lJbC3GB1k3V6SskTSNeiytyXyFVtSnvRMvrglKrPiekkklBSt6o3THgPN9tek0t -1m0JsA7jYfKy/pBsWnsQZEm0CzwI8up5DGymGolqVjKgKaIwgo+BUQzzornZdbki -nicUukovLGNYh/FdEOZfkbu5W8xH4h51toyPzHVdVwXngsaEDnRyKss7VfVucOtm -acMkuziTNZtoYS+b1q6md3J8cUhYMxCv6YCCHbUHQBv2PeyirUedtJQpNLOML80l -A1g1wCWkVV/hswdWPcjQY7gg+4wdQyz4+anV7G+XAgMBAAGjggEzMIIBLzAOBgNV -HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud -EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLHdMl3otzdy0s5czib+R3niAQjpMB8G -A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl -BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp -MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g -BDgwNjA0BgZngQwBAgEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y -ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAcUrEwyOu9+OyAnmME+hTjoDF -8OPvcWCpqXs0ZYU0vUc7A1cWAJlIOuDg8OrNtkg81aty8NAby2QtOw10aNd0iDF8 -aroO8IxNeM7aEPSKlkWXqZetxTUaGGTok7YNnR+5Xh2A6udbnI6uDqaE0tEXzrP7 -9oFPPOZon8/xpnbFfafz3X1YD+D2YQEcUY52MytInVyBUXIIF7r9AdPuRvn0smhA -mTEBbE8bxlbrgXPSeVIFkiZbcc2dxNLOI3cPQXppXiElxvi3/3r3R97CAHucWkWc -Kk5GkNl1LNj/jO7M3GnrbOYV0KP/SAusVd/fJZ1CtlGjZpVgxdAi5yJ6UaXMhw== +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c -----END CERTIFICATE----- )=EOF="; diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 57df83b..dd74140 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -1,5 +1,5 @@ /* - * Pushbullet Push Notification 1.4 (esp8266) + * Pushbullet Push Notification 1.5 (esp8266) * * Processes the security system status and demonstrates how to send a push notification when the status has changed. * This example sends notifications via Pushbullet: https://www.pushbullet.com @@ -11,6 +11,7 @@ * 4. Upload the sketch. * * Release notes: + * 1.5 - Update HTTPS root certificate for api.pushbullet.com * 1.4 - Add HTTPS certificate validation, add customizable message prefix * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Check if WiFi disconnects and wait to send updates until reconnection @@ -63,32 +64,38 @@ const char* messagePrefix = "[Security system] "; // Set a prefix for all messa #define dscReadPin D2 // GPIO 4 #define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 -// HTTPS root certificate for api.pushbullet.com: GlobalSign Root CA - R2, expires 2021.12.15 +// HTTPS root certificate for api.pushbullet.com: Google Trust Services GTS Root R1, expires 2036.06.21 const char pushbulletCertificateRoot[] = R"=EOF=( -----BEGIN CERTIFICATE----- -MIIESjCCAzKgAwIBAgINAeO0nXfN9AwGGRa24zANBgkqhkiG9w0BAQsFADBMMSAw -HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs -U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy -MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg -U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxRDIwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCy2Xvh4dc/HJFy//kQzYcVeXS3PkeLsmFV/Qw2xn53Qjqy -+lJbC3GB1k3V6SskTSNeiytyXyFVtSnvRMvrglKrPiekkklBSt6o3THgPN9tek0t -1m0JsA7jYfKy/pBsWnsQZEm0CzwI8up5DGymGolqVjKgKaIwgo+BUQzzornZdbki -nicUukovLGNYh/FdEOZfkbu5W8xH4h51toyPzHVdVwXngsaEDnRyKss7VfVucOtm -acMkuziTNZtoYS+b1q6md3J8cUhYMxCv6YCCHbUHQBv2PeyirUedtJQpNLOML80l -A1g1wCWkVV/hswdWPcjQY7gg+4wdQyz4+anV7G+XAgMBAAGjggEzMIIBLzAOBgNV -HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud -EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLHdMl3otzdy0s5czib+R3niAQjpMB8G -A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl -BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp -MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g -BDgwNjA0BgZngQwBAgEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y -ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAcUrEwyOu9+OyAnmME+hTjoDF -8OPvcWCpqXs0ZYU0vUc7A1cWAJlIOuDg8OrNtkg81aty8NAby2QtOw10aNd0iDF8 -aroO8IxNeM7aEPSKlkWXqZetxTUaGGTok7YNnR+5Xh2A6udbnI6uDqaE0tEXzrP7 -9oFPPOZon8/xpnbFfafz3X1YD+D2YQEcUY52MytInVyBUXIIF7r9AdPuRvn0smhA -mTEBbE8bxlbrgXPSeVIFkiZbcc2dxNLOI3cPQXppXiElxvi3/3r3R97CAHucWkWc -Kk5GkNl1LNj/jO7M3GnrbOYV0KP/SAusVd/fJZ1CtlGjZpVgxdAi5yJ6UaXMhw== +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c -----END CERTIFICATE----- )=EOF="; From 3a0d7d79103c93f405901b199a0f96f45bd97313 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 27 Jan 2022 11:12:30 +0300 Subject: [PATCH 31/49] Update examples release notes, update VirtualKeypad-Web webserver, reduce Arduino/AVR KeybusReader space usage --- README.md | 3 ++- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 3 ++- .../Homebridge-MQTT/Homebridge-MQTT.ino | 3 ++- .../Arduino/KeybusReader/KeybusReader.ino | 3 ++- .../Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 3 ++- examples/Arduino/Status/Status.ino | 5 ++-- examples/esp32/Email/Email.ino | 3 ++- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 3 ++- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 3 ++- examples/esp32/Homey/Homey.ino | 3 ++- examples/esp32/KeybusReader/KeybusReader.ino | 3 ++- .../esp32/KeybusReaderIP/KeybusReaderIP.ino | 3 ++- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 3 ++- examples/esp32/Pushbullet/Pushbullet.ino | 1 + examples/esp32/Status/Status.ino | 5 ++-- examples/esp32/Telegram/Telegram.ino | 3 ++- examples/esp32/TimeSyncNTP/TimeSyncNTP.ino | 1 - examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 3 ++- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 3 ++- .../VirtualKeypad-Blynk.ino | 25 +++++++++++-------- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 8 ++++-- examples/esp8266/Email/Email.ino | 3 ++- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 6 +++-- .../Homebridge-MQTT/Homebridge-MQTT.ino | 3 ++- examples/esp8266/Homey/Homey.ino | 3 ++- .../esp8266/KeybusReader/KeybusReader.ino | 3 ++- .../esp8266/KeybusReaderIP/KeybusReaderIP.ino | 3 ++- .../esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 3 ++- examples/esp8266/Pushbullet/Pushbullet.ino | 1 + examples/esp8266/Status/Status.ino | 9 ++++--- examples/esp8266/Telegram/Telegram.ino | 3 ++- examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino | 1 - examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 3 ++- .../VirtualKeypad-Blynk.ino | 25 +++++++++++-------- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 12 ++++++--- src/dscKeybusPrintData.cpp | 2 ++ 36 files changed, 109 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 7c343cc..280faa1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and Classic series security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. +This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. @@ -108,6 +108,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed + - Bugfix: `Pushbullet` example sketch updated TLS security certificate fingerprint * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index a41314a..cf05807 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -1,5 +1,5 @@ /* - * HomeAssistant-MQTT 1.4 (Arduino with Ethernet) + * HomeAssistant-MQTT 1.5 (Arduino with Ethernet) * * Processes the security system status and allows for control using Home Assistant via MQTT. * @@ -156,6 +156,7 @@ entity: alarm_control_panel.security_partition_1 * Closed: "0" * * Release notes + * 1.5 - Added DSC Classic series support * 1.4 - Added PGM outputs 1-14 status * 1.2 - Added night arm (arming with no entry delay) * Added status update on initial MQTT connection and reconnection diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index e974ff6..84bae91 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.5 (Arduino with Ethernet) + * Homebridge-MQTT 1.6 (Arduino with Ethernet) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -146,6 +146,7 @@ * Closed: "0" * * Release notes: + * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration diff --git a/examples/Arduino/KeybusReader/KeybusReader.ino b/examples/Arduino/KeybusReader/KeybusReader.ino index 7e94cb8..3f2e78c 100644 --- a/examples/Arduino/KeybusReader/KeybusReader.ino +++ b/examples/Arduino/KeybusReader/KeybusReader.ino @@ -1,11 +1,12 @@ /* - * DSC Keybus Reader 1.2 (Arduino) + * DSC Keybus Reader 1.3 (Arduino) * * Decodes and prints data from the Keybus to a serial interface, including reading from serial for the virtual * keypad. This is primarily to help decode the Keybus protocol - see the Status example to put the interface * to productive use. * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Handle spurious data while keybus is disconnected * Removed redundant data processing * 1.0 - Initial release diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index a21dcb0..d74b7a0 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -1,5 +1,5 @@ /* - * OpenHAB-MQTT 1.2 (Arduino with Ethernet) + * OpenHAB-MQTT 1.3 (Arduino with Ethernet) * * Processes the security system status and allows for control using OpenHAB. This uses MQTT to * interface with OpenHAB and the MQTT binding and demonstrates using panel and partition states @@ -81,6 +81,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * Fire alarm restored: "0" * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Added PGM outputs 1-14 status * Removed partition exit delay MQTT message, not used in this OpenHAB example * 1.0 - Initial release diff --git a/examples/Arduino/Status/Status.ino b/examples/Arduino/Status/Status.ino index 347ee47..57fb0f3 100644 --- a/examples/Arduino/Status/Status.ino +++ b/examples/Arduino/Status/Status.ino @@ -1,11 +1,12 @@ /* - * DSC Status 1.3 (Arduino) + * DSC Status 1.4 (Arduino) * * Processes and prints the security system status to a serial interface, including reading from serial for the * virtual keypad. This demonstrates how to determine if the security system status has changed, what has * changed, and how to take action based on those changes. * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Added PGM outputs 1-14 status * 1.1 - Added partition ready, access code, and timestamp status * 1.0 - Initial release @@ -112,7 +113,7 @@ void loop() { if (dsc.disabled[partition]) { Serial.print(F("Partition ")); Serial.print(partition + 1); - Serial.println(F(" disabled")); + Serial.println(F(": Disabled")); } } if (dsc.disabled[partition]) continue; diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index f3ceced..ea4267c 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -1,5 +1,5 @@ /* - * Email Notification 1.0 (esp32) + * Email Notification 1.1 (esp32) * * Processes the security system status and demonstrates how to send an email when the status has changed. Configure * the email SMTP server settings in sendEmail(). @@ -9,6 +9,7 @@ * apps: https://support.google.com/accounts/answer/6010255 * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 643e14b..6ea29a9 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -1,5 +1,5 @@ /* - * HomeAssistant-MQTT 1.4 (esp32) + * HomeAssistant-MQTT 1.5 (esp32) * * Processes the security system status and allows for control using Home Assistant via MQTT. * @@ -160,6 +160,7 @@ entity: alarm_control_panel.security_partition_1 * Closed: "0" * * Release notes: + * 1.5 - Added DSC Classic series support * 1.4 - Added PGM outputs 1-14 status * 1.0 - Initial release * diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index 8f58429..5075559 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.5 (esp32) + * Homebridge-MQTT 1.6 (esp32) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index b1da9ff..c91055f 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -1,5 +1,5 @@ /* - * Homey 1.0 (esp32) + * Homey 1.1 (esp32) * * Processes the security system status for partition 1 and allows for control using Athom Homey. * @@ -12,6 +12,7 @@ * Zone states are published by Homey.trigger command including the zone number. * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp32/KeybusReader/KeybusReader.ino b/examples/esp32/KeybusReader/KeybusReader.ino index d97d520..13d02ae 100644 --- a/examples/esp32/KeybusReader/KeybusReader.ino +++ b/examples/esp32/KeybusReader/KeybusReader.ino @@ -1,11 +1,12 @@ /* - * DSC Keybus Reader 1.2 (esp32) + * DSC Keybus Reader 1.3 (esp32) * * Decodes and prints data from the Keybus to a serial interface, including reading from serial for the virtual * keypad. This is primarily to help decode the Keybus protocol - see the Status example to put the interface * to productive use. * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Handle spurious data while keybus is disconnected * Removed redundant data processing * 1.0 - Initial release diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index 3614557..7ac0ad8 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -1,5 +1,5 @@ /* - * DSC Keybus Reader IP 1.2 (esp32) + * DSC Keybus Reader IP 1.3 (esp32) * * Decodes and prints data from the Keybus to a TCP connection including virtual keyboard over IP. This is * primarily to help decode the Keybus protocol - see the Status example to put the interface to productive use. @@ -9,6 +9,7 @@ * 2. For macOS/Linux: telnet dsc.local * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Updated to connect via telnet * Handle spurious data while keybus is disconnected * Removed redundant data processing diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index f0ea568..720a147 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -1,5 +1,5 @@ /* - * OpenHAB-MQTT 1.2 (esp32) + * OpenHAB-MQTT 1.3 (esp32) * * Processes the security system status and allows for control using OpenHAB. This uses MQTT to * interface with OpenHAB and the MQTT binding and demonstrates sending the panel status as a @@ -89,6 +89,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * Closed: "0" * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Added PGM outputs 1-14 status * 1.1 - Removed partition exit delay MQTT message, not used in this OpenHAB example * 1.0 - Initial release diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index 95849ba..f12d5dd 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -12,6 +12,7 @@ * * Release notes: * 1.5 - Update HTTPS root certificate for api.pushbullet.com + * Added DSC Classic series support * 1.4 - Add HTTPS certificate validation, add customizable message prefix * 1.0 - Initial release * diff --git a/examples/esp32/Status/Status.ino b/examples/esp32/Status/Status.ino index 49d4fc2..e1d1971 100644 --- a/examples/esp32/Status/Status.ino +++ b/examples/esp32/Status/Status.ino @@ -1,11 +1,12 @@ /* - * DSC Status 1.3 (esp32) + * DSC Status 1.4 (esp32) * * Processes and prints the security system status to a serial interface, including reading from serial for the * virtual keypad. This demonstrates how to determine if the security system status has changed, what has * changed, and how to take action based on those changes. * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Added PGM outputs 1-14 status * 1.0 - Initial release * @@ -111,7 +112,7 @@ void loop() { if (dsc.disabled[partition]) { Serial.print(F("Partition ")); Serial.print(partition + 1); - Serial.println(F(" disabled")); + Serial.println(F(": Disabled")); } } if (dsc.disabled[partition]) continue; diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 92badef..35a62e4 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -1,5 +1,5 @@ /* - * Telegram Bot 1.0 (esp32) + * Telegram Bot 1.1 (esp32) * * Processes the security system status and allows for control via a Telegram bot: https://www.telegram.org * @@ -27,6 +27,7 @@ * - Disarm: /disarm * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino index ed7c090..4fe6259 100644 --- a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino @@ -54,7 +54,6 @@ const byte timePartition = 1; // Set the partition to use for setting // Configures the Keybus interface with the specified pins. #define dscClockPin 18 // 4,13,16-39 #define dscReadPin 19 // 4,13,16-39 -#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 #define dscWritePin 21 // 4,13,16-33 // Initialize components diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index bacc67d..f7f9af9 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -1,5 +1,5 @@ /* - * TinyGSM SMS Notification 1.0 (esp32) + * TinyGSM SMS Notification 1.1 (esp32) * * Processes the security system status and demonstrates how to send an SMS text message when the status has * changed. This example sends SMS text messages via a TinyGSM-compatible module which can be integrated @@ -11,6 +11,7 @@ * 2. Set the destination phone numbers in the sketch settings. * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 7907fd3..eeab906 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -1,10 +1,11 @@ /* - * Twilio SMS Notification 1.0 (esp32) + * Twilio SMS Notification 1.1 (esp32) * * Processes the security system status and demonstrates how to send an SMS text message when the status has * changed. This example sends SMS text messages via Twilio: https://www.twilio.com * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index e80d9c6..34d10b8 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -1,12 +1,21 @@ /* - * VirtualKeypad-Blynk 1.3 (esp32) + * VirtualKeypad-Blynk 1.4 (esp32) * - * Provides a virtual keypad interface for the free Blynk (https://www.blynk.cc) app on iOS and Android, similar - * to a physical DSC LED keypad. Note that while the Blynk app has an LCD to display the partition status, the - * sketch currently does not emulate the menu navigation features of the DSC LCD keypads (PK5500, etc). + * Provides a virtual keypad interface for the free Blynk legacy (https://www.blynk.cc) app on iOS and Android, similar + * to a physical DSC LED keypad (the newer Blynk.Cloud app is not currently supported): + * + * iOS: https://apps.apple.com/us/app/blynk-0-1-legacy/id808760481 + * Android: https://play.google.com/store/apps/details?id=cc.blynk&hl=en&gl=US + * + * Installing Blynk as a local server (https://github.com/blynkkk/blynk-server) is recommended to keep control of the + * security system internal to your network. This also lets you use as many widgets as needed for free - local + * servers can setup users with any amount of Blynk Energy. + * + * Note that while the Blynk legacy app has an LCD to display the partition status, the sketch currently does + * not emulate the menu navigation features of the DSC LCD keypads (PK5500, etc). * * Usage: - * 1. Scan one of the following QR codes from within the Blynk app for an example keypad layout - as QR codes + * 1. Scan one of the following QR codes from within the Blynk legacy app for an example keypad layout - as QR codes * can contain a limited amount of objects, only the 8 and 16-zone template includes PGM outputs 1-8. Use * cloning within the Blynk app to add up to 64 zones and up to 14 PGM outputs. Some Android devices have * issues reading these QR codes and may need to be used with a different monitor/device. @@ -21,11 +30,6 @@ * 5. Add the auth token to the sketch below. * 6. Upload the sketch. * - * Installing Blynk as a local server (https://github.com/blynkkk/blynk-server) is recommended to keep control of the - * security system internal to your network. This also lets you use as many widgets as needed for free - local - * servers can setup users with any amount of Blynk Energy. Using the default Blynk cloud service with the above - * example layouts requires more of Blynk's Energy units than available on the free usage tier. - * * The Blynk layout can be customized with widgets using these virtual pin mappings: * V0 - Keypad 0 ... V9 - Keypad 9 V10 - Keypad @@ -53,6 +57,7 @@ V61 - Zone 1 ... V124 - Zone 64 * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Display alarm memory, programming zone lights, and event buffer * Add PGM outputs 1-14 status * 1.0 - Initial release diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 551bd87..7cf085b 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -1,5 +1,5 @@ /* - * VirtualKeypad-Web 1.4 (esp32) + * VirtualKeypad-Web 1.5 (esp32) * * Provides a virtual keypad web interface using the esp32 as a standalone web server, including * alarm memory, programming zone lights, and viewing the event buffer. To access the event buffer, @@ -7,8 +7,10 @@ * * Usage: * 1. Install the following libraries directly from each Github repository: - * ESPAsyncWebServer: https://github.com/me-no-dev/ESPAsyncWebServer * AsyncTCP: https://github.com/me-no-dev/AsyncTCP + * ESPAsyncWebServer: https://github.com/arjenhiemstra/ESPAsyncWebServer + * * This fork of the original ESPAsyncWebServer fixes the web server crashing + * when used with recent versions of Safari on macOS and iOS * * 2. Install the Arduino ESP32 filesystem uploader to enable uploading web server files: * https://github.com/me-no-dev/arduino-esp32fs-plugin @@ -29,6 +31,8 @@ * the serial output or http://dsc.local (for clients and networks that support mDNS). * * Release notes: + * 1.5 - Added DSC Classic series support + * Changed ESPAsyncWebServer to a newer fork to fix web server crashes with Safari * 1.4 - Fix crash when pressing keys while Keybus is disconnected * 1.0 - Initial release * diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index 5765be9..a0d6761 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -1,5 +1,5 @@ /* - * Email Notification 1.3 (esp8266) + * Email Notification 1.4 (esp8266) * * Processes the security system status and demonstrates how to send an email when the status has changed. Configure * the email SMTP server settings in sendMessage(). @@ -9,6 +9,7 @@ * apps: https://support.google.com/accounts/answer/6010255 * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Check if WiFi disconnects and wait to send updates until reconnection * Add appendPartition() to simplify sketch diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 7e5ae6f..d4fdc5a 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -1,5 +1,5 @@ /* - * HomeAssistant-MQTT 1.4 (esp8266) + * HomeAssistant-MQTT 1.5 (esp8266) * * Processes the security system status and allows for control using Home Assistant via MQTT. * @@ -160,6 +160,8 @@ entity: alarm_control_panel.security_partition_1 * Closed: "0" * * Release notes: + * 1.5 - Added DSC Classic series support + * Fixed armed away with no entry delay status message * 1.4 - Added PGM outputs 1-14 status * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Added sensor component to display partition status messages @@ -555,7 +557,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x12: mqtt.publish(publishTopic, "Battery check in progress"); break; case 0x14: mqtt.publish(publishTopic, "Auto-arm in progress", true); break; case 0x15: mqtt.publish(publishTopic, "Arming with bypassed zones", true); break; - case 0x06: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; + case 0x16: mqtt.publish(publishTopic, "Armed: Away with no entry delay", true); break; case 0x17: mqtt.publish(publishTopic, "Power saving: Keypad blanked", true); break; case 0x19: mqtt.publish(publishTopic, "Disarmed: Alarm memory"); break; case 0x22: mqtt.publish(publishTopic, "Disarmed: Recent closing", true); break; diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index 5213a7f..e9bdcce 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.5 (esp8266) + * Homebridge-MQTT 1.6 (esp8266) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status * Added notes on Google Home integration diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index 1b4b1f0..4f904f7 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -1,5 +1,5 @@ /* - * Homey 1.2 (esp8266) + * Homey 1.3 (esp8266) * * Processes the security system status for partition 1 and allows for control using Athom Homey. * @@ -12,6 +12,7 @@ * Zone states are published by Homey.trigger command including the zone number. * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.1 - Added status update on WiFi reconnection * Removed writeReady check, moved into library diff --git a/examples/esp8266/KeybusReader/KeybusReader.ino b/examples/esp8266/KeybusReader/KeybusReader.ino index 32f4558..f87c69b 100644 --- a/examples/esp8266/KeybusReader/KeybusReader.ino +++ b/examples/esp8266/KeybusReader/KeybusReader.ino @@ -1,11 +1,12 @@ /* - * DSC Keybus Reader 1.2 (esp8266) + * DSC Keybus Reader 1.3 (esp8266) * * Decodes and prints data from the Keybus to a serial interface, including reading from serial for the virtual * keypad. This is primarily to help decode the Keybus protocol - see the Status example to put the interface * to productive use. * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Handle spurious data while keybus is disconnected * Removed redundant data processing * 1.1 - Updated esp8266 wiring diagram for 33k/10k resistors diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index d9fbc5c..1503e85 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -1,5 +1,5 @@ /* - * DSC Keybus Reader IP 1.2 (esp8266) + * DSC Keybus Reader IP 1.3 (esp8266) * * Decodes and prints data from the Keybus to a TCP connection including virtual keyboard over IP. This is * primarily to help decode the Keybus protocol - see the Status example to put the interface to productive use. @@ -9,6 +9,7 @@ * 2. For macOS/Linux: telnet dsc.local * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Updated to connect via telnet * Handle spurious data while keybus is disconnected * Removed redundant data processing diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index 8c57123..6bad788 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -1,5 +1,5 @@ /* - * OpenHAB-MQTT 1.2 (esp8266) + * OpenHAB-MQTT 1.3 (esp8266) * * Processes the security system status and allows for control using OpenHAB. This uses MQTT to * interface with OpenHAB and the MQTT binding and demonstrates sending the panel status as a @@ -89,6 +89,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} * Closed: "0" * * Release notes: + * 1.3 - Added DSC Classic series support * 1.2 - Added PGM outputs 1-14 status * 1.1 - Removed partition exit delay MQTT message, not used in this OpenHAB example * Updated esp8266 wiring diagram for 33k/10k resistors diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index dd74140..d72707c 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -12,6 +12,7 @@ * * Release notes: * 1.5 - Update HTTPS root certificate for api.pushbullet.com + * Added DSC Classic series support * 1.4 - Add HTTPS certificate validation, add customizable message prefix * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Check if WiFi disconnects and wait to send updates until reconnection diff --git a/examples/esp8266/Status/Status.ino b/examples/esp8266/Status/Status.ino index a7780f3..752a892 100644 --- a/examples/esp8266/Status/Status.ino +++ b/examples/esp8266/Status/Status.ino @@ -1,11 +1,12 @@ /* - * DSC Status 1.3 (esp8266) + * DSC Status 1.4 (esp8266) * * Processes and prints the security system status to a serial interface, including reading from serial for the * virtual keypad. This demonstrates how to determine if the security system status has changed, what has * changed, and how to take action based on those changes. * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Added PGM outputs 1-14 status * 1.2 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.1 - Added partition ready, access code, and timestamp status @@ -113,7 +114,7 @@ void loop() { if (dsc.disabled[partition]) { Serial.print(F("Partition ")); Serial.print(partition + 1); - Serial.println(F(" disabled")); + Serial.println(F(": Disabled")); } } if (dsc.disabled[partition]) continue; @@ -124,12 +125,12 @@ void loop() { if (dsc.ready[partition]) { Serial.print(F("Partition ")); Serial.print(partition + 1); - Serial.println(F(" ready")); + Serial.println(F(": Ready")); } else { Serial.print(F("Partition ")); Serial.print(partition + 1); - Serial.println(F(" not ready")); + Serial.println(F(": Not ready")); } } diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index ebcac0b..c79ac8d 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -1,5 +1,5 @@ /* - * Telegram Bot 1.0 (esp8266) + * Telegram Bot 1.1 (esp8266) * * Processes the security system status and allows for control via a Telegram bot: https://www.telegram.org * @@ -27,6 +27,7 @@ * - Disarm: /disarm * * Release notes: + * 1.1 - Added DSC Classic series support * 1.0 - Initial release * * Wiring: diff --git a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino index 00fb628..1ffced1 100644 --- a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino @@ -54,7 +54,6 @@ const byte timePartition = 1; // Set the partition to use for setting // Configures the Keybus interface with the specified pins. #define dscClockPin D1 // GPIO 5 #define dscReadPin D2 // GPIO 4 -#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 #define dscWritePin D8 // GPIO 15 // Initialize components diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index 59c5094..eaa2822 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -1,10 +1,11 @@ /* - * Twilio SMS Notification 1.3 (esp8266) + * Twilio SMS Notification 1.4 (esp8266) * * Processes the security system status and demonstrates how to send an SMS text message when the status has * changed. This example sends SMS text messages via Twilio: https://www.twilio.com * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Check if WiFi disconnects and wait to send updates until reconnection * Add appendPartition() to simplify sketch diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 0f339de..ccad2bb 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -1,12 +1,21 @@ /* - * VirtualKeypad-Blynk 1.3 (esp8266) + * VirtualKeypad-Blynk 1.4 (esp8266) * - * Provides a virtual keypad interface for the free Blynk (https://www.blynk.cc) app on iOS and Android, similar - * to a physical DSC LED keypad. Note that while the Blynk app has an LCD to display the partition status, the - * sketch currently does not emulate the menu navigation features of the DSC LCD keypads (PK5500, etc). + * Provides a virtual keypad interface for the free Blynk legacy (https://www.blynk.cc) app on iOS and Android, similar + * to a physical DSC LED keypad (the newer Blynk.Cloud app is not currently supported): + * + * iOS: https://apps.apple.com/us/app/blynk-0-1-legacy/id808760481 + * Android: https://play.google.com/store/apps/details?id=cc.blynk&hl=en&gl=US + * + * Installing Blynk as a local server (https://github.com/blynkkk/blynk-server) is recommended to keep control of the + * security system internal to your network. This also lets you use as many widgets as needed for free - local + * servers can setup users with any amount of Blynk Energy. + * + * Note that while the Blynk legacy app has an LCD to display the partition status, the sketch currently does + * not emulate the menu navigation features of the DSC LCD keypads (PK5500, etc). * * Usage: - * 1. Scan one of the following QR codes from within the Blynk app for an example keypad layout - as QR codes + * 1. Scan one of the following QR codes from within the Blynk legacy app for an example keypad layout - as QR codes * can contain a limited amount of objects, only the 8 and 16-zone template includes PGM outputs 1-8. Use * cloning within the Blynk app to add up to 64 zones and up to 14 PGM outputs. Some Android devices have * issues reading these QR codes and may need to be used with a different monitor/device. @@ -21,11 +30,6 @@ * 5. Add the auth token to the sketch below. * 6. Upload the sketch. * - * Installing Blynk as a local server (https://github.com/blynkkk/blynk-server) is recommended to keep control of the - * security system internal to your network. This also lets you use as many widgets as needed for free - local - * servers can setup users with any amount of Blynk Energy. Using the default Blynk cloud service with the above - * example layouts requires more of Blynk's Energy units than available on the free usage tier. - * * The Blynk layout can be customized with widgets using these virtual pin mappings: V0 - Keypad 0 ... V9 - Keypad 9 V10 - Keypad @@ -53,6 +57,7 @@ V61 - Zone 1 ... V124 - Zone 64 * * Release notes: + * 1.4 - Added DSC Classic series support * 1.3 - Display alarm memory, programming zone lights, and event buffer * Add PGM outputs 1-14 status * 1.2 - Updated esp8266 wiring diagram for 33k/10k resistors diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 87274a7..e204b51 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -1,5 +1,5 @@ /* - * VirtualKeypad-Web 1.4 (esp8266) + * VirtualKeypad-Web 1.5 (esp8266) * * Provides a virtual keypad web interface using the esp8266 as a standalone web server, including * alarm memory, programming zone lights, and viewing the event buffer. To access the event buffer, @@ -7,8 +7,10 @@ * * Usage: * 1. Install the following libraries directly from each Github repository: - * ESPAsyncWebServer: https://github.com/me-no-dev/ESPAsyncWebServer * ESPAsyncTCP: https://github.com/me-no-dev/ESPAsyncTCP + * ESPAsyncWebServer: https://github.com/arjenhiemstra/ESPAsyncWebServer + * * This fork of the original ESPAsyncWebServer fixes the web server crashing + * when used with recent versions of Safari on macOS and iOS * * 2. Install ESP8266FS to enable uploading web server files to the esp8266: * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#uploading-files-to-file-system @@ -21,8 +23,8 @@ * 4. Set the WiFi SSID and password in the sketch. * 5. If desired, update the DNS hostname in the sketch. By default, this is set to * "dsc" and the web interface will be accessible at: http://dsc.local - * 6. Set the esp8266 flash size to use 1M SPIFFS. - * Arduino IDE: Tools > Flash Size > 4M (1M SPIFFS) + * 6. Set the esp8266 flash size to use at least 1MB for the filesystem. + * Arduino IDE: Tools > Flash Size > 4MB (FS:1MB ...) * 7. Upload the sketch. * 8. Upload the SPIFFS data containing the web server files: * Arduino IDE: Tools > ESP8266 Sketch Data Upload @@ -30,6 +32,8 @@ * the serial output or http://dsc.local (for clients and networks that support mDNS). * * Release notes: + * 1.5 - Added DSC Classic series support + * Changed ESPAsyncWebServer to a newer fork to fix web server crashes with Safari * 1.4 - Fix crash when pressing keys while Keybus is disconnected * 1.3 - Add event buffer display * Display zone lights in alarm memory and programming diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index d23d991..7b3b5e5 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -985,12 +985,14 @@ void dscKeybusInterface::printPanelStatus5(byte panelByte) { * from multiple sets of status messages, split into printPanelStatus4...printPanelStatus1B. */ void dscKeybusInterface::printPanelStatus14(byte panelByte) { + #if !defined(__AVR__) // Excludes Arduino/AVR to conserve storage space switch (panelData[panelByte]) { case 0xC0: stream->print(F("TLink com fault")); return; case 0xC2: stream->print(F("Tlink network fault")); return; case 0xC4: stream->print(F("TLink receiver trouble")); return; case 0xC5: stream->print(F("TLink receiver restored")); return; } + #endif printUnknownData(); } From c73a70b219766316cc4479973284eff49b467d17 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 27 Jan 2022 17:27:35 +0300 Subject: [PATCH 32/49] Fix esp32 Email example sketch messages --- examples/esp32/Email/Email.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index ea4267c..e96dbcd 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -131,12 +131,12 @@ void loop() { if (dsc.alarm[partition]) { char messageContent[19] = "Alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent, messageContent); + sendMessage(messageContent); } else { char messageContent[34] = "Disarmed after alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent, messageContent); + sendMessage(messageContent); } } @@ -146,12 +146,12 @@ void loop() { if (dsc.fire[partition]) { char messageContent[24] = "Fire alarm: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent, messageContent); + sendMessage(messageContent); } else { char messageContent[33] = "Fire alarm restored: Partition "; appendPartition(partition, messageContent); // Appends the message with the partition number - sendMessage(messageContent, messageContent); + sendMessage(messageContent); } } } From 532a5d880019d21fe85a93af1c262f15294c06df Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 27 Jan 2022 18:40:31 +0300 Subject: [PATCH 33/49] Upstream ESP32 IDF 4+ timer issue fixed, revert workaround --- src/dscClassic.cpp | 30 +++--------------------------- src/dscClassic.h | 2 -- src/dscClassicKeypad.cpp | 22 +--------------------- src/dscClassicKeypad.h | 2 -- src/dscKeybus.h | 2 -- src/dscKeybusInterface.cpp | 28 ++-------------------------- src/dscKeybusInterface.h | 3 --- src/dscKeypad.cpp | 22 +--------------------- src/dscKeypad.h | 2 -- 9 files changed, 7 insertions(+), 106 deletions(-) diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp index f07573d..7cf9fc4 100644 --- a/src/dscClassic.cpp +++ b/src/dscClassic.cpp @@ -21,16 +21,8 @@ #if defined(ESP32) portMUX_TYPE dscClassicInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; - -#if ESP_IDF_VERSION_MAJOR < 4 hw_timer_t * dscClassicInterface::timer1 = NULL; - -#else // ESP-IDF 4+ -esp_timer_handle_t timer1; -const esp_timer_create_args_t timer1Parameters = { .callback = reinterpret_cast(&dscClassicInterface::dscDataInterrupt) }; - -#endif // ESP_IDF_VERSION_MAJOR -#endif // ESP32 +#endif dscClassicInterface::dscClassicInterface(byte setClockPin, byte setReadPin, byte setPC16Pin, byte setWritePin, const char * setAccessCode) { dscClockPin = setClockPin; @@ -72,16 +64,12 @@ void dscClassicInterface::begin(Stream &_stream) { // esp32 timer1 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timer1 = timerBegin(1, 80, true); timerStop(timer1); timerAttachInterrupt(timer1, &dscDataInterrupt, true); timerAlarmWrite(timer1, 250, true); timerAlarmEnable(timer1); - #else // IDF4+ - esp_timer_create(&timer1Parameters, &timer1); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif // Generates an interrupt when the Keybus clock rises or falls - requires a hardware interrupt pin on Arduino/AVR attachInterrupt(digitalPinToInterrupt(dscClockPin), dscClockInterrupt, CHANGE); @@ -101,13 +89,9 @@ void dscClassicInterface::stop() { // Disables esp32 timer1 #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerAlarmDisable(timer1); timerEnd(timer1); - #else // ESP-IDF 4+ - esp_timer_stop(timer1); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif // Disables the Keybus clock pin interrupt detachInterrupt(digitalPinToInterrupt(dscClockPin)); @@ -1038,11 +1022,7 @@ void IRAM_ATTR dscClassicInterface::dscClockInterrupt() { // esp32 timer1 calls dscDataInterrupt() in 250us #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer1); - #else // IDF4+ - esp_timer_start_periodic(timer1, 250); - #endif portENTER_CRITICAL(&timer1Mux); #endif @@ -1095,11 +1075,7 @@ void dscClassicInterface::dscDataInterrupt() { void ICACHE_RAM_ATTR dscClassicInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscClassicInterface::dscDataInterrupt() { - #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer1); - #else // IDF 4+ - esp_timer_stop(timer1); - #endif portENTER_CRITICAL(&timer1Mux); #endif diff --git a/src/dscClassic.h b/src/dscClassic.h index 9a8025c..2c4c5bb 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -159,9 +159,7 @@ class dscClassicInterface { static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); #if defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer1; - #endif static portMUX_TYPE timer1Mux; #endif diff --git a/src/dscClassicKeypad.cpp b/src/dscClassicKeypad.cpp index a35a159..d2b8d66 100644 --- a/src/dscClassicKeypad.cpp +++ b/src/dscClassicKeypad.cpp @@ -21,15 +21,7 @@ #if defined(ESP32) portMUX_TYPE dscClassicKeypadInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; - -#if ESP_IDF_VERSION_MAJOR < 4 hw_timer_t * dscClassicKeypadInterface::timer1 = NULL; - -#else // ESP-IDF 4+ -esp_timer_handle_t timer3; -const esp_timer_create_args_t timer3Parameters = { .callback = reinterpret_cast(&dscClassicKeypadInterface::dscClockInterrupt) }; - -#endif // ESP_IDF_VERSION_MAJOR #endif // ESP32 @@ -70,16 +62,12 @@ void dscClassicKeypadInterface::begin(Stream &_stream) { // esp32 timer1 calls dscClockInterrupt() #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timer1 = timerBegin(1, 80, true); timerStop(timer1); timerAttachInterrupt(timer1, &dscClockInterrupt, true); timerAlarmWrite(timer1, 1000, true); timerAlarmEnable(timer1); - #else // IDF4+ - esp_timer_create(&timer3Parameters, &timer3); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif intervalStart = millis(); @@ -137,11 +125,7 @@ bool dscClassicKeypadInterface::loop() { #elif defined(ESP8266) timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer1); - #else // IDF4+ - esp_timer_start_periodic(timer3, 1000); - #endif // ESP_IDF_VERSION_MAJOR #endif } else if (!commandReady) intervalStart = millis(); @@ -397,11 +381,7 @@ void IRAM_ATTR dscClassicKeypadInterface::dscClockInterrupt() { #elif defined(ESP8266) timer1_disable(); #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer1); - #else // IDF4+ - esp_timer_stop(timer3); - #endif // ESP_IDF_VERSION_MAJOR #endif } diff --git a/src/dscClassicKeypad.h b/src/dscClassicKeypad.h index 640069f..47e2d8c 100644 --- a/src/dscClassicKeypad.h +++ b/src/dscClassicKeypad.h @@ -82,9 +82,7 @@ class dscClassicKeypadInterface { bool keyBeep, beepStart; #if defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer1; - #endif static portMUX_TYPE timer1Mux; #endif diff --git a/src/dscKeybus.h b/src/dscKeybus.h index bdebcee..c008d4a 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -295,9 +295,7 @@ class dscKeybusInterface { static bool redundantPanelData(byte previousCmd[], volatile byte currentCmd[], byte checkedBytes = dscReadSize); #if defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer1; - #endif static portMUX_TYPE timer1Mux; #endif diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 67491ce..7ed739c 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -22,15 +22,7 @@ #if defined(ESP32) portMUX_TYPE dscKeybusInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; - -#if ESP_IDF_VERSION_MAJOR < 4 hw_timer_t * dscKeybusInterface::timer1 = NULL; - -#else // ESP-IDF 4+ -esp_timer_handle_t timer0; -const esp_timer_create_args_t timer0Parameters = { .callback = reinterpret_cast(&dscKeybusInterface::dscDataInterrupt) }; - -#endif // ESP_IDF_VERSION_MAJOR #endif // ESP32 @@ -70,16 +62,12 @@ void dscKeybusInterface::begin(Stream &_stream) { // esp32 timer1 calls dscDataInterrupt() from dscClockInterrupt() #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timer1 = timerBegin(1, 80, true); timerStop(timer1); timerAttachInterrupt(timer1, &dscDataInterrupt, true); timerAlarmWrite(timer1, 250, true); timerAlarmEnable(timer1); - #else // IDF4+ - esp_timer_create(&timer0Parameters, &timer0); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif // Generates an interrupt when the Keybus clock rises or falls - requires a hardware interrupt pin on Arduino/AVR attachInterrupt(digitalPinToInterrupt(dscClockPin), dscClockInterrupt, CHANGE); @@ -99,13 +87,9 @@ void dscKeybusInterface::stop() { // Disables esp32 timer1 #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerAlarmDisable(timer1); timerEnd(timer1); - #else // ESP-IDF 4+ - esp_timer_stop(timer0); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif // Disables the Keybus clock pin interrupt detachInterrupt(digitalPinToInterrupt(dscClockPin)); @@ -644,11 +628,7 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { // esp32 timer1 calls dscDataInterrupt() in 250us #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer1); - #else // IDF4+ - esp_timer_start_periodic(timer0, 250); - #endif portENTER_CRITICAL(&timer1Mux); #endif @@ -794,11 +774,7 @@ void dscKeybusInterface::dscDataInterrupt() { void ICACHE_RAM_ATTR dscKeybusInterface::dscDataInterrupt() { #elif defined(ESP32) void IRAM_ATTR dscKeybusInterface::dscDataInterrupt() { - #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer1); - #else // IDF 4+ - esp_timer_stop(timer0); - #endif portENTER_CRITICAL(&timer1Mux); #endif diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index 166bb8b..564a1a1 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -20,9 +20,6 @@ #ifndef dscKeybusInterface_h #define dscKeybusInterface_h -#if defined ESP32 && ESP_IDF_VERSION_MAJOR >= 4 -#include "esp_timer.h" -#endif // DSC Classic Series #if defined dscClassicSeries diff --git a/src/dscKeypad.cpp b/src/dscKeypad.cpp index b8aba39..cd946a0 100644 --- a/src/dscKeypad.cpp +++ b/src/dscKeypad.cpp @@ -23,15 +23,7 @@ #if defined(ESP32) portMUX_TYPE dscKeypadInterface::timer1Mux = portMUX_INITIALIZER_UNLOCKED; - -#if ESP_IDF_VERSION_MAJOR < 4 hw_timer_t * dscKeypadInterface::timer1 = NULL; - -#else // ESP-IDF 4+ -esp_timer_handle_t timer2; -const esp_timer_create_args_t timer2Parameters = { .callback = reinterpret_cast(&dscKeypadInterface::dscClockInterrupt) }; - -#endif // ESP_IDF_VERSION_MAJOR #endif // ESP32 @@ -70,16 +62,12 @@ void dscKeypadInterface::begin(Stream &_stream) { // esp32 timer1 calls dscClockInterrupt() #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timer1 = timerBegin(1, 80, true); timerStop(timer1); timerAttachInterrupt(timer1, &dscClockInterrupt, true); timerAlarmWrite(timer1, 500, true); timerAlarmEnable(timer1); - #else // IDF4+ - esp_timer_create(&timer2Parameters, &timer2); - #endif // ESP_IDF_VERSION_MAJOR - #endif // ESP32 + #endif intervalStart = millis(); @@ -239,11 +227,7 @@ bool dscKeypadInterface::loop() { #elif defined(ESP8266) timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStart(timer1); - #else // IDF4+ - esp_timer_start_periodic(timer2, 500); - #endif // ESP_IDF_VERSION_MAJOR #endif } else if (!commandReady) intervalStart = millis(); @@ -554,11 +538,7 @@ void IRAM_ATTR dscKeypadInterface::dscClockInterrupt() { #elif defined(ESP8266) timer1_disable(); #elif defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 timerStop(timer1); - #else // IDF4+ - esp_timer_stop(timer2); - #endif // ESP_IDF_VERSION_MAJOR #endif } diff --git a/src/dscKeypad.h b/src/dscKeypad.h index 7983baf..6f51b1f 100644 --- a/src/dscKeypad.h +++ b/src/dscKeypad.h @@ -92,9 +92,7 @@ class dscKeypadInterface { unsigned long intervalStart; #if defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 4 static hw_timer_t * timer1; - #endif static portMUX_TYPE timer1Mux; #endif From 8dfa5802c5bd3b94ddb8bf1c825180e76688c5f2 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Mon, 31 Jan 2022 23:30:52 +0300 Subject: [PATCH 34/49] Workaround for upstream esp32 TLS handshake issue, fix esp32 OpenHAB-MQTT compilation error, fix example sketch armed message array size, set example sketches using TLS to use NTP and root certificates, embed example sketch base64 encoding, remove deprecated code --- README.md | 2 + .../KeypadInterface-MQTT.ino | 2 +- .../KeypadInterface/KeypadInterface.ino | 2 +- examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino | 4 +- examples/Arduino/Unlocker/Unlocker.ino | 2 +- examples/esp32/Email/Email.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- examples/esp32/Homey/Homey.ino | 3 +- .../esp32/KeybusReaderIP/KeybusReaderIP.ino | 2 +- .../KeypadInterface-MQTT.ino | 4 +- .../esp32/KeypadInterface/KeypadInterface.ino | 2 +- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 7 +- examples/esp32/Pushbullet/Pushbullet.ino | 7 +- examples/esp32/Telegram/Telegram.ino | 7 +- examples/esp32/TimeSyncNTP/TimeSyncNTP.ino | 4 +- examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 4 +- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 56 ++++++- examples/esp32/Unlocker/Unlocker.ino | 2 +- .../VirtualKeypad-Blynk.ino | 4 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 6 +- examples/esp8266/Email/Email.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- .../Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- examples/esp8266/Homey/Homey.ino | 2 +- .../esp8266/KeybusReaderIP/KeybusReaderIP.ino | 2 +- .../KeypadInterface-MQTT.ino | 4 +- .../KeypadInterface/KeypadInterface.ino | 2 +- .../esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/esp8266/Pushbullet/Pushbullet.ino | 6 +- examples/esp8266/Telegram/Telegram.ino | 6 +- examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino | 4 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 58 ++++++- examples/esp8266/Unlocker/Unlocker.ino | 2 +- .../VirtualKeypad-Blynk.ino | 4 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 6 +- src/dscKeybus.h | 1 - src/dscKeybusInterface.cpp | 154 ------------------ 38 files changed, 164 insertions(+), 221 deletions(-) diff --git a/README.md b/README.md index 280faa1..861dcdb 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,9 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed + - Updated: Added TLS root certificate to `Twilio-SMS` - Bugfix: `Pushbullet` example sketch updated TLS security certificate fingerprint + - Bugfix: Workaround for [upstream esp32 TLS handshake issue](https://github.com/espressif/arduino-esp32/issues/6165) * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index a969d7b..7478b9d 100644 --- a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -119,7 +119,7 @@ void setup() { if (mqttConnect()) mqttPreviousTime = millis(); else mqttPreviousTime = 0; - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 44ce431..6e4cb24 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -88,7 +88,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino index d14ed13..c23f60a 100644 --- a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino @@ -80,7 +80,7 @@ void setup() { Serial.println(); // Initializes ethernet with DHCP - Serial.print(F("Ethernet...")); + Serial.print(F("Ethernet....")); while(!Ethernet.begin(mac)) { Serial.print("."); delay(1000); @@ -89,7 +89,7 @@ void setup() { Serial.println(Ethernet.localIP()); ipClient.begin(localPort); - Serial.print(F("NTP time...")); + Serial.print(F("NTP time....")); setSyncProvider(getDstCorrectedTime); // Initiates the NTP client, synced hourly setSyncInterval(3600); while (timeStatus() != timeSet) { diff --git a/examples/Arduino/Unlocker/Unlocker.ino b/examples/Arduino/Unlocker/Unlocker.ino index b08dcec..d705420 100644 --- a/examples/Arduino/Unlocker/Unlocker.ino +++ b/examples/Arduino/Unlocker/Unlocker.ino @@ -128,7 +128,7 @@ void setup() { // Starts the Keybus interface and optionally specifies how to print data. // begin() sets Serial by default and can accept a different stream: begin(Serial1), etc. dsc.begin(); - Serial.print(F("DSC Keybus Interface...")); + Serial.print(F("DSC Keybus Interface....")); // Loops until partition 1 is ready for key presses in status "Partition ready" (0x01), // "Stay/away zones open" (0x02), or "Zones open" (0x03) diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index e96dbcd..66534e9 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -70,7 +70,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 6ea29a9..007fcd5 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -253,7 +253,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index 5075559..014e8c5 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -238,7 +238,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index c91055f..a81f1de 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -87,7 +87,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -134,7 +134,6 @@ void loop() { // Run the Homey loop Homey.loop(); - dsc.loop(); if (dsc.statusChanged) { // Checks if the security system status has changed diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index 7ac0ad8..3edbbd9 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -89,7 +89,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 098ab0c..49782e4 100644 --- a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -106,7 +106,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -120,7 +120,7 @@ void setup() { if (mqttConnect()) mqttPreviousTime = millis(); else mqttPreviousTime = 0; - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index ad10a88..f557e7c 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -88,7 +88,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index 720a147..fb9b748 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -183,7 +183,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -271,7 +271,7 @@ void loop() { dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag // Disarmed during exit delay - else if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { + if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { publishState(mqttPartitionTopic, partition, "D"); } } @@ -486,7 +486,7 @@ void publishMessage(const char* sourceTopic, byte partition) { case 0x10: mqtt.publish(publishTopic, "Keypad lockout", true); break; case 0x11: mqtt.publish(publishTopic, "Alarm", true); break; case 0x14: mqtt.publish(publishTopic, "Auto-arm", true); break; - case 0x15: mqtt.publish(publishTopic, "Arm with bypass"); break; + case 0x15: mqtt.publish(publishTopic, "Arm with bypass", true); break; case 0x16: mqtt.publish(publishTopic, "No entry delay", true); break; case 0x22: mqtt.publish(publishTopic, "Alarm memory", true); break; case 0x33: mqtt.publish(publishTopic, "Busy", true); break; @@ -542,4 +542,3 @@ void publishMessage(const char* sourceTopic, byte partition) { default: return; } } - diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index f12d5dd..70df6fa 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -111,7 +111,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); ipClient.setCACert(pushbulletCertificateRoot); @@ -122,7 +122,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time...")); + Serial.print(F("NTP time....")); configTime(0, 0, "pool.ntp.org"); time_t now = time(nullptr); while (now < 24 * 3600) @@ -187,7 +187,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); @@ -328,6 +328,7 @@ void loop() { bool sendMessage(const char* pushMessage) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 // Connects and sends the message as JSON if (!ipClient.connect("api.pushbullet.com", 443)) return false; diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 35a62e4..839910e 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -106,7 +106,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); ipClient.setCACert(TELEGRAM_CERTIFICATE_ROOT); @@ -117,7 +117,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time...")); + Serial.print(F("NTP time....")); configTime(0, 0, "pool.ntp.org"); time_t now = time(nullptr); while (now < 24 * 3600) @@ -199,7 +199,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); @@ -388,6 +388,7 @@ void handleTelegram(byte telegramMessages) { bool sendMessage(const char* messageContent) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 byte messageLength = strlen(messagePrefix) + strlen(messageContent) + 1; char message[messageLength]; strcpy(message, messagePrefix); diff --git a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino index 4fe6259..fb7e434 100644 --- a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino @@ -71,7 +71,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -81,7 +81,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time...")); + Serial.print(F("NTP time....")); configTime(timeZoneOffset, daylightOffset, ntpServer); // Initiates the NTP client, synced hourly while (!getLocalTime(&ntpTime)) { Serial.print("."); diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index f7f9af9..6f90fc4 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -102,13 +102,13 @@ void setup() { Serial1.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX); while (!modem.isNetworkConnected()) { - Serial.print(F("GSM...")); + Serial.print(F("GSM....")); while (!modem.restart()) { Serial.print("."); } Serial.println(); - Serial.print(F("Waiting for network...")); + Serial.print(F("Waiting for network....")); if (modem.waitForNetwork(600000L) && modem.isNetworkConnected()) { Serial.println(F("connected.")); } diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index eeab906..893cd25 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -5,6 +5,8 @@ * changed. This example sends SMS text messages via Twilio: https://www.twilio.com * * Release notes: + * 1.2 - Add TLS root certificate for Twilio + * Encode authorization data in base64 directly within the sketch * 1.1 - Added DSC Classic series support * 1.0 - Initial release * @@ -42,13 +44,13 @@ #include #include +#include "mbedtls/base64.h" // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard -const char* Base64EncodedAuth = ""; // macOS/Linux terminal: $ echo -n "AccountSID:AuthToken" | base64 -w 0 const char* From = ""; // i.e. 16041234567 const char* To = ""; // i.e. 16041234567 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages @@ -58,6 +60,32 @@ const char* messagePrefix = "[Security system] "; // Set a prefix for all messa #define dscReadPin 19 // 4,13,16-39 #define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +// HTTPS root certificate for api.twilio.com: DigiCert Global Root CA, expires 2031.11.10 +const char twilioCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +)=EOF="; + // Initialize components #ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); @@ -66,6 +94,9 @@ dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); #endif WiFiClientSecure ipClient; bool wifiConnected = true; +char twilioAuth[128]; +size_t twilioAuthLength = 128; +unsigned char encodedTwilioAuth[128]; void setup() { @@ -74,9 +105,10 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); + ipClient.setCACert(twilioCertificateRoot); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); @@ -84,7 +116,22 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + // Sends a message on startup to verify connectivity + strcat(twilioAuth, AccountSID); + strcat(twilioAuth, ":"); + strcat(twilioAuth, AuthToken); + mbedtls_base64_encode(encodedTwilioAuth,twilioAuthLength,&twilioAuthLength,(unsigned char*)twilioAuth, strlen(twilioAuth)); Serial.print(F("Twilio....")); if (sendMessage("Initializing")) Serial.println(F("connected.")); else Serial.println(F("connection error.")); @@ -138,7 +185,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); @@ -279,6 +326,7 @@ void loop() { bool sendMessage(const char* messageContent) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 // Connects and sends the message as x-www-form-urlencoded if (!ipClient.connect("api.twilio.com", 443)) return false; @@ -286,7 +334,7 @@ bool sendMessage(const char* messageContent) { ipClient.print(AccountSID); ipClient.println(F("/Messages.json HTTP/1.1")); ipClient.print(F("Authorization: Basic ")); - ipClient.println(Base64EncodedAuth); + ipClient.println((char*)encodedTwilioAuth); ipClient.println(F("Host: api.twilio.com")); ipClient.println(F("User-Agent: ESP32")); ipClient.println(F("Accept: */*")); diff --git a/examples/esp32/Unlocker/Unlocker.ino b/examples/esp32/Unlocker/Unlocker.ino index c1ed6ca..c382a3f 100644 --- a/examples/esp32/Unlocker/Unlocker.ino +++ b/examples/esp32/Unlocker/Unlocker.ino @@ -515,7 +515,7 @@ void setup() { // Starts the Keybus interface and optionally specifies how to print data. // begin() sets Serial by default and can accept a different stream: begin(Serial1), etc. dsc.begin(); - Serial.print(F("DSC Keybus Interface...")); + Serial.print(F("DSC Keybus Interface....")); // Loops until partition 1 is ready for key presses in status "Partition ready" (0x01), // "Stay/away zones open" (0x02), or "Zones open" (0x03) diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 34d10b8..45dfd86 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -231,13 +231,13 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); Blynk.begin(blynkAuthToken, wifiSSID, wifiPassword, blynkServer, blynkPort); while (WiFi.status() != WL_CONNECTED) yield(); Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("Blynk...")); + Serial.print(F("Blynk....")); while (!Blynk.connected()) { Blynk.run(); yield(); diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 7cf085b..403d703 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -9,8 +9,8 @@ * 1. Install the following libraries directly from each Github repository: * AsyncTCP: https://github.com/me-no-dev/AsyncTCP * ESPAsyncWebServer: https://github.com/arjenhiemstra/ESPAsyncWebServer - * * This fork of the original ESPAsyncWebServer fixes the web server crashing - * when used with recent versions of Safari on macOS and iOS + * * This is a fork of the original ESPAsyncWebServer that fixes the web server crashing + * when used with recent versions of Safari on macOS and iOS. * * 2. Install the Arduino ESP32 filesystem uploader to enable uploading web server files: * https://github.com/me-no-dev/arduino-esp32fs-plugin @@ -122,7 +122,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index a0d6761..6a52bd0 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -75,7 +75,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index d4fdc5a..bf7af77 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -262,7 +262,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index e9bdcce..f944a7f 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -244,7 +244,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index 4f904f7..2cf60a5 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -90,7 +90,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index 1503e85..ed92d8d 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -90,7 +90,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 3284930..92571a5 100644 --- a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -106,7 +106,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -120,7 +120,7 @@ void setup() { if (mqttConnect()) mqttPreviousTime = millis(); else mqttPreviousTime = 0; - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 2cbd336..6be2173 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -88,7 +88,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("Keybus...")); + Serial.print(F("Keybus....")); dsc.begin(); Serial.println(F("connected.")); Serial.println(F("DSC Keypad Interface is online.")); diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index 6bad788..d24467d 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -184,7 +184,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index d72707c..f210053 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -117,7 +117,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); ipClient.setTrustAnchors(&pushbulletCert); @@ -128,7 +128,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time")); + Serial.print(F("NTP time....")); configTime(0, 0, "pool.ntp.org"); time_t now = time(nullptr); while (now < 24 * 3600) @@ -193,7 +193,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index c79ac8d..e08a341 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -108,7 +108,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); ipClient.setTrustAnchors(&telegramCert); @@ -119,7 +119,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time")); + Serial.print(F("NTP time....")); configTime(0, 0, "pool.ntp.org"); time_t now = time(nullptr); while (now < 24 * 3600) @@ -201,7 +201,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); diff --git a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino index 1ffced1..cdecba5 100644 --- a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino @@ -72,7 +72,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { @@ -82,7 +82,7 @@ void setup() { Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("NTP time...")); + Serial.print(F("NTP time....")); configTime(ntpTimeZone, ntpServer); // Initiates the NTP client, synced hourly time(&ntpNow); while (ntpNow < 1606784461) diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index eaa2822..228a3cb 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -1,10 +1,12 @@ /* - * Twilio SMS Notification 1.4 (esp8266) + * Twilio SMS Notification 1.5 (esp8266) * * Processes the security system status and demonstrates how to send an SMS text message when the status has * changed. This example sends SMS text messages via Twilio: https://www.twilio.com * * Release notes: + * 1.5 - Add TLS root certificate for Twilio + * Encode authorization data in base64 directly within the sketch * 1.4 - Added DSC Classic series support * 1.3 - Updated esp8266 wiring diagram for 33k/10k resistors * 1.2 - Check if WiFi disconnects and wait to send updates until reconnection @@ -47,13 +49,13 @@ #include #include +#include "base64.h" // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard -const char* Base64EncodedAuth = ""; // macOS/Linux terminal: $ echo -n "AccountSID:AuthToken" | base64 -w 0 const char* From = ""; // i.e. 16041234567 const char* To = ""; // i.e. 16041234567 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages @@ -63,14 +65,44 @@ const char* messagePrefix = "[Security system] "; // Set a prefix for all messa #define dscReadPin D2 // GPIO 4 #define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 +// HTTPS root certificate for api.twilio.com: DigiCert Global Root CA, expires 2031.11.10 +const char twilioCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +)=EOF="; + // Initialize components #ifndef dscClassicSeries dscKeybusInterface dsc(dscClockPin, dscReadPin); #else dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); #endif +X509List twilioCert(twilioCertificateRoot); WiFiClientSecure ipClient; bool wifiConnected = true; +char twilioAuth[128]; +size_t twilioAuthLength = 128; +char encodedTwilioAuth[128]; void setup() { @@ -79,9 +111,10 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); + ipClient.setTrustAnchors(&twilioCert); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); @@ -90,7 +123,22 @@ void setup() { Serial.println(WiFi.localIP()); ipClient.setInsecure(); + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + // Sends a message on startup to verify connectivity + strcat(twilioAuth, AccountSID); + strcat(twilioAuth, ":"); + strcat(twilioAuth, AuthToken); + base64::encode(String(twilioAuth)).toCharArray(encodedTwilioAuth, 128); Serial.print(F("Twilio....")); if (sendMessage("Initializing")) Serial.println(F("connected.")); else Serial.println(F("connection error.")); @@ -144,7 +192,7 @@ void loop() { // Checks armed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - char messageContent[25]; + char messageContent[30]; if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); @@ -292,7 +340,7 @@ bool sendMessage(const char* messageContent) { ipClient.print(AccountSID); ipClient.println(F("/Messages.json HTTP/1.1")); ipClient.print(F("Authorization: Basic ")); - ipClient.println(Base64EncodedAuth); + ipClient.println(encodedTwilioAuth); ipClient.println(F("Host: api.twilio.com")); ipClient.println(F("User-Agent: ESP8266")); ipClient.println(F("Accept: */*")); diff --git a/examples/esp8266/Unlocker/Unlocker.ino b/examples/esp8266/Unlocker/Unlocker.ino index ca223d0..96ba197 100644 --- a/examples/esp8266/Unlocker/Unlocker.ino +++ b/examples/esp8266/Unlocker/Unlocker.ino @@ -521,7 +521,7 @@ void setup() { // Starts the Keybus interface and optionally specifies how to print data. // begin() sets Serial by default and can accept a different stream: begin(Serial1), etc. dsc.begin(); - Serial.print(F("DSC Keybus Interface...")); + Serial.print(F("DSC Keybus Interface....")); // Loops until partition 1 is ready for key presses in status "Partition ready" (0x01), // "Stay/away zones open" (0x02), or "Zones open" (0x03) diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index ccad2bb..b0351b5 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -231,13 +231,13 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); Blynk.begin(blynkAuthToken, wifiSSID, wifiPassword, blynkServer, blynkPort); while (WiFi.status() != WL_CONNECTED) yield(); Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - Serial.print(F("Blynk...")); + Serial.print(F("Blynk....")); while (!Blynk.connected()) { Blynk.run(); yield(); diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index e204b51..4f5c5db 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -9,8 +9,8 @@ * 1. Install the following libraries directly from each Github repository: * ESPAsyncTCP: https://github.com/me-no-dev/ESPAsyncTCP * ESPAsyncWebServer: https://github.com/arjenhiemstra/ESPAsyncWebServer - * * This fork of the original ESPAsyncWebServer fixes the web server crashing - * when used with recent versions of Safari on macOS and iOS + * * This is a fork of the original ESPAsyncWebServer that fixes the web server crashing + * when used with recent versions of Safari on macOS and iOS. * * 2. Install ESP8266FS to enable uploading web server files to the esp8266: * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#uploading-files-to-file-system @@ -129,7 +129,7 @@ void setup() { Serial.println(); Serial.println(); - Serial.print(F("WiFi...")); + Serial.print(F("WiFi....")); WiFi.mode(WIFI_STA); WiFi.begin(wifiSSID, wifiPassword); while (WiFi.status() != WL_CONNECTED) { diff --git a/src/dscKeybus.h b/src/dscKeybus.h index c008d4a..31558c1 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -151,7 +151,6 @@ class dscKeybusInterface { static void dscDataInterrupt(); // Deprecated - bool handlePanel(); // Returns true if valid panel data is available. Relabeled to loop() bool processRedundantData; // Controls if repeated periodic commands are processed and displayed (default: false) private: diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 7ed739c..6f729d8 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -226,160 +226,6 @@ bool dscKeybusInterface::loop() { } -// Deprecated, replaced by loop() -bool dscKeybusInterface::handlePanel() { - - // Checks if Keybus data is detected and sets a status flag if data is not detected for 3s - #if defined(ESP32) - portENTER_CRITICAL(&timer1Mux); - #else - noInterrupts(); - #endif - - if (millis() - keybusTime > 3000) keybusConnected = false; // keybusTime is set in dscDataInterrupt() when the clock resets - else keybusConnected = true; - - #if defined(ESP32) - portEXIT_CRITICAL(&timer1Mux); - #else - interrupts(); - #endif - - if (previousKeybus != keybusConnected) { - previousKeybus = keybusConnected; - keybusChanged = true; - if (!pauseStatus) statusChanged = true; - if (!keybusConnected) return true; - } - - // Writes keys when multiple keys are sent as a char array - if (writeKeysPending) writeKeys(writeKeysArray); - - // Skips processing if the panel data buffer is empty - if (panelBufferLength == 0) return false; - - // Copies data from the buffer to panelData[] - static byte panelBufferIndex = 1; - byte dataIndex = panelBufferIndex - 1; - for (byte i = 0; i < dscReadSize; i++) panelData[i] = panelBuffer[dataIndex][i]; - panelBitCount = panelBufferBitCount[dataIndex]; - panelByteCount = panelBufferByteCount[dataIndex]; - panelBufferIndex++; - - // Resets counters when the buffer is cleared - #if defined(ESP32) - portENTER_CRITICAL(&timer1Mux); - #else - noInterrupts(); - #endif - - if (panelBufferIndex > panelBufferLength) { - panelBufferIndex = 1; - panelBufferLength = 0; - } - - #if defined(ESP32) - portEXIT_CRITICAL(&timer1Mux); - #else - interrupts(); - #endif - - // Waits at startup for the 0x05 status command or a command with valid CRC data to eliminate spurious data. - static bool firstClockCycle = true; - if (firstClockCycle) { - if ((validCRC() || panelData[0] == 0x05) && panelData[0] != 0) firstClockCycle = false; - else return false; - } - - // Skips redundant data sent constantly while in installer programming - static byte previousCmd0A[dscReadSize]; - static byte previousCmdE6_20[dscReadSize]; - switch (panelData[0]) { - case 0x0A: // Status in programming - if (redundantPanelData(previousCmd0A, panelData)) return false; - break; - - case 0xE6: - if (panelData[2] == 0x20 && redundantPanelData(previousCmdE6_20, panelData)) return false; // Status in programming, zone lights 33-64 - break; - } - if (dscPartitions > 4) { - static byte previousCmdE6_03[dscReadSize]; - if (panelData[0] == 0xE6 && panelData[2] == 0x03 && redundantPanelData(previousCmdE6_03, panelData, 8)) return false; // Status in alarm/programming, partitions 5-8 - } - - // Skips redundant data from periodic commands sent at regular intervals, by default this data is processed - if (!processRedundantData) { - static byte previousCmd11[dscReadSize]; - static byte previousCmd16[dscReadSize]; - static byte previousCmd27[dscReadSize]; - static byte previousCmd2D[dscReadSize]; - static byte previousCmd34[dscReadSize]; - static byte previousCmd3E[dscReadSize]; - static byte previousCmd5D[dscReadSize]; - static byte previousCmd63[dscReadSize]; - static byte previousCmdB1[dscReadSize]; - static byte previousCmdC3[dscReadSize]; - switch (panelData[0]) { - case 0x11: // Keypad slot query - if (redundantPanelData(previousCmd11, panelData)) return false; - break; - - case 0x16: // Zone wiring - if (redundantPanelData(previousCmd16, panelData)) return false; - break; - - case 0x27: // Status with zone 1-8 info - if (redundantPanelData(previousCmd27, panelData)) return false; - break; - - case 0x2D: // Status with zone 9-16 info - if (redundantPanelData(previousCmd2D, panelData)) return false; - break; - - case 0x34: // Status with zone 17-24 info - if (redundantPanelData(previousCmd34, panelData)) return false; - break; - - case 0x3E: // Status with zone 25-32 info - if (redundantPanelData(previousCmd3E, panelData)) return false; - break; - - case 0x5D: // Flash panel lights: status and zones 1-32 - if (redundantPanelData(previousCmd5D, panelData)) return false; - break; - - case 0x63: // Flash panel lights: status and zones 33-64 - if (redundantPanelData(previousCmd63, panelData)) return false; - break; - - case 0xB1: // Enabled zones 1-32 - if (redundantPanelData(previousCmdB1, panelData)) return false; - break; - - case 0xC3: // Unknown command - if (redundantPanelData(previousCmdC3, panelData)) return false; - break; - } - } - - // Processes valid panel data - switch (panelData[0]) { - case 0x05: - case 0x1B: processPanelStatus(); break; - case 0x27: processPanel_0x27(); break; - case 0x2D: processPanel_0x2D(); break; - case 0x34: processPanel_0x34(); break; - case 0x3E: processPanel_0x3E(); break; - case 0xA5: processPanel_0xA5(); break; - case 0xE6: if (dscPartitions > 2) processPanel_0xE6(); break; - case 0xEB: if (dscPartitions > 2) processPanel_0xEB(); break; - } - - return true; -} - - bool dscKeybusInterface::handleModule() { if (!moduleDataCaptured) return false; moduleDataCaptured = false; From 28520b5b44ba7ffe995d81a8bf84aa04042965d2 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Tue, 1 Feb 2022 12:38:54 +0300 Subject: [PATCH 35/49] Add Pushover example sketch for esp8266/esp32 --- README.md | 9 +- examples/esp32/Email/Email.ino | 2 + examples/esp32/Pushbullet/Pushbullet.ino | 8 +- examples/esp32/Pushover/Pushover.ino | 388 +++++++++++++++++++++ examples/esp32/Twilio-SMS/Twilio-SMS.ino | 2 + examples/esp8266/Pushbullet/Pushbullet.ino | 2 +- examples/esp8266/Pushover/Pushover.ino | 387 ++++++++++++++++++++ 7 files changed, 791 insertions(+), 7 deletions(-) create mode 100644 examples/esp32/Pushover/Pushover.ino create mode 100644 examples/esp8266/Pushover/Pushover.ino diff --git a/README.md b/README.md index 861dcdb..683fe64 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This enables existing DSC security system installations to retain the features a The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) -* Notifications: [Telegram](https://www.telegram.org) bot, [PushBullet](https://www.pushbullet.com), [Twilio SMS](https://www.twilio.com), E-mail +* Notifications: [Telegram](https://www.telegram.org) bot (with arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app * Keypad interface: Use DSC keypads as physical input devices for any general purpose (without a DSC panel). * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes @@ -95,9 +95,9 @@ This library uses a combination of hardware and timer interrupts to accurately c * Includes [Arduino framework support](https://github.com/esp8266/Arduino) and WiFi for ~$3USD shipped. - esp32: * Development boards: NodeMCU ESP-32S, Doit ESP32 Devkit v1, Wemos Lolin D32, etc. - * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (v1.0.5-rc6 or newer required), dual cores, WiFi, and Bluetooth for ~$5USD shipped. + * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (v2.0.2 or newer required), dual cores, WiFi, and Bluetooth for ~$5USD shipped. - esp32-s2: - * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (idf-release/v4.2 branch required) and WiFi. + * Includes [Arduino framework support](https://github.com/espressif/arduino-esp32) (v2.0.2 or newer required) and WiFi. * Possible features (PRs welcome!): - [DSC IT-100](https://cms.dsc.com/download.php?t=1&id=16238) emulation - Unlock 6-digit installer codes @@ -106,6 +106,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * develop - New: DSC Classic series panel support: PC1500, PC1550 - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. + - New: `Pushover` example sketch for esp8266 and esp32 - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed - Updated: Added TLS root certificate to `Twilio-SMS` @@ -219,6 +220,8 @@ The included examples demonstrate how to use the library and can be used as-is o * **Telegram** (esp8266/esp32): Demonstrates sending status updates and arming/disarming the security system via a [Telegram](https://www.telegram.org) bot. +* **Pushover** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushover](https://www.pushover.net). + * **Pushbullet** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushbullet](https://www.pushbullet.com). * **Twilio-SMS** (esp8266/esp32): Demonstrates sending status updates as an SMS text message via [Twilio](https://www.twilio.com) - thanks to [ColingNG](https://github.com/ColinNg) for contributing this example! diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index 66534e9..90a6f32 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -243,6 +243,7 @@ bool smtpValidResponse() { while (!ipClient.available()) { dsc.loop(); // Processes Keybus data while waiting on the SMTP response if (millis() - previousMillis > 3000) { + Serial.println(); Serial.println(F("Connection timed out waiting for a response.")); ipClient.stop(); return false; @@ -261,6 +262,7 @@ bool smtpValidResponse() { // Unsuccessful, prints the response to serial to help debug else { + Serial.println(); Serial.println(F("Email send error, response:")); Serial.print(replyCode); while (ipClient.available()) Serial.print((char)ipClient.read()); diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index 70df6fa..d381412 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -327,7 +327,7 @@ void loop() { } -bool sendMessage(const char* pushMessage) { +bool sendMessage(const char* messageContent) { ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 // Connects and sends the message as JSON @@ -338,13 +338,13 @@ bool sendMessage(const char* pushMessage) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(pushMessage) + strlen(messagePrefix) + 25); // Length including JSON data + ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); // Length including JSON data ipClient.print(F("Access-Token: ")); ipClient.println(pushbulletToken); ipClient.println(); ipClient.print(F("{\"body\":\"")); ipClient.print(messagePrefix); - ipClient.print(pushMessage); + ipClient.print(messageContent); ipClient.print(F("\",\"type\":\"note\"}")); // Waits for a response @@ -352,6 +352,7 @@ bool sendMessage(const char* pushMessage) { while (!ipClient.available()) { dsc.loop(); if (millis() - previousMillis > 3000) { + Serial.println(); Serial.println(F("Connection timed out waiting for a response.")); ipClient.stop(); return false; @@ -376,6 +377,7 @@ bool sendMessage(const char* pushMessage) { // Unsuccessful, prints the response to serial to help debug else { + Serial.println(); Serial.println(F("Push notification error, response:")); Serial.print(statusCode); while (ipClient.available()) Serial.print((char)ipClient.read()); diff --git a/examples/esp32/Pushover/Pushover.ino b/examples/esp32/Pushover/Pushover.ino new file mode 100644 index 0000000..b613790 --- /dev/null +++ b/examples/esp32/Pushover/Pushover.ino @@ -0,0 +1,388 @@ +/* + * Pushover Push Notification 1.0 (esp32) + * + * Processes the security system status and demonstrates how to send a push notification when the status has changed. + * This example sends notifications via Pushover: https://www.pushover.net + * + * Usage: + * 1. Set the WiFi SSID and password in the sketch. + * 2. Create a Pushover account: https://www.pushover.net + * 3. Copy the user key to pushoverUserKey. + * 4. Create a Pushover application to get an API token: https://pushover.net/apps/build + * 5. Copy the API token to pushoverAPIToken. + * 6. Upload the sketch. + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp32 development board 5v pin + * + * DSC Aux(-) --- esp32 Ground + * + * +--- dscClockPin // Default: 18 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: 19 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* pushoverUserKey = ""; // Set the user key generated in the Pushover account settings +const char* pushoverAPIToken = ""; // Set the API token generated in the Pushover account settings +const char* messagePrefix = "[Security system] "; // Set a prefix for all messages + +// Configures the Keybus interface with the specified pins. +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 + +// HTTPS root certificate for api.pushover.net: DigiCert Global Root CA, expires 2031.11.10 +const char pushoverCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +)=EOF="; + +// Initialize components +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +WiFiClientSecure ipClient; +bool wifiConnected = true; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi....")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + ipClient.setCACert(pushoverCertificateRoot); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + + // Sends a push notification on startup to verify connectivity + Serial.print(F("Pushover....")); + if (sendMessage("Initializing")) Serial.println(F("connected.")); + else Serial.println(F("connection error.")); + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + + // Updates status if WiFi drops and reconnects + if (!wifiConnected && WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi reconnected"); + wifiConnected = true; + dsc.pauseStatus = false; + dsc.statusChanged = true; + } + else if (WiFi.status() != WL_CONNECTED && wifiConnected) { + Serial.println("WiFi disconnected"); + wifiConnected = false; + dsc.pauseStatus = true; + } + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) sendMessage("Connected"); + else sendMessage("Disconnected"); + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks armed status + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + char messageContent[30]; + + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); + else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); + else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); + + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char pushMessage[22] = "Disarmed: Partition "; + appendPartition(partition, pushMessage); // Appends the push message with the partition number + sendMessage(pushMessage); + } + } + + // Checks exit delay status + if (dsc.exitDelayChanged[partition]) { + dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag + + if (dsc.exitDelay[partition]) { + char messageContent[36] = "Exit delay in progress: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.armedChanged[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag + + // Checks fire alarm status + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + + // Checks for zones in alarm + // Zone alarm status is stored in the alarmZones[] and alarmZonesChanged[] arrays using 1 bit per zone, up to 64 zones + // alarmZones[0] and alarmZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 + // alarmZones[1] and alarmZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 + // ... + // alarmZones[7] and alarmZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 + if (dsc.alarmZonesStatusChanged) { + dsc.alarmZonesStatusChanged = false; // Resets the alarm zones status flag + for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) { + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(dsc.alarmZonesChanged[zoneGroup], zoneBit)) { // Checks an individual alarm zone status flag + bitWrite(dsc.alarmZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual alarm zone status flag + if (bitRead(dsc.alarmZones[zoneGroup], zoneBit)) { // Zone alarm + char pushMessage[15] = "Zone alarm: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(pushMessage, zoneNumber); + sendMessage(pushMessage); + } + else { + char pushMessage[24] = "Zone alarm restored: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(pushMessage, zoneNumber); + sendMessage(pushMessage); + } + } + } + } + } + + // Checks trouble status + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + + // Checks for AC power status + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } +} + + +bool sendMessage(const char* messageContent) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 + + // Connects and sends the message as JSON + if (!ipClient.connect("api.pushover.net", 443)) return false; + ipClient.println(F("POST /1/messages.json HTTP/1.1")); + ipClient.println(F("Host: api.pushover.net")); + ipClient.println(F("User-Agent: ESP32")); + ipClient.println(F("Accept: */*")); + ipClient.println(F("Content-Type: application/json")); + ipClient.print(F("Content-Length: ")); + ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); // Length including JSON data + ipClient.println(); + ipClient.print(F("{\"token\":\"")); + ipClient.print(pushoverAPIToken); + ipClient.print(F("\",\"user\":\"")); + ipClient.print(pushoverUserKey); + ipClient.print(F("\",\"message\":\"")); + ipClient.print(messagePrefix); + ipClient.print(messageContent); + ipClient.print(F("\"}")); + + // Waits for a response + unsigned long previousMillis = millis(); + while (!ipClient.available()) { + dsc.loop(); + if (millis() - previousMillis > 3000) { + Serial.println(); + Serial.println(F("Connection timed out waiting for a response.")); + ipClient.stop(); + return false; + } + } + + // Reads the response until the first space - the next characters will be the HTTP status code + while (ipClient.available()) { + if (ipClient.read() == ' ') break; + } + + // Checks the first character of the HTTP status code - the message was sent successfully if the status code + // begins with "2" + char statusCode = ipClient.read(); + + // Successful, reads the remaining response to clear the client buffer + if (statusCode == '2') { + while (ipClient.available()) ipClient.read(); + ipClient.stop(); + return true; + } + + // Unsuccessful, prints the response to serial to help debug + else { + Serial.println(); + Serial.println(F("Push notification error, response:")); + Serial.print(statusCode); + while (ipClient.available()) Serial.print((char)ipClient.read()); + Serial.println(); + ipClient.stop(); + return false; + } +} + + +void appendPartition(byte sourceNumber, char* pushMessage) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(pushMessage, partitionNumber); +} diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 893cd25..5c2d6ac 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -356,6 +356,7 @@ bool sendMessage(const char* messageContent) { while (!ipClient.available()) { dsc.loop(); if (millis() - previousMillis > 3000) { + Serial.println(); Serial.println(F("Connection timed out waiting for a response.")); ipClient.stop(); return false; @@ -380,6 +381,7 @@ bool sendMessage(const char* messageContent) { // Unsuccessful, prints the response to serial to help debug else { + Serial.println(); Serial.println(F("SMS messaging error, response:")); Serial.print(statusCode); while (ipClient.available()) Serial.print((char)ipClient.read()); diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index f210053..14899f8 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -343,7 +343,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(messageContent) + strlen(messagePrefix) + 25); // Length including JSON data + ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); // Length including JSON data ipClient.print(F("Access-Token: ")); ipClient.println(pushbulletToken); ipClient.println(); diff --git a/examples/esp8266/Pushover/Pushover.ino b/examples/esp8266/Pushover/Pushover.ino new file mode 100644 index 0000000..a2076f0 --- /dev/null +++ b/examples/esp8266/Pushover/Pushover.ino @@ -0,0 +1,387 @@ +/* + * Pushover Push Notification 1.0 (esp8266) + * + * Processes the security system status and demonstrates how to send a push notification when the status has changed. + * This example sends notifications via Pushover: https://www.pushover.net + * + * Usage: + * 1. Set the WiFi SSID and password in the sketch. + * 2. Create a Pushover account: https://www.pushover.net + * 3. Copy the user key to pushoverUserKey. + * 4. Create a Pushover application to get an API token: https://pushover.net/apps/build + * 5. Copy the API token to pushoverAPIToken. + * 6. Upload the sketch. + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp8266 development board 5v pin (NodeMCU, Wemos) + * + * DSC Aux(-) --- esp8266 Ground + * + * +--- dscClockPin // Default: D1, GPIO 5 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: D2, GPIO 4 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* pushoverUserKey = ""; // Set the user key generated in the Pushover account settings +const char* pushoverAPIToken = ""; // Set the API token generated in the Pushover account settings +const char* messagePrefix = "[Security system] "; // Set a prefix for all messages + +// Configures the Keybus interface with the specified pins. +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 + +// HTTPS root certificate for api.pushover.net: DigiCert Global Root CA, expires 2031.11.10 +const char pushoverCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +)=EOF="; + +// Initialize components +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +X509List pushoverCert(pushoverCertificateRoot); +WiFiClientSecure ipClient; +bool wifiConnected = true; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi....")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + ipClient.setTrustAnchors(&pushoverCert); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + + // Sends a message on startup to verify connectivity + Serial.print(F("Pushover....")); + if (sendMessage("Initializing")) Serial.println(F("connected.")); + else Serial.println(F("connection error.")); + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + + // Updates status if WiFi drops and reconnects + if (!wifiConnected && WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi reconnected"); + wifiConnected = true; + dsc.pauseStatus = false; + dsc.statusChanged = true; + } + else if (WiFi.status() != WL_CONNECTED && wifiConnected) { + Serial.println("WiFi disconnected"); + wifiConnected = false; + dsc.pauseStatus = true; + } + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) sendMessage("Connected"); + else sendMessage("Disconnected"); + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks armed status + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + char messageContent[30]; + + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); + else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); + else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); + + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks exit delay status + if (dsc.exitDelayChanged[partition]) { + dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag + + if (dsc.exitDelay[partition]) { + char messageContent[36] = "Exit delay in progress: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.armedChanged[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag + + // Checks fire alarm status + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + + // Checks for zones in alarm + // Zone alarm status is stored in the alarmZones[] and alarmZonesChanged[] arrays using 1 bit per zone, up to 64 zones + // alarmZones[0] and alarmZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 + // alarmZones[1] and alarmZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 + // ... + // alarmZones[7] and alarmZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 + if (dsc.alarmZonesStatusChanged) { + dsc.alarmZonesStatusChanged = false; // Resets the alarm zones status flag + for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) { + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(dsc.alarmZonesChanged[zoneGroup], zoneBit)) { // Checks an individual alarm zone status flag + bitWrite(dsc.alarmZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual alarm zone status flag + if (bitRead(dsc.alarmZones[zoneGroup], zoneBit)) { // Zone alarm + char messageContent[15] = "Zone alarm: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + else { + char messageContent[24] = "Zone alarm restored: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + } + } + } + } + + // Checks trouble status + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + + // Checks for AC power status + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } +} + + +bool sendMessage(const char* messageContent) { + + // Connects and sends the message as JSON + if (!ipClient.connect("api.pushover.net", 443)) return false; + ipClient.println(F("POST /1/messages.json HTTP/1.1")); + ipClient.println(F("Host: api.pushover.net")); + ipClient.println(F("User-Agent: ESP32")); + ipClient.println(F("Accept: */*")); + ipClient.println(F("Content-Type: application/json")); + ipClient.print(F("Content-Length: ")); + ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); // Length including JSON data + ipClient.println(); + ipClient.print(F("{\"token\":\"")); + ipClient.print(pushoverAPIToken); + ipClient.print(F("\",\"user\":\"")); + ipClient.print(pushoverUserKey); + ipClient.print(F("\",\"message\":\"")); + ipClient.print(messagePrefix); + ipClient.print(messageContent); + ipClient.print(F("\"}")); + + // Waits for a response + unsigned long previousMillis = millis(); + while (!ipClient.available()) { + dsc.loop(); + if (millis() - previousMillis > 3000) { + Serial.println(); + Serial.println(F("Connection timed out waiting for a response.")); + ipClient.stop(); + return false; + } + } + + // Reads the response until the first space - the next characters will be the HTTP status code + while (ipClient.available()) { + if (ipClient.read() == ' ') break; + } + + // Checks the first character of the HTTP status code - the message was sent successfully if the status code + // begins with "2" + char statusCode = ipClient.read(); + + // Successful, reads the remaining response to clear the client buffer + if (statusCode == '2') { + while (ipClient.available()) ipClient.read(); + ipClient.stop(); + return true; + } + + // Unsuccessful, prints the response to serial to help debug + else { + Serial.println(); + Serial.println(F("Push notification error, response:")); + Serial.print(statusCode); + while (ipClient.available()) Serial.print((char)ipClient.read()); + Serial.println(); + ipClient.stop(); + return false; + } +} + + +void appendPartition(byte sourceNumber, char* messageContent) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(messageContent, partitionNumber); +} From 15a61d1f50af9950e8f414b154226af9746dccec Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Wed, 2 Feb 2022 15:19:24 +0300 Subject: [PATCH 36/49] Add Pushsafer example sketch for esp8266/esp32, add URL encoding for Twilio-SMS messages --- README.md | 16 +- examples/esp32/Pushsafer/Pushsafer.ino | 422 +++++++++++++++++++++ examples/esp32/Twilio-SMS/Twilio-SMS.ino | 55 ++- examples/esp8266/Pushover/Pushover.ino | 2 +- examples/esp8266/Pushsafer/Pushsafer.ino | 422 +++++++++++++++++++++ examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 57 ++- 6 files changed, 942 insertions(+), 32 deletions(-) create mode 100644 examples/esp32/Pushsafer/Pushsafer.ino create mode 100644 examples/esp8266/Pushsafer/Pushsafer.ino diff --git a/README.md b/README.md index 683fe64..c3cbe10 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This enables existing DSC security system installations to retain the features a The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) -* Notifications: [Telegram](https://www.telegram.org) bot (with arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Twilio SMS](https://www.twilio.com), E-mail +* Notifications: [Telegram](https://www.telegram.org) bot (with arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com) [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app * Keypad interface: Use DSC keypads as physical input devices for any general purpose (without a DSC panel). * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes @@ -106,7 +106,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * develop - New: DSC Classic series panel support: PC1500, PC1550 - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. - - New: `Pushover` example sketch for esp8266 and esp32 + - New: [Pushover](https://www.pushover.net) and [Pushsafer](https://www.pushsafer.com) push notification example sketches for esp8266/esp32 - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed - Updated: Added TLS root certificate to `Twilio-SMS` @@ -218,11 +218,13 @@ The included examples demonstrate how to use the library and can be used as-is o * **Homey**: Integrates with [Athom Homey](https://www.athom.com/en/) and the [Homeyduino](https://github.com/athombv/homey-arduino-library/) library, including armed, alarm, and fire states (currently limited to one partition), and zone states. Thanks to [MagnusPer](https://github.com/MagnusPer) for contributing this example! -* **Telegram** (esp8266/esp32): Demonstrates sending status updates and arming/disarming the security system via a [Telegram](https://www.telegram.org) bot. +* **Telegram** (esp8266/esp32): Demonstrates sending status updates and arming/disarming the security system via a [Telegram](https://www.telegram.org) bot. Supports iOS, Android, and macOS/Windows/Linux desktop notifications (free). -* **Pushover** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushover](https://www.pushover.net). +* **Pushover** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushover](https://www.pushover.net). Supports iOS, Android, macOS native desktop notifications, and Chrome/Firefox/Safari browser popups ($4.99USD one-time purchase per client platform). -* **Pushbullet** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushbullet](https://www.pushbullet.com). +* **Pushbullet** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushbullet](https://www.pushbullet.com). Supports Android, Windows desktop notifications, and Chrome/Firefox browser popups (free). Note that iOS is no longer supported. + +* **Pushsafer** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushsafer](https://www.pushsafer.com). Supports iOS, Android, Windows desktop notifications, and Chrome/Firefox/Edge/Opera/Yandex browser popups (€0.99EUR or less per 1000 notifications). * **Twilio-SMS** (esp8266/esp32): Demonstrates sending status updates as an SMS text message via [Twilio](https://www.twilio.com) - thanks to [ColingNG](https://github.com/ColinNg) for contributing this example! @@ -233,12 +235,12 @@ The included examples demonstrate how to use the library and can be used as-is o * Verizon: 5558675309@vtext.com * AT&T: 5558675309@txt.att.net -* **VirtualKeypad-Blynk** (esp8266/esp32): Provides a virtual keypad interface for the free [Blynk](https://www.blynk.cc) app on iOS and Android, including viewing alarm memory, programming zone lights, and the event buffer. Scan one of the following QR codes from within the Blynk app for an example keypad layout: +* **VirtualKeypad-Blynk** (esp8266/esp32): Provides a virtual keypad interface for the free [Blynk legacy](https://www.blynk.cc) app on iOS and Android, including viewing alarm memory, programming zone lights, and the event buffer. The newer generation Blynk app is not currently supported. Scan one of the following QR codes from within the Blynk app for an example keypad layout: - [Virtual keypad with 16 zones](https://user-images.githubusercontent.com/12835671/103719316-5f6f1d80-4f8e-11eb-8a7c-4bd7bfe3cd8a.png) - [Virtual keypad with 32 zones](https://user-images.githubusercontent.com/12835671/103719459-af4de480-4f8e-11eb-8e4a-7172961e2d29.png) - [Virtual keypad with 8 zones and event log](https://user-images.githubusercontent.com/12835671/103719518-cc82b300-4f8e-11eb-8b2a-97299e7be3a2.png) - Note: Installing [Blynk as a local server](https://github.com/blynkkk/blynk-server) is recommended to keep control of the security system internal to your network. This also lets you use as many widgets as needed for free - local servers can setup users with any amount of Blynk Energy. Using the default Blynk cloud service with the above example layouts requires more of Blynk's Energy units than available on the free usage tier. + Note: Installing [Blynk as a local server](https://github.com/blynkkk/blynk-server) is recommended to keep control of the security system internal to your network. * **VirtualKeypad-Web** (esp8266/esp32): Provides a virtual keypad web interface, using the esp8266/esp32 itself as a standalone web server, including viewing alarm memory, programming zone lights, and the event buffer. Thanks to [Elektrik1](https://github.com/Elektrik1) for contributing this example! diff --git a/examples/esp32/Pushsafer/Pushsafer.ino b/examples/esp32/Pushsafer/Pushsafer.ino new file mode 100644 index 0000000..64d33da --- /dev/null +++ b/examples/esp32/Pushsafer/Pushsafer.ino @@ -0,0 +1,422 @@ +/* + * Pushsafer Push Notification 1.0 (esp32) + * + * Processes the security system status and demonstrates how to send a push notification when the status has changed. + * This example sends notifications via Pushsafer: https://www.pushsafer.com + * + * Usage: + * 1. Set the WiFi SSID and password in the sketch. + * 2. Create a Pushsafer account: https://www.pushsafer.com + * 3. Copy the private key to pushsaferKey. + * 4. Upload the sketch. + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp32 development board 5v pin + * + * DSC Aux(-) --- esp32 Ground + * + * +--- dscClockPin // Default: 18 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: 19 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* pushsaferKey = ""; // Set the private key generated in the Pushsafer account settings +const char* messagePrefix = "[Security system] "; // Set a prefix for all messages + +// Configures the Keybus interface with the specified pins. +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 + +// HTTPS root certificate for www.pushsafer.com: ISRG Root X1, expires 2035.06.04 +const char pushsaferCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)=EOF="; + +// Initialize components +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +WiFiClientSecure ipClient; +bool wifiConnected = true; +char encodedMessagePrefix[128], encodedMessageContent[480]; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi....")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + ipClient.setCACert(pushsaferCertificateRoot); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + + // Encodes message prefix in URL encoding + encodeURL(messagePrefix, encodedMessagePrefix); + + // Sends a message on startup to verify connectivity + Serial.print(F("Pushsafer....")); + if (sendMessage("Initializing")) Serial.println(F("connected.")); + else Serial.println(F("connection error.")); + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + + // Updates status if WiFi drops and reconnects + if (!wifiConnected && WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi reconnected"); + wifiConnected = true; + dsc.pauseStatus = false; + dsc.statusChanged = true; + } + else if (WiFi.status() != WL_CONNECTED && wifiConnected) { + Serial.println("WiFi disconnected"); + wifiConnected = false; + dsc.pauseStatus = true; + } + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) sendMessage("Connected"); + else sendMessage("Disconnected"); + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks armed status + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + char messageContent[30]; + + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); + else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); + else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); + + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks exit delay status + if (dsc.exitDelayChanged[partition]) { + dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag + + if (dsc.exitDelay[partition]) { + char messageContent[36] = "Exit delay in progress: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.armedChanged[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag + + // Checks fire alarm status + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + + // Checks for zones in alarm + // Zone alarm status is stored in the alarmZones[] and alarmZonesChanged[] arrays using 1 bit per zone, up to 64 zones + // alarmZones[0] and alarmZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 + // alarmZones[1] and alarmZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 + // ... + // alarmZones[7] and alarmZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 + if (dsc.alarmZonesStatusChanged) { + dsc.alarmZonesStatusChanged = false; // Resets the alarm zones status flag + for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) { + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(dsc.alarmZonesChanged[zoneGroup], zoneBit)) { // Checks an individual alarm zone status flag + bitWrite(dsc.alarmZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual alarm zone status flag + if (bitRead(dsc.alarmZones[zoneGroup], zoneBit)) { // Zone alarm + char messageContent[15] = "Zone alarm: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + else { + char messageContent[24] = "Zone alarm restored: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + } + } + } + } + + // Checks trouble status + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + + // Checks for AC power status + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } +} + + +bool sendMessage(const char* messageContent) { + encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 + + if (!ipClient.connect("www.pushsafer.com", 443)) return false; + ipClient.println(F("POST /api HTTP/1.1")); + ipClient.println(F("Host: www.pushsafer.com")); + ipClient.println(F("User-Agent: ESP32")); + ipClient.println(F("Accept: */*")); + ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); + ipClient.print(F("Content-Length: ")); + ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); // Total length of the request body + ipClient.println(); + ipClient.print(F("k=")); + ipClient.print(pushsaferKey); + ipClient.print(F("&m=")); + ipClient.print(encodedMessagePrefix); + ipClient.print(encodedMessageContent); + + // Waits for a response + unsigned long previousMillis = millis(); + while (!ipClient.available()) { + dsc.loop(); + if (millis() - previousMillis > 3000) { + Serial.println(); + Serial.println(F("Connection timed out waiting for a response.")); + ipClient.stop(); + return false; + } + } + + // Reads the response until the first space - the next characters will be the HTTP status code + while (ipClient.available()) { + if (ipClient.read() == ' ') break; + } + + // Checks the first character of the HTTP status code - the message was sent successfully if the status code + // begins with "2" + char statusCode = ipClient.read(); + + // Successful, reads the remaining response to clear the client buffer + if (statusCode == '2') { + while (ipClient.available()) ipClient.read(); + ipClient.stop(); + return true; + } + + // Unsuccessful, prints the response to serial to help debug + else { + Serial.println(); + Serial.println(F("Push notification error, response:")); + Serial.print(statusCode); + while (ipClient.available()) Serial.print((char)ipClient.read()); + Serial.println(); + ipClient.stop(); + return false; + } +} + + +void appendPartition(byte sourceNumber, char* messageContent) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(messageContent, partitionNumber); +} + + +// Helper for encodeURL() +static char encodeHex(char c) { + return "0123456789ABCDEF"[c & 0x0F]; +} + + +// Encodes a char array to URL encoded using '+' for spaces as required for application/x-www-form-urlencoded +char *encodeURL(const char *src, char *dst) { + char c, *d = dst; + while (c = *src++) { + if (c == ' ') { + *d++ = '+'; + continue; + } + else if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9')) { + *d++ = '%'; + *d++ = encodeHex(c >> 4); + c = encodeHex(c); + } + *d++ = c; + } + *d = '\0'; + return dst; +} diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 5c2d6ac..7003250 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -49,10 +49,10 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard -const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard -const char* From = ""; // i.e. 16041234567 -const char* To = ""; // i.e. 16041234567 +const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard +const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard +const char* From = ""; // From phone number, starting with the country code without the + sign: 18005551234 +const char* To = ""; // To phone number, starting with the country code without the + sign: 18005551234 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. @@ -97,6 +97,7 @@ bool wifiConnected = true; char twilioAuth[128]; size_t twilioAuthLength = 128; unsigned char encodedTwilioAuth[128]; +char encodedMessagePrefix[128], encodedMessageContent[480]; void setup() { @@ -127,11 +128,14 @@ void setup() { } Serial.println(F("synchronized.")); - // Sends a message on startup to verify connectivity + // Encodes authentication in base64 and message prefix in URL encoding strcat(twilioAuth, AccountSID); strcat(twilioAuth, ":"); strcat(twilioAuth, AuthToken); - mbedtls_base64_encode(encodedTwilioAuth,twilioAuthLength,&twilioAuthLength,(unsigned char*)twilioAuth, strlen(twilioAuth)); + mbedtls_base64_encode(encodedTwilioAuth, twilioAuthLength, &twilioAuthLength, (unsigned char*)twilioAuth, strlen(twilioAuth)); + encodeURL(messagePrefix, encodedMessagePrefix); + + // Sends a message on startup to verify connectivity Serial.print(F("Twilio....")); if (sendMessage("Initializing")) Serial.println(F("connected.")); else Serial.println(F("connection error.")); @@ -327,6 +331,7 @@ void loop() { bool sendMessage(const char* messageContent) { ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 + encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding // Connects and sends the message as x-www-form-urlencoded if (!ipClient.connect("api.twilio.com", 443)) return false; @@ -340,16 +345,16 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(To) + strlen(From) + strlen(messagePrefix) + strlen(messageContent) + 18); // Length including data + ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); // Length including data ipClient.println("Connection: Close"); ipClient.println(); - ipClient.print(F("To=+")); + ipClient.print(F("To=%2B")); ipClient.print(To); - ipClient.print(F("&From=+")); + ipClient.print(F("&From=%2B")); ipClient.print(From); ipClient.print(F("&Body=")); - ipClient.print(messagePrefix); - ipClient.println(messageContent); + ipClient.print(encodedMessagePrefix); + ipClient.print(encodedMessageContent); // Waits for a response unsigned long previousMillis = millis(); @@ -397,3 +402,31 @@ void appendPartition(byte sourceNumber, char* messageContent) { itoa(sourceNumber + 1, partitionNumber, 10); strcat(messageContent, partitionNumber); } + + +// Helper for encodeURL() +static char encodeHex(char c) { + return "0123456789ABCDEF"[c & 0x0F]; +} + + +// Encodes a char array to URL encoded using '+' for spaces as required for application/x-www-form-urlencoded +char *encodeURL(const char *src, char *dst) { + char c, *d = dst; + while (c = *src++) { + if (c == ' ') { + *d++ = '+'; + continue; + } + else if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9')) { + *d++ = '%'; + *d++ = encodeHex(c >> 4); + c = encodeHex(c); + } + *d++ = c; + } + *d = '\0'; + return dst; +} diff --git a/examples/esp8266/Pushover/Pushover.ino b/examples/esp8266/Pushover/Pushover.ino index a2076f0..80a79af 100644 --- a/examples/esp8266/Pushover/Pushover.ino +++ b/examples/esp8266/Pushover/Pushover.ino @@ -324,7 +324,7 @@ bool sendMessage(const char* messageContent) { if (!ipClient.connect("api.pushover.net", 443)) return false; ipClient.println(F("POST /1/messages.json HTTP/1.1")); ipClient.println(F("Host: api.pushover.net")); - ipClient.println(F("User-Agent: ESP32")); + ipClient.println(F("User-Agent: ESP8266")); ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); diff --git a/examples/esp8266/Pushsafer/Pushsafer.ino b/examples/esp8266/Pushsafer/Pushsafer.ino new file mode 100644 index 0000000..86cc349 --- /dev/null +++ b/examples/esp8266/Pushsafer/Pushsafer.ino @@ -0,0 +1,422 @@ +/* + * Pushsafer Push Notification 1.0 (esp8266) + * + * Processes the security system status and demonstrates how to send a push notification when the status has changed. + * This example sends notifications via Pushsafer: https://www.pushsafer.com + * + * Usage: + * 1. Set the WiFi SSID and password in the sketch. + * 2. Create a Pushsafer account: https://www.pushsafer.com + * 3. Copy the private key to pushsaferKey. + * 4. Upload the sketch. + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp8266 development board 5v pin (NodeMCU, Wemos) + * + * DSC Aux(-) --- esp8266 Ground + * + * +--- dscClockPin // Default: D1, GPIO 5 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: D2, GPIO 4 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: D7, GPIO 13 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +#include +#include + +// Settings +const char* wifiSSID = ""; +const char* wifiPassword = ""; +const char* pushsaferKey = ""; // Set the private key generated in the Pushsafer account settings +const char* messagePrefix = "[Security system] "; // Set a prefix for all messages + +// Configures the Keybus interface with the specified pins. +#define dscClockPin D1 // GPIO 5 +#define dscReadPin D2 // GPIO 4 +#define dscPC16Pin D7 // DSC Classic Series only, GPIO 13 + +// HTTPS root certificate for www.pushsafer.com: ISRG Root X1, expires 2035.06.04 +const char pushsaferCertificateRoot[] = R"=EOF=( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)=EOF="; + +// Initialize components +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin); +#endif +X509List pushsaferCert(pushsaferCertificateRoot); +WiFiClientSecure ipClient; +bool wifiConnected = true; +char encodedMessagePrefix[128], encodedMessageContent[480]; + + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + Serial.print(F("WiFi....")); + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSSID, wifiPassword); + ipClient.setTrustAnchors(&pushsaferCert); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print(F("connected: ")); + Serial.println(WiFi.localIP()); + + Serial.print(F("NTP time....")); + configTime(0, 0, "pool.ntp.org"); + time_t now = time(nullptr); + while (now < 24 * 3600) + { + Serial.print("."); + delay(2000); + now = time(nullptr); + } + Serial.println(F("synchronized.")); + + // Encodes message prefix in URL encoding + encodeURL(messagePrefix, encodedMessagePrefix); + + // Sends a message on startup to verify connectivity + Serial.print(F("Pushsafer....")); + if (sendMessage("Initializing")) Serial.println(F("connected.")); + else Serial.println(F("connection error.")); + + // Starts the Keybus interface + dsc.begin(); + Serial.println(F("DSC Keybus Interface is online.")); +} + + +void loop() { + + // Updates status if WiFi drops and reconnects + if (!wifiConnected && WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi reconnected"); + wifiConnected = true; + dsc.pauseStatus = false; + dsc.statusChanged = true; + } + else if (WiFi.status() != WL_CONNECTED && wifiConnected) { + Serial.println("WiFi disconnected"); + wifiConnected = false; + dsc.pauseStatus = true; + } + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Checks if the interface is connected to the Keybus + if (dsc.keybusChanged) { + dsc.keybusChanged = false; // Resets the Keybus data status flag + if (dsc.keybusConnected) sendMessage("Connected"); + else sendMessage("Disconnected"); + } + + // Checks status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks armed status + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + char messageContent[30]; + + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night away: Partition "); + else if (dsc.armedAway[partition]) strcpy(messageContent, "Armed away: Partition "); + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) strcpy(messageContent, "Armed night stay: Partition "); + else if (dsc.armedStay[partition]) strcpy(messageContent, "Armed stay: Partition "); + + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks exit delay status + if (dsc.exitDelayChanged[partition]) { + dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag + + if (dsc.exitDelay[partition]) { + char messageContent[36] = "Exit delay in progress: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.exitDelay[partition] && !dsc.armed[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + + // Checks alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + + if (dsc.alarm[partition]) { + char messageContent[19] = "Alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else if (!dsc.armedChanged[partition]) { + char messageContent[22] = "Disarmed: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + dsc.armedChanged[partition] = false; // Resets the partition armed status flag + + // Checks fire alarm status + if (dsc.fireChanged[partition]) { + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) { + char messageContent[24] = "Fire alarm: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + else { + char messageContent[33] = "Fire alarm restored: Partition "; + appendPartition(partition, messageContent); // Appends the message with the partition number + sendMessage(messageContent); + } + } + } + + // Checks for zones in alarm + // Zone alarm status is stored in the alarmZones[] and alarmZonesChanged[] arrays using 1 bit per zone, up to 64 zones + // alarmZones[0] and alarmZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 + // alarmZones[1] and alarmZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 + // ... + // alarmZones[7] and alarmZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 + if (dsc.alarmZonesStatusChanged) { + dsc.alarmZonesStatusChanged = false; // Resets the alarm zones status flag + for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) { + for (byte zoneBit = 0; zoneBit < 8; zoneBit++) { + if (bitRead(dsc.alarmZonesChanged[zoneGroup], zoneBit)) { // Checks an individual alarm zone status flag + bitWrite(dsc.alarmZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual alarm zone status flag + if (bitRead(dsc.alarmZones[zoneGroup], zoneBit)) { // Zone alarm + char messageContent[15] = "Zone alarm: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + else { + char messageContent[24] = "Zone alarm restored: "; + char zoneNumber[3]; + itoa((zoneBit + 1 + (zoneGroup * 8)), zoneNumber, 10); // Determines the zone number + strcat(messageContent, zoneNumber); + sendMessage(messageContent); + } + } + } + } + } + + // Checks trouble status + if (dsc.troubleChanged) { + dsc.troubleChanged = false; // Resets the trouble status flag + if (dsc.trouble) sendMessage("Trouble status on"); + else sendMessage("Trouble status restored"); + } + + // Checks for AC power status + if (dsc.powerChanged) { + dsc.powerChanged = false; // Resets the battery trouble status flag + if (dsc.powerTrouble) sendMessage("AC power trouble"); + else sendMessage("AC power restored"); + } + + // Checks panel battery status + if (dsc.batteryChanged) { + dsc.batteryChanged = false; // Resets the battery trouble status flag + if (dsc.batteryTrouble) sendMessage("Panel battery trouble"); + else sendMessage("Panel battery restored"); + } + + // Checks for keypad fire alarm status + if (dsc.keypadFireAlarm) { + dsc.keypadFireAlarm = false; // Resets the keypad fire alarm status flag + sendMessage("Keypad Fire alarm"); + } + + // Checks for keypad aux auxiliary alarm status + if (dsc.keypadAuxAlarm) { + dsc.keypadAuxAlarm = false; // Resets the keypad auxiliary alarm status flag + sendMessage("Keypad Aux alarm"); + } + + // Checks for keypad panic alarm status + if (dsc.keypadPanicAlarm) { + dsc.keypadPanicAlarm = false; // Resets the keypad panic alarm status flag + sendMessage("Keypad Panic alarm"); + } + } +} + + +bool sendMessage(const char* messageContent) { + encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding + + if (!ipClient.connect("www.pushsafer.com", 443)) return false; + ipClient.println(F("POST /api HTTP/1.1")); + ipClient.println(F("Host: www.pushsafer.com")); + ipClient.println(F("User-Agent: ESP8266")); + ipClient.println(F("Accept: */*")); + ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); + ipClient.print(F("Content-Length: ")); + ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); // Total length of the request body + ipClient.println(); + ipClient.print(F("k=")); + ipClient.print(pushsaferKey); + ipClient.print(F("&m=")); + ipClient.print(encodedMessagePrefix); + ipClient.print(encodedMessageContent); + + // Waits for a response + unsigned long previousMillis = millis(); + while (!ipClient.available()) { + dsc.loop(); + if (millis() - previousMillis > 3000) { + Serial.println(); + Serial.println(F("Connection timed out waiting for a response.")); + ipClient.stop(); + return false; + } + } + + // Reads the response until the first space - the next characters will be the HTTP status code + while (ipClient.available()) { + if (ipClient.read() == ' ') break; + } + + // Checks the first character of the HTTP status code - the message was sent successfully if the status code + // begins with "2" + char statusCode = ipClient.read(); + + // Successful, reads the remaining response to clear the client buffer + if (statusCode == '2') { + while (ipClient.available()) ipClient.read(); + ipClient.stop(); + return true; + } + + // Unsuccessful, prints the response to serial to help debug + else { + Serial.println(); + Serial.println(F("Push notification error, response:")); + Serial.print(statusCode); + while (ipClient.available()) Serial.print((char)ipClient.read()); + Serial.println(); + ipClient.stop(); + return false; + } +} + + +void appendPartition(byte sourceNumber, char* messageContent) { + char partitionNumber[2]; + itoa(sourceNumber + 1, partitionNumber, 10); + strcat(messageContent, partitionNumber); +} + + +// Helper for encodeURL() +static char encodeHex(char c) { + return "0123456789ABCDEF"[c & 0x0F]; +} + + +// Encodes a char array to URL encoded using '+' for spaces as required for application/x-www-form-urlencoded +char *encodeURL(const char *src, char *dst) { + char c, *d = dst; + while (c = *src++) { + if (c == ' ') { + *d++ = '+'; + continue; + } + else if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9')) { + *d++ = '%'; + *d++ = encodeHex(c >> 4); + c = encodeHex(c); + } + *d++ = c; + } + *d = '\0'; + return dst; +} diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index 228a3cb..b78b467 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -54,10 +54,10 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard -const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard -const char* From = ""; // i.e. 16041234567 -const char* To = ""; // i.e. 16041234567 +const char* AccountSID = ""; // Set the account SID from the Twilio Account Dashboard +const char* AuthToken = ""; // Set the auth token from the Twilio Account Dashboard +const char* From = ""; // From phone number, starting with the country code without the + sign: 18005551234 +const char* To = ""; // To phone number, starting with the country code without the + sign: 18005551234 const char* messagePrefix = "[Security system] "; // Set a prefix for all messages // Configures the Keybus interface with the specified pins. @@ -102,7 +102,7 @@ WiFiClientSecure ipClient; bool wifiConnected = true; char twilioAuth[128]; size_t twilioAuthLength = 128; -char encodedTwilioAuth[128]; +char encodedTwilioAuth[128], encodedMessagePrefix[128], encodedMessageContent[480]; void setup() { @@ -121,7 +121,6 @@ void setup() { } Serial.print(F("connected: ")); Serial.println(WiFi.localIP()); - ipClient.setInsecure(); Serial.print(F("NTP time....")); configTime(0, 0, "pool.ntp.org"); @@ -134,11 +133,14 @@ void setup() { } Serial.println(F("synchronized.")); - // Sends a message on startup to verify connectivity + // Encodes authentication in base64 and message prefix in URL encoding strcat(twilioAuth, AccountSID); strcat(twilioAuth, ":"); strcat(twilioAuth, AuthToken); base64::encode(String(twilioAuth)).toCharArray(encodedTwilioAuth, 128); + encodeURL(messagePrefix, encodedMessagePrefix); + + // Sends a message on startup to verify connectivity Serial.print(F("Twilio....")); if (sendMessage("Initializing")) Serial.println(F("connected.")); else Serial.println(F("connection error.")); @@ -333,8 +335,8 @@ void loop() { bool sendMessage(const char* messageContent) { + encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding - // Connects and sends the message as x-www-form-urlencoded if (!ipClient.connect("api.twilio.com", 443)) return false; ipClient.print(F("POST https://api.twilio.com/2010-04-01/Accounts/")); ipClient.print(AccountSID); @@ -346,16 +348,16 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(To) + strlen(From) + strlen(messagePrefix) + strlen(messageContent) + 18); // Length including data + ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); // Length including data ipClient.println("Connection: Close"); ipClient.println(); - ipClient.print(F("To=+")); + ipClient.print(F("To=%2B")); ipClient.print(To); - ipClient.print(F("&From=+")); + ipClient.print(F("&From=%2B")); ipClient.print(From); ipClient.print(F("&Body=")); - ipClient.print(messagePrefix); - ipClient.println(messageContent); + ipClient.print(encodedMessagePrefix); + ipClient.print(encodedMessageContent); // Waits for a response unsigned long previousMillis = millis(); @@ -386,6 +388,7 @@ bool sendMessage(const char* messageContent) { // Unsuccessful, prints the response to serial to help debug else { + Serial.println(); Serial.println(F("SMS messaging error, response:")); Serial.print(statusCode); while (ipClient.available()) Serial.print((char)ipClient.read()); @@ -401,3 +404,31 @@ void appendPartition(byte sourceNumber, char* messageContent) { itoa(sourceNumber + 1, partitionNumber, 10); strcat(messageContent, partitionNumber); } + + +// Helper for encodeURL() +static char encodeHex(char c) { + return "0123456789ABCDEF"[c & 0x0F]; +} + + +// Encodes a char array to URL encoded using '+' for spaces as required for application/x-www-form-urlencoded +char *encodeURL(const char *src, char *dst) { + char c, *d = dst; + while (c = *src++) { + if (c == ' ') { + *d++ = '+'; + continue; + } + else if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9')) { + *d++ = '%'; + *d++ = encodeHex(c >> 4); + c = encodeHex(c); + } + *d++ = c; + } + *d = '\0'; + return dst; +} From ce1d5634ba007f1ae5eafffb8bd6a960cd725e5b Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 10 Feb 2022 11:09:13 +0300 Subject: [PATCH 37/49] Additional Telegram Bot example sketch workaround for Arduino/esp32 TLS issue --- README.md | 7 ++++--- examples/esp32/Email/Email.ino | 1 + examples/esp32/Pushbullet/Pushbullet.ino | 4 ++-- examples/esp32/Pushover/Pushover.ino | 3 +-- examples/esp32/Pushsafer/Pushsafer.ino | 4 ++-- examples/esp32/Telegram/Telegram.ino | 5 ++++- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 3 +-- examples/esp8266/Pushbullet/Pushbullet.ino | 2 +- examples/esp8266/Pushover/Pushover.ino | 4 +--- examples/esp8266/Pushsafer/Pushsafer.ino | 2 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 2 +- 11 files changed, 19 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c3cbe10..db3a170 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,9 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed - Updated: Added TLS root certificate to `Twilio-SMS` + - Bugfix: `VirtualKeypad-Web` updated notes to switch to [this fork of ESPAsyncWebServer](https://github.com/arjenhiemstra/ESPAsyncWebServer) to resolve crashes with iOS and macOS clients. - Bugfix: `Pushbullet` example sketch updated TLS security certificate fingerprint - - Bugfix: Workaround for [upstream esp32 TLS handshake issue](https://github.com/espressif/arduino-esp32/issues/6165) + - Bugfix: Workaround for [upstream esp32 TLS handshake issue](https://github.com/espressif/arduino-esp32/issues/6165) preventing making a TLS connection more than once. * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT @@ -244,9 +245,9 @@ The included examples demonstrate how to use the library and can be used as-is o * **VirtualKeypad-Web** (esp8266/esp32): Provides a virtual keypad web interface, using the esp8266/esp32 itself as a standalone web server, including viewing alarm memory, programming zone lights, and the event buffer. Thanks to [Elektrik1](https://github.com/Elektrik1) for contributing this example! -* **TimeSyncNTP**: Synchronizes and maintains the panel time via an NTP server, including DST adjustments. +* **TimeSyncNTP**: Synchronizes and maintains time on PowerSeries panels via an NTP server, including DST adjustments. -* **Unlocker**: Checks all possible 4-digit installer codes until a valid code is found, including handling keypad lockout if enabled. The valid code is output to serial as well as repeatedly flashed with the built-in LED. +* **Unlocker**: Finds the 4-digit installer code for PowerSeries panels by checking all possible codes, including handling keypad lockout if enabled. The valid code is output to serial as well as repeatedly flashed with the built-in LED. Arduino checks each code sequentially but esp8266/esp32 may find the code more quickly as they check in order of the [most commonly used general 4-digit codes](https://www.datagenetics.com/blog/september32012). * **KeypadInterface**: Interfaces directly to DSC PowerSeries and Classic series (tested with PC1500RK) keypads (without a DSC panel) to enable using these as physical inputs for any general purpose. Examples included for interfacing via serial and MQTT. Note that this uses a different wiring setup from the standard Keybus interface, refer to the wiring diagram in the example sketch. diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index 90a6f32..380bc93 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -201,6 +201,7 @@ void loop() { // server - the login and password must be base64 encoded. For example, on the macOS/Linux terminal: // $ echo -n 'mylogin@example.com' | base64 -w 0 bool sendMessage(const char* messageContent) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 if (!ipClient.connect("smtp.example.com", 465)) return false; // Set the SMTP server address - for example: smtp.gmail.com if(!smtpValidResponse()) return false; ipClient.println(F("HELO ESP32")); diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index d381412..d938ffa 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -330,7 +330,7 @@ void loop() { bool sendMessage(const char* messageContent) { ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 - // Connects and sends the message as JSON + // Connects and sends the message as a Pushbullet note-type push if (!ipClient.connect("api.pushbullet.com", 443)) return false; ipClient.println(F("POST /v2/pushes HTTP/1.1")); ipClient.println(F("Host: api.pushbullet.com")); @@ -338,7 +338,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); // Length including JSON data + ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); ipClient.print(F("Access-Token: ")); ipClient.println(pushbulletToken); ipClient.println(); diff --git a/examples/esp32/Pushover/Pushover.ino b/examples/esp32/Pushover/Pushover.ino index b613790..6a2b983 100644 --- a/examples/esp32/Pushover/Pushover.ino +++ b/examples/esp32/Pushover/Pushover.ino @@ -321,7 +321,6 @@ void loop() { bool sendMessage(const char* messageContent) { ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 - // Connects and sends the message as JSON if (!ipClient.connect("api.pushover.net", 443)) return false; ipClient.println(F("POST /1/messages.json HTTP/1.1")); ipClient.println(F("Host: api.pushover.net")); @@ -329,7 +328,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); // Length including JSON data + ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); ipClient.println(); ipClient.print(F("{\"token\":\"")); ipClient.print(pushoverAPIToken); diff --git a/examples/esp32/Pushsafer/Pushsafer.ino b/examples/esp32/Pushsafer/Pushsafer.ino index 64d33da..3ebadba 100644 --- a/examples/esp32/Pushsafer/Pushsafer.ino +++ b/examples/esp32/Pushsafer/Pushsafer.ino @@ -328,8 +328,8 @@ void loop() { bool sendMessage(const char* messageContent) { - encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 + encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding if (!ipClient.connect("www.pushsafer.com", 443)) return false; ipClient.println(F("POST /api HTTP/1.1")); @@ -338,7 +338,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); // Total length of the request body + ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); ipClient.println(); ipClient.print(F("k=")); ipClient.print(pushsaferKey); diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 839910e..3afa298 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -1,5 +1,5 @@ /* - * Telegram Bot 1.1 (esp32) + * Telegram Bot 1.2 (esp32) * * Processes the security system status and allows for control via a Telegram bot: https://www.telegram.org * @@ -27,6 +27,7 @@ * - Disarm: /disarm * * Release notes: + * 1.2 - Workaround for upstream esp32 TLS handshake issue https://github.com/espressif/arduino-esp32/issues/6165 * 1.1 - Added DSC Classic series support * 1.0 - Initial release * @@ -157,6 +158,8 @@ void loop() { // Checks for incoming Telegram messages static unsigned long telegramPreviousTime; if (millis() - telegramPreviousTime > telegramCheckInterval) { + ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 + byte telegramMessages = telegramBot.getUpdates(telegramBot.last_message_received + 1); while (telegramMessages) { handleTelegram(telegramMessages); diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 7003250..9df2179 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -333,7 +333,6 @@ bool sendMessage(const char* messageContent) { ipClient.setHandshakeTimeout(30); // Workaround for https://github.com/espressif/arduino-esp32/issues/6165 encodeURL(messageContent, encodedMessageContent); // Encodes message content in URL encoding - // Connects and sends the message as x-www-form-urlencoded if (!ipClient.connect("api.twilio.com", 443)) return false; ipClient.print(F("POST https://api.twilio.com/2010-04-01/Accounts/")); ipClient.print(AccountSID); @@ -345,7 +344,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); // Length including data + ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); ipClient.println("Connection: Close"); ipClient.println(); ipClient.print(F("To=%2B")); diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index 14899f8..dd77c9b 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -343,7 +343,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); // Length including JSON data + ipClient.println(strlen(messagePrefix) + strlen(messageContent) + 25); ipClient.print(F("Access-Token: ")); ipClient.println(pushbulletToken); ipClient.println(); diff --git a/examples/esp8266/Pushover/Pushover.ino b/examples/esp8266/Pushover/Pushover.ino index 80a79af..b18d7dc 100644 --- a/examples/esp8266/Pushover/Pushover.ino +++ b/examples/esp8266/Pushover/Pushover.ino @@ -319,8 +319,6 @@ void loop() { bool sendMessage(const char* messageContent) { - - // Connects and sends the message as JSON if (!ipClient.connect("api.pushover.net", 443)) return false; ipClient.println(F("POST /1/messages.json HTTP/1.1")); ipClient.println(F("Host: api.pushover.net")); @@ -328,7 +326,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/json")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); // Length including JSON data + ipClient.println(strlen(pushoverAPIToken) + strlen(pushoverUserKey) + strlen(messagePrefix) + strlen(messageContent) + 35); ipClient.println(); ipClient.print(F("{\"token\":\"")); ipClient.print(pushoverAPIToken); diff --git a/examples/esp8266/Pushsafer/Pushsafer.ino b/examples/esp8266/Pushsafer/Pushsafer.ino index 86cc349..9af6e87 100644 --- a/examples/esp8266/Pushsafer/Pushsafer.ino +++ b/examples/esp8266/Pushsafer/Pushsafer.ino @@ -338,7 +338,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); // Total length of the request body + ipClient.println(strlen(pushsaferKey) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 5); ipClient.println(); ipClient.print(F("k=")); ipClient.print(pushsaferKey); diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index b78b467..77da21b 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -348,7 +348,7 @@ bool sendMessage(const char* messageContent) { ipClient.println(F("Accept: */*")); ipClient.println(F("Content-Type: application/x-www-form-urlencoded")); ipClient.print(F("Content-Length: ")); - ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); // Length including data + ipClient.println(strlen(To) + strlen(From) + strlen(encodedMessagePrefix) + strlen(encodedMessageContent) + 21); ipClient.println("Connection: Close"); ipClient.println(); ipClient.print(F("To=%2B")); From 005e4f254c3ace146dcfe4709345bbc202fa4eed Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 10 Feb 2022 14:45:46 +0300 Subject: [PATCH 38/49] Fix access code entry on lower number partitions while higher number partitions are in exit delay #272 --- src/dscKeybusProcessData.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index 970ea46..c072497 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -256,7 +256,6 @@ void dscKeybusInterface::processPanelStatus() { // Exit delay in progress case 0x08: { writeArm[partitionIndex] = false; - accessCodePrompt = false; processExitDelayStatus(partitionIndex, true); From b9f5a807c8c408b467973e5ac16b99129c70e62b Mon Sep 17 00:00:00 2001 From: kricon Date: Fri, 11 Feb 2022 05:08:41 +0100 Subject: [PATCH 39/49] KeypadInterface updated to v1.4 --- .../KeypadInterface/KeypadInterface.ino | 39 ++++++++++++++++--- .../esp32/KeypadInterface/KeypadInterface.ino | 39 ++++++++++++++++--- .../KeypadInterface/KeypadInterface.ino | 39 ++++++++++++++++--- src/dscClassic.cpp | 2 +- src/dscKeybus.h | 1 + src/dscKeybusPrintData.cpp | 36 ++++++++++++----- 6 files changed, 131 insertions(+), 25 deletions(-) diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 0f018af..bc3490f 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.3a (Arduino) + * DSC Keypad Interface 1.4 (Arduino) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -28,7 +28,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.4 - Added ability to change LCD keypad messages * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone @@ -121,8 +121,9 @@ void loop() { */ if (inputReceived) { inputReceived = false; - if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support - else { //remove or comment this line for classic series support + #if defined(dscKeypad) + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); + else { for (byte i = 0; i < strlen(input); i++) { switch (input[i]) { case 'r': case 'R': dsc.lightReady = setLight(); break; @@ -149,7 +150,35 @@ void loop() { default: break; } } - } //remove or comment this line for classic series support + } + #else + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + #endif } dsc.loop(); diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index dfe8f56..1b7bd4c 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.3a (esp32) + * DSC Keypad Interface 1.4 (esp32) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -28,7 +28,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.4 - Added ability to change LCD keypad messages * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone @@ -121,8 +121,9 @@ void loop() { */ if (inputReceived) { inputReceived = false; - if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support - else { //remove or comment this line for classic series support + #if defined(dscKeypad) + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); + else { for (byte i = 0; i < strlen(input); i++) { switch (input[i]) { case 'r': case 'R': dsc.lightReady = setLight(); break; @@ -149,7 +150,35 @@ void loop() { default: break; } } - } //remove or comment this line for classic series support + } + #else + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + #endif } dsc.loop(); diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 1ce3b28..13b3db5 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -1,5 +1,5 @@ /* - * DSC Keypad Interface 1.3a (esp8266) + * DSC Keypad Interface 1.4 (esp8266) * * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to * enable use of DSC keypads as physical inputs for any general purpose. @@ -28,7 +28,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3a- Added ability to change LCD keypad messages - WOULD NOT COMPILE FOR CLASSIC SERIES, search for "remove" + * 1.4 - Added ability to change LCD keypad messages * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone @@ -121,8 +121,9 @@ void loop() { */ if (inputReceived) { inputReceived = false; - if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); //remove or comment this line for classic series support - else { //remove or comment this line for classic series support + #if defined(dscKeypad) + if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); + else { for (byte i = 0; i < strlen(input); i++) { switch (input[i]) { case 'r': case 'R': dsc.lightReady = setLight(); break; @@ -149,7 +150,35 @@ void loop() { default: break; } } - } //remove or comment this line for classic series support + } + #else + for (byte i = 0; i < strlen(input); i++) { + switch (input[i]) { + case 'r': case 'R': dsc.lightReady = setLight(); break; + case 'a': case 'A': dsc.lightArmed = setLight(); break; + case 'm': case 'M': dsc.lightMemory = setLight(); break; + case 'y': case 'Y': dsc.lightBypass = setLight(); break; + case 't': case 'T': dsc.lightTrouble = setLight(); break; + case 'p': case 'P': dsc.lightProgram = setLight(); break; + case 'f': case 'F': dsc.lightFire = setLight(); break; + case 'l': case 'L': dsc.lightBacklight = setLight(); break; + case '1': dsc.lightZone1 = setLight(); break; + case '2': dsc.lightZone2 = setLight(); break; + case '3': dsc.lightZone3 = setLight(); break; + case '4': dsc.lightZone4 = setLight(); break; + case '5': dsc.lightZone5 = setLight(); break; + case '6': dsc.lightZone6 = setLight(); break; + case '7': dsc.lightZone7 = setLight(); break; + case '8': dsc.lightZone8 = setLight(); break; + case 'b': case 'B': sendBeeps(i); i += beepLength; break; + case 'n': case 'N': sendTone(i); i+= toneLength; break; + case 'z': case 'Z': sendBuzzer(i); i+= buzzerLength; break; + case '-': lightOff = true; break; + case '!': lightBlink = true; break; + default: break; + } + } + #endif } dsc.loop(); diff --git a/src/dscClassic.cpp b/src/dscClassic.cpp index 470f82a..570a957 100644 --- a/src/dscClassic.cpp +++ b/src/dscClassic.cpp @@ -863,7 +863,7 @@ void dscClassicInterface::printPanelMessage() { stream->print(F("| Status: ")); if (pc16Data[1]) { if (bitRead(pc16Data[1], 7)) stream->print(F("Trouble ")); - if (bitRead(pc16Data[1], 6)) stream->print(F("Bypassed zones ")); + if (bitRead(pc16Data[1], 6)) stream->print(F("Bypassed zones ")); if (bitRead(pc16Data[1], 5)) stream->print(F("Armed (side A) ")); if (bitRead(pc16Data[1], 4)) stream->print(F("Armed (side B) ")); if (bitRead(pc16Data[1], 3)) stream->print(F("Keypad Panic alarm ")); diff --git a/src/dscKeybus.h b/src/dscKeybus.h index bdebcee..2c32118 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -287,6 +287,7 @@ class dscKeybusInterface { void printModule_KeyCodes(byte keyByte); void printModule_Expander(); bool printModuleSlots(byte startCount, byte startByte, byte endByte, byte startMask, byte endMask, byte bitShift, byte matchValue, bool reverse = false); + void printModuleProgramming(byte panelByte2, byte panelByte3); bool validCRC(); void writeKeys(const char * writeKeysArray); diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index e3a25be..44b6ad1 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -1921,7 +1921,8 @@ void dscKeybusInterface::printPanel_0x87() { */ /* void dscKeybusInterface::printPanel_0x8D() { - stream->print(F("Module programming entry")); + stream->print(F("Module programming entry: ")); + printModuleProgramming(panelData[2], panelData[3]); } */ @@ -1949,7 +1950,8 @@ void dscKeybusInterface::printPanel_0x8D() { */ /* void dscKeybusInterface::printPanel_0x94() { - stream->print(F("Module programming request")); + stream->print(F("Module programming request: ")); + printModuleProgramming(panelData[2], panelData[3]); } */ @@ -2161,14 +2163,12 @@ void dscKeybusInterface::printPanel_0xBB() { */ void dscKeybusInterface::printPanel_0xC3() { if (panelData[3] == 0xFF) { + stream->print(F("TLM: ")); + if (panelData[2] & 0x10) stream->print(F("trouble/attempt")); + else stream->print(F("available/disabled")); - - stream->print(F("TLM: ")); - if (panelData[2] & 0x10) stream->print(F("trouble/attempt")); - else stream->print(F("available/disabled")); - - if (panelData[2] & 0x08) stream->print(F(" | Dialer call attempt")); - if (panelData[2] & 0x20) stream->print(F(" | Keypad lockout")); + if (panelData[2] & 0x08) stream->print(F(" | Dialer call attempt")); + if (panelData[2] & 0x20) stream->print(F(" | Keypad lockout")); } else printUnknownData(); } @@ -3734,6 +3734,24 @@ bool dscKeybusInterface::printModuleSlots(byte outputNumber, byte startByte, byt } +// Print 0x8D and 0x94 section and command subsection data used for programming modules +void dscKeybusInterface::printModuleProgramming(byte panelByte2, byte panelByte3) { + switch (panelByte2) { + case 0x11: stream->print(F("RF5132")); break; //section 804 verified on pc1832 and pc5020 + case 0x14: stream->print(F("RF5400")); break; //section 801 not verified + case 0x15: stream->print(F("RF5936")); break; //section 802 not verified + case 0x16: stream->print(F("LINKS2X50")); break; //section 803 not verified + case 0x17: stream->print(F("PC5108L")); break; //section 806 not verified + case 0x19: stream->print(F("RF5100")); break; //section 805 not verified + case 0x31: stream->print(F("*5 user")); break; //*5 access codes verified on pc1832 and pc5020 + default: stream->print("Unknown data"); + } + stream->print(" | "); + if (panelByte3 < 16) stream->print("0"); + stream->print(panelByte3, HEX); +} + + /* * Panel lights and status message for commands: 0x05, 0x1B, 0x27, 0x2D, 0x34, 0x3E */ From ce1761daccdf0c6be2325bc8b74edcd518c86bb0 Mon Sep 17 00:00:00 2001 From: kricon Date: Fri, 11 Feb 2022 07:39:04 +0100 Subject: [PATCH 40/49] Reverted module programming messages (with additional info for ESP) --- .../KeypadInterface/KeypadInterface.ino | 3 ++- .../esp32/KeypadInterface/KeypadInterface.ino | 3 ++- .../KeypadInterface/KeypadInterface.ino | 3 ++- src/dscKeybusPrintData.cpp | 26 ++++++++++++------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 83b69a8..115eddf 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -29,7 +29,7 @@ * * Release notes: * 1.4 - Added ability to change LCD keypad messages - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -121,6 +121,7 @@ void loop() { */ if (inputReceived) { inputReceived = false; + #if defined(dscKeypad) if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); else { diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index 886bf27..f19861c 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -29,7 +29,7 @@ * * Release notes: * 1.4 - Added ability to change LCD keypad messages - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -121,6 +121,7 @@ void loop() { */ if (inputReceived) { inputReceived = false; + #if defined(dscKeypad) if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); else { diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 117336f..a5a4414 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -29,7 +29,7 @@ * * Release notes: * 1.4 - Added ability to change LCD keypad messages - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release @@ -121,6 +121,7 @@ void loop() { */ if (inputReceived) { inputReceived = false; + #if defined(dscKeypad) if (String(input).startsWith("0x")) dsc.panelCommand05[2] = strtoul(input, NULL, 16); else { diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index 7d218a1..eb5d7e4 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -102,8 +102,8 @@ void dscKeybusInterface::printPanelMessage() { case 0x7F: printPanel_0x7F(); return; // Buzzer, partition 1 | Structure: complete | Content: complete case 0x82: printPanel_0x82(); return; // Buzzer, partition 2 | Structure: complete | Content: complete case 0x87: printPanel_0x87(); return; // PGM outputs | Structure: complete | Content: complete -// case 0x8D: printPanel_0x8D(); return; // RF module programming - it sends data from panel to RF module after programming entry is done | Structure: *incomplete | Content: *incomplete -// case 0x94: printPanel_0x94(); return; // Requesting and getting data from RF module to the panel | Structure: *incomplete | Content: *incomplete + case 0x8D: printPanel_0x8D(); return; // RF module programming - it sends data from panel to RF module after programming entry is done | Structure: *incomplete | Content: *incomplete + case 0x94: printPanel_0x94(); return; // Requesting and getting data from RF module to the panel | Structure: *incomplete | Content: *incomplete case 0x9E: printPanel_0x9E(); return; // DLS query | Structure: complete | Content: complete case 0xA5: printPanel_0xA5(); return; // Date, time, system status messages - partitions 1-2 | Structure: *incomplete | Content: *incomplete case 0xAA: printPanel_0xAA(); return; // Event buffer messages | Structure: complete | Content: *incomplete @@ -157,7 +157,7 @@ void dscKeybusInterface::printModuleMessage() { case 0x57: printModule_0x57(); return; // Wireless key query response | Structure: *incomplete | Content: *incomplete case 0x58: printModule_0x58(); return; // Module status query response | Structure: *incomplete | Content: *incomplete case 0x70: printModule_0x70(); return; // LCD keypad data entry | Structure: complete | Content: complete -// case 0x94: printModule_0x94(); return; // Module programming response | Structure: *incomplete | Content: *incomplete + case 0x94: printModule_0x94(); return; // Module programming response | Structure: *incomplete | Content: *incomplete case 0xD5: printModule_0xD5(); return; // Keypad zone query response | Structure: *incomplete | Content: *incomplete case 0x22: case 0x28: @@ -1921,12 +1921,16 @@ void dscKeybusInterface::printPanel_0x87() { * 10001101 0 00010001 00111001 00000000 00000111 11111111 11111111 11111111 11011011 [0x8D] Wls programming key response // Set RF jamming zone 07 in [804][93] subsection * Byte 0 1 2 3 4 5 6 7 8 9 */ -/* + void dscKeybusInterface::printPanel_0x8D() { + #if !defined(__AVR__) stream->print(F("Module programming entry: ")); printModuleProgramming(panelData[2], panelData[3]); + #else + stream->print(F("Module programming entry")); + #endif } -*/ + /* * 0x94: Used to request programming data from modules @@ -1950,12 +1954,16 @@ void dscKeybusInterface::printPanel_0x8D() { * 10010100 0 00010001 00000000 00000000 10100101 00000000 00000000 00000000 01001100 11111100 [0x94] Unknown data * Byte 0 1 2 3 4 5 6 7 8 9 10 */ -/* + void dscKeybusInterface::printPanel_0x94() { + #if !defined(__AVR__) stream->print(F("Module programming request: ")); printModuleProgramming(panelData[2], panelData[3]); + #else + stream->print(F("Module programming request")); + #endif } -*/ + /* * 0x9E: DLS query @@ -3455,11 +3463,11 @@ void dscKeybusInterface::printModule_0x70() { * Structure decoding: *incomplete * Content decoding: *incomplete */ -/* + void dscKeybusInterface::printModule_0x94() { stream->print(F("Module programming response")); } -*/ + /* * Module data during panel command: 0xD5 Keypad zone query From 5cddb829667fc4364997303f4165844300d78d6f Mon Sep 17 00:00:00 2001 From: kricon Date: Fri, 11 Feb 2022 08:00:29 +0100 Subject: [PATCH 41/49] Added PC2550 and PC1565-2P to tested list on readme Also updated release notes of KeypadInterface-MQTT examples --- README.md | 6 +++--- .../Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 2 +- .../esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 2 +- .../esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino | 2 +- src/dscKeybusPrintData.cpp | 3 --- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index db3a170..aec24a0 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,10 @@ This library uses a combination of hardware and timer interrupts to accurately c - Extensive data decoding: the majority of Keybus data as seen in the [DSC IT-100 Data Interface developer's guide](https://cms.dsc.com/download.php?t=1&id=16238) has been reverse engineered and documented in [`src/dscKeybusPrintData.cpp`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybusPrintData.cpp). - Non-blocking code: Allows sketches to run as quickly as possible without using `delay` or `delayMicroseconds` * Supported security systems: - - [DSC PowerSeries](https://www.dsc.com/?n=enduser&o=identify) - all panels are supported, tested with: PC585, PC1555MX, PC1565, PC5005, PC5010, PC5015, PC5020, PC1616, PC1808, PC1832, PC1864 - - [DSC Classic series](https://www.dsc.com/?n=enduser&o=identify): PC1500, PC1550 + - [DSC PowerSeries](https://www.dsc.com/?n=enduser&o=identify) - all panels are supported, tested with: PC585, PC1555MX, PC1565, PC1565-2P, PC5005, PC5010, PC5015, PC5020, PC1616, PC1808, PC1832, PC1864 + - [DSC Classic series](https://www.dsc.com/?n=enduser&o=identify): PC1500, PC1550, PC2550 * Requires configuring the panel through *8 programming to enable PC16-OUT: section 19, option 4. - * PC2500, PC2550, PC3000 are untested, [post an issue](https://github.com/taligentx/dscKeybusInterface/issues) if you're able to test these panels. + * PC2500 and PC3000 are untested, [post an issue](https://github.com/taligentx/dscKeybusInterface/issues) if you're able to test these panels. - Rebranded DSC PowerSeries (such as some ADT systems) should also work with this interface. * Unsupported security systems: - DSC Alexor (PC9155) is all wireless and does not have an accessible Keybus interface. diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 1b3bbeb..0e19b27 100644 --- a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -23,7 +23,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 3db0ba1..c3c60a4 100644 --- a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -23,7 +23,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index c187c79..232ada7 100644 --- a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -23,7 +23,7 @@ * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Fire, Program, Zones 1-8: dsc.lightReady, dsc.lightZone1, etc * * Release notes: - * 1.3 - Added support for Program, Fire, Zone 7 and 8 lights on CLassic keypad PC2550RK + * 1.3 - Add Classic keypad support - PC2550RK * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone * 1.0 - Initial release diff --git a/src/dscKeybusPrintData.cpp b/src/dscKeybusPrintData.cpp index eb5d7e4..f634b6c 100644 --- a/src/dscKeybusPrintData.cpp +++ b/src/dscKeybusPrintData.cpp @@ -1921,7 +1921,6 @@ void dscKeybusInterface::printPanel_0x87() { * 10001101 0 00010001 00111001 00000000 00000111 11111111 11111111 11111111 11011011 [0x8D] Wls programming key response // Set RF jamming zone 07 in [804][93] subsection * Byte 0 1 2 3 4 5 6 7 8 9 */ - void dscKeybusInterface::printPanel_0x8D() { #if !defined(__AVR__) stream->print(F("Module programming entry: ")); @@ -1954,7 +1953,6 @@ void dscKeybusInterface::printPanel_0x8D() { * 10010100 0 00010001 00000000 00000000 10100101 00000000 00000000 00000000 01001100 11111100 [0x94] Unknown data * Byte 0 1 2 3 4 5 6 7 8 9 10 */ - void dscKeybusInterface::printPanel_0x94() { #if !defined(__AVR__) stream->print(F("Module programming request: ")); @@ -3463,7 +3461,6 @@ void dscKeybusInterface::printModule_0x70() { * Structure decoding: *incomplete * Content decoding: *incomplete */ - void dscKeybusInterface::printModule_0x94() { stream->print(F("Module programming response")); } From 8fa9790fada5650763e3f72e3c5d6ad8478740e2 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Wed, 23 Feb 2022 17:08:37 +0300 Subject: [PATCH 42/49] Enable writing access code automatically if needed when writing command output keys --- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 4 +-- .../Homebridge-MQTT/Homebridge-MQTT.ino | 4 +-- .../Arduino/KeybusReader/KeybusReader.ino | 2 +- .../Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 4 +-- examples/Arduino/Status/Status.ino | 2 +- examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino | 2 +- examples/esp32/Email/Email.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 4 +-- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 4 +-- examples/esp32/Homey/Homey.ino | 2 +- examples/esp32/KeybusReader/KeybusReader.ino | 2 +- .../esp32/KeybusReaderIP/KeybusReaderIP.ino | 2 +- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 4 +-- examples/esp32/Pushbullet/Pushbullet.ino | 2 +- examples/esp32/Pushover/Pushover.ino | 2 +- examples/esp32/Pushsafer/Pushsafer.ino | 2 +- examples/esp32/Status/Status.ino | 2 +- examples/esp32/Telegram/Telegram.ino | 4 +-- examples/esp32/TimeSyncNTP/TimeSyncNTP.ino | 2 +- examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino | 2 +- examples/esp32/Twilio-SMS/Twilio-SMS.ino | 2 +- .../VirtualKeypad-Blynk.ino | 2 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 2 +- examples/esp8266/Email/Email.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 4 +-- .../Homebridge-MQTT/Homebridge-MQTT.ino | 4 +-- examples/esp8266/Homey/Homey.ino | 2 +- .../esp8266/KeybusReader/KeybusReader.ino | 2 +- .../esp8266/KeybusReaderIP/KeybusReaderIP.ino | 2 +- .../esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 4 +-- examples/esp8266/Pushbullet/Pushbullet.ino | 2 +- examples/esp8266/Pushover/Pushover.ino | 2 +- examples/esp8266/Pushsafer/Pushsafer.ino | 2 +- examples/esp8266/Status/Status.ino | 2 +- examples/esp8266/Telegram/Telegram.ino | 4 +-- examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino | 2 +- examples/esp8266/Twilio-SMS/Twilio-SMS.ino | 2 +- .../VirtualKeypad-Blynk.ino | 2 +- .../VirtualKeypad-Web/VirtualKeypad-Web.ino | 2 +- library.properties | 2 +- src/dscClassic.h | 4 ++- src/dscKeybus.h | 2 +- src/dscKeybusInterface.cpp | 34 +++++++++---------- src/dscKeybusInterface.h | 2 -- src/dscKeybusProcessData.cpp | 13 ++++--- 45 files changed, 81 insertions(+), 76 deletions(-) diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index cf05807..55b273b 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -211,7 +211,7 @@ entity: alarm_control_panel.security_partition_1 // Settings byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -282,7 +282,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index 84bae91..d541bd1 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -203,7 +203,7 @@ // Settings byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -271,7 +271,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/Arduino/KeybusReader/KeybusReader.ino b/examples/Arduino/KeybusReader/KeybusReader.ino index 3f2e78c..99c48f1 100644 --- a/examples/Arduino/KeybusReader/KeybusReader.ino +++ b/examples/Arduino/KeybusReader/KeybusReader.ino @@ -104,7 +104,7 @@ void loop() { } // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index d74b7a0..503bd45 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -132,7 +132,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} // Settings byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -203,7 +203,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/Arduino/Status/Status.ino b/examples/Arduino/Status/Status.ino index 57fb0f3..4be4c37 100644 --- a/examples/Arduino/Status/Status.ino +++ b/examples/Arduino/Status/Status.ino @@ -91,7 +91,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino index c23f60a..411adbf 100644 --- a/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino @@ -147,7 +147,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Email/Email.ino b/examples/esp32/Email/Email.ino index 380bc93..da44824 100644 --- a/examples/esp32/Email/Email.ino +++ b/examples/esp32/Email/Email.ino @@ -112,7 +112,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 007fcd5..ef6e026 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -210,7 +210,7 @@ entity: alarm_control_panel.security_partition_1 // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -283,7 +283,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index 014e8c5..e0d9065 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -199,7 +199,7 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -268,7 +268,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Homey/Homey.ino b/examples/esp32/Homey/Homey.ino index a81f1de..0501007 100755 --- a/examples/esp32/Homey/Homey.ino +++ b/examples/esp32/Homey/Homey.ino @@ -140,7 +140,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/KeybusReader/KeybusReader.ino b/examples/esp32/KeybusReader/KeybusReader.ino index 13d02ae..093a358 100644 --- a/examples/esp32/KeybusReader/KeybusReader.ino +++ b/examples/esp32/KeybusReader/KeybusReader.ino @@ -104,7 +104,7 @@ void loop() { } // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino index 3edbbd9..53b363d 100644 --- a/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp32/KeybusReaderIP/KeybusReaderIP.ino @@ -161,7 +161,7 @@ void loop() { if (dsc.loop()) { // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { ipClient.print(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index fb9b748..21dca44 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -140,7 +140,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -213,7 +213,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Pushbullet/Pushbullet.ino b/examples/esp32/Pushbullet/Pushbullet.ino index d938ffa..c29d2d2 100644 --- a/examples/esp32/Pushbullet/Pushbullet.ino +++ b/examples/esp32/Pushbullet/Pushbullet.ino @@ -165,7 +165,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Pushover/Pushover.ino b/examples/esp32/Pushover/Pushover.ino index 6a2b983..c27d490 100644 --- a/examples/esp32/Pushover/Pushover.ino +++ b/examples/esp32/Pushover/Pushover.ino @@ -156,7 +156,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Pushsafer/Pushsafer.ino b/examples/esp32/Pushsafer/Pushsafer.ino index 3ebadba..10d34ca 100644 --- a/examples/esp32/Pushsafer/Pushsafer.ino +++ b/examples/esp32/Pushsafer/Pushsafer.ino @@ -165,7 +165,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Status/Status.ino b/examples/esp32/Status/Status.ino index e1d1971..da18c26 100644 --- a/examples/esp32/Status/Status.ino +++ b/examples/esp32/Status/Status.ino @@ -90,7 +90,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 3afa298..812be0e 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -79,7 +79,7 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm (based on panel configuration) +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* telegramBotToken = ""; // Set the Telegram bot access token const char* telegramUserID = ""; // Set the Telegram chat user ID const char* messagePrefix = "[Security system] "; // Set a prefix for all messages @@ -174,7 +174,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino index fb7e434..d2da322 100644 --- a/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp32/TimeSyncNTP/TimeSyncNTP.ino @@ -136,7 +136,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino index 6f90fc4..c55a9fc 100644 --- a/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino @@ -132,7 +132,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/Twilio-SMS/Twilio-SMS.ino b/examples/esp32/Twilio-SMS/Twilio-SMS.ino index 9df2179..a6e8c61 100644 --- a/examples/esp32/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp32/Twilio-SMS/Twilio-SMS.ino @@ -167,7 +167,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index 45dfd86..19eb0e7 100644 --- a/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -276,7 +276,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino index 403d703..632e36b 100644 --- a/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -175,7 +175,7 @@ void loop() { dsc.statusChanged = false; // Resets the status flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Email/Email.ino b/examples/esp8266/Email/Email.ino index 6a52bd0..3083146 100644 --- a/examples/esp8266/Email/Email.ino +++ b/examples/esp8266/Email/Email.ino @@ -118,7 +118,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index bf7af77..0543566 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -219,7 +219,7 @@ entity: alarm_control_panel.security_partition_1 // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -292,7 +292,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index f944a7f..b6f27e9 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -205,7 +205,7 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -274,7 +274,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Homey/Homey.ino b/examples/esp8266/Homey/Homey.ino index 2cf60a5..9f00f33 100755 --- a/examples/esp8266/Homey/Homey.ino +++ b/examples/esp8266/Homey/Homey.ino @@ -143,7 +143,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/KeybusReader/KeybusReader.ino b/examples/esp8266/KeybusReader/KeybusReader.ino index f87c69b..01bbce5 100644 --- a/examples/esp8266/KeybusReader/KeybusReader.ino +++ b/examples/esp8266/KeybusReader/KeybusReader.ino @@ -105,7 +105,7 @@ void loop() { } // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino index ed92d8d..7047dc3 100644 --- a/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino +++ b/examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino @@ -164,7 +164,7 @@ void loop() { if (dsc.loop()) { // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { ipClient.print(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index d24467d..b43198d 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -141,7 +141,7 @@ Contact zone3 "Zone 3" {channel="mqtt:topic:mymqtt:dsc:zone3"} // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration. +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* mqttServer = ""; // MQTT server domain name or IP address const int mqttPort = 1883; // MQTT server port const char* mqttUsername = ""; // Optional, leave blank if not required @@ -214,7 +214,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Pushbullet/Pushbullet.ino b/examples/esp8266/Pushbullet/Pushbullet.ino index dd77c9b..88395a6 100644 --- a/examples/esp8266/Pushbullet/Pushbullet.ino +++ b/examples/esp8266/Pushbullet/Pushbullet.ino @@ -171,7 +171,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Pushover/Pushover.ino b/examples/esp8266/Pushover/Pushover.ino index b18d7dc..6d8c1c0 100644 --- a/examples/esp8266/Pushover/Pushover.ino +++ b/examples/esp8266/Pushover/Pushover.ino @@ -156,7 +156,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Pushsafer/Pushsafer.ino b/examples/esp8266/Pushsafer/Pushsafer.ino index 9af6e87..91e18b0 100644 --- a/examples/esp8266/Pushsafer/Pushsafer.ino +++ b/examples/esp8266/Pushsafer/Pushsafer.ino @@ -166,7 +166,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Status/Status.ino b/examples/esp8266/Status/Status.ino index 752a892..1d4ccc8 100644 --- a/examples/esp8266/Status/Status.ino +++ b/examples/esp8266/Status/Status.ino @@ -92,7 +92,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index e08a341..de55816 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -78,7 +78,7 @@ // Settings const char* wifiSSID = ""; const char* wifiPassword = ""; -const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm (based on panel configuration) +const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. const char* telegramBotToken = ""; // Set the Telegram bot access token const char* telegramUserID = ""; // Set the Telegram chat user ID const char* messagePrefix = "[Security system] "; // Set a prefix for all messages @@ -173,7 +173,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino index cdecba5..59e3fc7 100644 --- a/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino +++ b/examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino @@ -140,7 +140,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino index 77da21b..e817db8 100644 --- a/examples/esp8266/Twilio-SMS/Twilio-SMS.ino +++ b/examples/esp8266/Twilio-SMS/Twilio-SMS.ino @@ -172,7 +172,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino index b0351b5..9a7e12d 100644 --- a/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino +++ b/examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino @@ -278,7 +278,7 @@ void loop() { dsc.statusChanged = false; // Reset the status tracking flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino index 4f5c5db..7601edc 100644 --- a/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino +++ b/examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino @@ -184,7 +184,7 @@ void loop() { dsc.statusChanged = false; // Resets the status flag // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call - // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + // loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h if (dsc.bufferOverflow) { Serial.println(F("Keybus buffer overflow")); dsc.bufferOverflow = false; diff --git a/library.properties b/library.properties index 43e47c2..5d060f9 100644 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ sentence=Directly interface Arduino, esp8266, and esp32 microcontrollers to DSC paragraph=Includes examples to monitor armed/alarm/zone/fire/trouble status, integrate with Homebridge (Apple HomeKit, Google Home) and Home Assistant via MQTT, send email and push notifications via Telegram and Pushbullet, and decode the Keybus protocol. category=Device Control url=https://github.com/taligentx/dscKeybusInterface -architectures=* +architectures=avr,esp8266,esp32 includes=dscKeybusInterface.h diff --git a/src/dscClassic.h b/src/dscClassic.h index 2c4c5bb..81e1f98 100644 --- a/src/dscClassic.h +++ b/src/dscClassic.h @@ -28,8 +28,10 @@ const byte dscReadSize = 2; // Maximum bytes of a Keybus command #if defined(__AVR__) const byte dscBufferSize = 10; // Number of commands to buffer if the sketch is busy - requires dscReadSize + 2 bytes of memory per command -#elif defined(ESP8266) || defined(ESP32) +#elif defined(ESP8266) const byte dscBufferSize = 50; +#elif defined(ESP32) +const DRAM_ATTR byte dscBufferSize = 50; #endif // Exit delay target states diff --git a/src/dscKeybus.h b/src/dscKeybus.h index 31558c1..cededad 100644 --- a/src/dscKeybus.h +++ b/src/dscKeybus.h @@ -301,7 +301,7 @@ class dscKeybusInterface { Stream* stream; const char* writeKeysArray; bool writeKeysPending; - bool writeArm[dscPartitions]; + bool writeAccessCode[dscPartitions]; bool queryResponse; bool previousTrouble; bool previousKeybus; diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 6f729d8..9844ce0 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -349,23 +349,23 @@ void dscKeybusInterface::setWriteKey(const char receivedKey) { case '9': writeKey = 0x27; break; case '*': writeKey = 0x28; if (status[writePartition - 1] < 0x9E) starKeyCheck = true; break; case '#': writeKey = 0x2D; break; - case 'f': case 'F': writeKey = 0xBB; writeAlarm = true; break; // Keypad fire alarm - case 'b': case 'B': writeKey = 0x82; break; // Enter event buffer - case '>': writeKey = 0x87; break; // Event buffer right arrow - case '<': writeKey = 0x88; break; // Event buffer left arrow - case 'l': case 'L': writeKey = 0xA5; break; // LCD keypad data request - case 's': case 'S': writeKey = 0xAF; writeArm[writePartition - 1] = true; break; // Arm stay - case 'w': case 'W': writeKey = 0xB1; writeArm[writePartition - 1] = true; break; // Arm away - case 'n': case 'N': writeKey = 0xB6; writeArm[writePartition - 1] = true; break; // Arm with no entry delay (night arm) - case 'a': case 'A': writeKey = 0xDD; writeAlarm = true; break; // Keypad auxiliary alarm - case 'c': case 'C': writeKey = 0xBB; break; // Door chime - case 'r': case 'R': writeKey = 0xDA; break; // Reset - case 'p': case 'P': writeKey = 0xEE; writeAlarm = true; break; // Keypad panic alarm - case 'x': case 'X': writeKey = 0xE1; break; // Exit - case '[': writeKey = 0xD5; break; // Command output 1 - case ']': writeKey = 0xDA; break; // Command output 2 - case '{': writeKey = 0x70; break; // Command output 3 - case '}': writeKey = 0xEC; break; // Command output 4 + case 'f': case 'F': writeKey = 0xBB; writeAlarm = true; break; // Keypad fire alarm + case 'b': case 'B': writeKey = 0x82; break; // Enter event buffer + case '>': writeKey = 0x87; break; // Event buffer right arrow + case '<': writeKey = 0x88; break; // Event buffer left arrow + case 'l': case 'L': writeKey = 0xA5; break; // LCD keypad data request + case 's': case 'S': writeKey = 0xAF; writeAccessCode[writePartition - 1] = true; break; // Arm stay + case 'w': case 'W': writeKey = 0xB1; writeAccessCode[writePartition - 1] = true; break; // Arm away + case 'n': case 'N': writeKey = 0xB6; writeAccessCode[writePartition - 1] = true; break; // Arm with no entry delay (night arm) + case 'a': case 'A': writeKey = 0xDD; writeAlarm = true; break; // Keypad auxiliary alarm + case 'c': case 'C': writeKey = 0xBB; break; // Door chime + case 'r': case 'R': writeKey = 0xDA; break; // Reset + case 'p': case 'P': writeKey = 0xEE; writeAlarm = true; break; // Keypad panic alarm + case 'x': case 'X': writeKey = 0xE1; break; // Exit + case '[': writeKey = 0xD5; writeAccessCode[writePartition - 1] = true; break; // Command output 1 + case ']': writeKey = 0xDA; writeAccessCode[writePartition - 1] = true; break; // Command output 2 + case '{': writeKey = 0x70; writeAccessCode[writePartition - 1] = true; break; // Command output 3 + case '}': writeKey = 0xEC; writeAccessCode[writePartition - 1] = true; break; // Command output 4 default: { validKey = false; break; diff --git a/src/dscKeybusInterface.h b/src/dscKeybusInterface.h index 564a1a1..0715e2c 100644 --- a/src/dscKeybusInterface.h +++ b/src/dscKeybusInterface.h @@ -83,7 +83,6 @@ ISR(TIMER1_OVF_vect) { // DSC Keypad Interface #elif defined dscKeypad - #include "dscKeypad.h" byte dscKeypadInterface::dscClockPin; @@ -119,7 +118,6 @@ ISR(TIMER1_OVF_vect) { // DSC Classic Keypad Interface #elif defined dscClassicKeypad - #include "dscClassicKeypad.h" byte dscClassicKeypadInterface::dscClockPin; diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index c072497..c8dcd25 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -236,7 +236,7 @@ void dscKeybusInterface::processPanelStatus() { armedAway[partitionIndex] = true; } - writeArm[partitionIndex] = false; + writeAccessCode[partitionIndex] = false; armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex] || armedStay[partitionIndex] != previousArmedStay[partitionIndex]) { @@ -255,7 +255,7 @@ void dscKeybusInterface::processPanelStatus() { // Exit delay in progress case 0x08: { - writeArm[partitionIndex] = false; + writeAccessCode[partitionIndex] = false; processExitDelayStatus(partitionIndex, true); @@ -356,8 +356,8 @@ void dscKeybusInterface::processPanelStatus() { // Enter access code case 0x9F: { - if (writeArm[partitionIndex]) { // Ensures access codes are only sent when an arm command is sent through this interface - writeArm[partitionIndex] = false; + if (writeAccessCode[partitionIndex]) { // Ensures access codes are only sent when an arm or command output key is sent through this interface + writeAccessCode[partitionIndex] = false; accessCodePrompt = true; if (!pauseStatus) statusChanged = true; } @@ -476,6 +476,11 @@ void dscKeybusInterface::processPanel_0x3E() { void dscKeybusInterface::processPanel_0x87() { if (!validCRC()) return; + // Resets flag to write access code if needed when writing command output keys + for (byte partitionIndex = 0; partitionIndex < dscPartitions; partitionIndex++) { + writeAccessCode[partitionIndex] = false; + } + pgmOutputs[0] = panelData[3] & 0x03; pgmOutputs[0] |= panelData[2] << 2; pgmOutputs[1] = panelData[2] >> 6; From 9c8b856dd086918801ea4250e83111de6ac6f063 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Thu, 24 Feb 2022 10:35:12 +0300 Subject: [PATCH 43/49] Add HomeKit-HomeSpan example sketch --- README.md | 30 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- .../Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- .../KeypadInterface-MQTT.ino | 13 +- .../KeypadInterface/KeypadInterface.ino | 12 +- .../Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- .../HomeKit-HomeSpan/HomeKit-HomeSpan.ino | 217 +++++++++ .../HomeKit-HomeSpan/dscHomeSpanAccessories.h | 449 ++++++++++++++++++ .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- .../KeypadInterface-MQTT.ino | 13 +- .../esp32/KeypadInterface/KeypadInterface.ino | 12 +- examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/esp32/Telegram/Telegram.ino | 2 +- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 2 +- .../Homebridge-MQTT/Homebridge-MQTT.ino | 2 +- .../KeypadInterface-MQTT.ino | 13 +- .../KeypadInterface/KeypadInterface.ino | 12 +- .../esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino | 2 +- examples/esp8266/Telegram/Telegram.ino | 2 +- 20 files changed, 732 insertions(+), 61 deletions(-) create mode 100644 examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino create mode 100644 examples/esp32/HomeKit-HomeSpan/dscHomeSpanAccessories.h diff --git a/README.md b/README.md index db3a170..8b1b8c0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and interfacing with DSC keypads (without a panel) for use as general purpose input devices. +This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and emulating DSC panels to connect DSC keypads as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) -* Notifications: [Telegram](https://www.telegram.org) bot (with arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com) [Twilio SMS](https://www.twilio.com), E-mail +* Notifications: [Telegram](https://www.telegram.org) bot (with remote arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com) [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app -* Keypad interface: Use DSC keypads as physical input devices for any general purpose (without a DSC panel). +* Keypad interface: Emulates a DSC panel to connect DSC keypads as physical input devices for any general purpose, without a DSC panel. * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes -See the [dscKeybusInterface-RTOS](https://github.com/taligentx/dscKeybusInterface-RTOS) repository for a port of this library to [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) - this enables a standalone esp8266 HomeKit accessory using [esp-homekit](https://github.com/maximkulkin/esp-homekit). - Example integrations: * [Apple Home & Siri](https://www.apple.com/ios/home/): ![HomeKit](https://user-images.githubusercontent.com/12835671/61570833-c9bb9780-aa54-11e9-9477-8e0853609e91.png) @@ -63,6 +61,7 @@ This library uses a combination of hardware and timer interrupts to accurately c * Monitor PGM outputs 1-14 status * Virtual keypad: - Write keys to the panel for all partitions + - Trigger panel command outputs * Keypad interface: - Use DSC PowerSeries and Classic series keypads as physical input devices for any general purpose without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) @@ -105,7 +104,8 @@ This library uses a combination of hardware and timer interrupts to accurately c ## Release notes * develop - New: DSC Classic series panel support: PC1500, PC1550 - - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - interface with DSC PowerSeries and Classic keypads as physical input devices for any general purpose without needing a DSC panel. + - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - emulate a DSC panel to connect DSC PowerSeries and Classic keypads as physical input devices for any general purpose, without needing a DSC panel. + - New: `HomeKit-HomeSpan` example sketch (esp32) - integrate directly with Apple HomeKit as a native standalone accessory using [HomeSpan](https://github.com/HomeSpan/HomeSpan) - New: [Pushover](https://www.pushover.net) and [Pushsafer](https://www.pushsafer.com) push notification example sketches for esp8266/esp32 - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed @@ -208,8 +208,10 @@ The included examples demonstrate how to use the library and can be used as-is o * Panel trouble * Keybus connected +* **HomeKit-HomeSpan** (esp32): Integrates directly with Apple HomeKit as a native accessory (for the Home app and Siri) using [HomeSpan](https://github.com/HomeSpan/HomeSpan), without needing a separate service or device. Demonstrates arming/disarming partitions, zones status, fire alarms, PGM outputs status, and controlling panel command outputs. + - For esp8266, the [dscKeybusInterface-RTOS](https://github.com/taligentx/dscKeybusInterface-RTOS) library includes a native HomeKit implementation that runs directly on the esp8266, without requiring a separate device running MQTT or Homebridge. + * **Homebridge-MQTT**: Interfaces with [Homebridge](https://github.com/nfarina/homebridge) via MQTT to integrate with Apple HomeKit (including the iOS Home app and Siri) and [Google Home](https://github.com/oznu/homebridge-gsh). Demonstrates arming/disarming partitions and for HomeKit, viewing the status of zones, PGM outputs, and fire alarms. - - The [dscKeybusInterface-RTOS](https://github.com/taligentx/dscKeybusInterface-RTOS) library includes a native HomeKit implementation that runs directly on esp8266, without requiring a separate device running MQTT or Homebridge. * **HomeAssistant-MQTT**: Interfaces with [Home Assistant](https://www.home-assistant.io) via MQTT. Demonstrates arming/disarming partitions and viewing the status of zones, PGM outputs, fire alarms, and trouble. For esp8266/esp32, the partition status is available as a text message for display. @@ -219,17 +221,17 @@ The included examples demonstrate how to use the library and can be used as-is o * **Homey**: Integrates with [Athom Homey](https://www.athom.com/en/) and the [Homeyduino](https://github.com/athombv/homey-arduino-library/) library, including armed, alarm, and fire states (currently limited to one partition), and zone states. Thanks to [MagnusPer](https://github.com/MagnusPer) for contributing this example! -* **Telegram** (esp8266/esp32): Demonstrates sending status updates and arming/disarming the security system via a [Telegram](https://www.telegram.org) bot. Supports iOS, Android, and macOS/Windows/Linux desktop notifications (free). +* **Telegram** (esp8266/esp32): Demonstrates sending status updates as push notifications and arming/disarming the security system via a [Telegram](https://www.telegram.org) bot. Supports iOS, Android, and macOS/Windows/Linux desktop notifications (free). -* **Pushover** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushover](https://www.pushover.net). Supports iOS, Android, macOS native desktop notifications, and Chrome/Firefox/Safari browser popups ($4.99USD one-time purchase per client platform). +* **Pushover** (esp8266/esp32): Demonstrates sending status updates as push notifications via [Pushover](https://www.pushover.net). Supports iOS, Android, macOS native desktop notifications, and Chrome/Firefox/Safari browser popups ($4.99USD one-time purchase per client platform). -* **Pushbullet** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushbullet](https://www.pushbullet.com). Supports Android, Windows desktop notifications, and Chrome/Firefox browser popups (free). Note that iOS is no longer supported. +* **Pushbullet** (esp8266/esp32): Demonstrates sending status updates as push notifications via [Pushbullet](https://www.pushbullet.com). Supports Android, Windows desktop notifications, and Chrome/Firefox browser popups (free). Note that iOS is no longer supported. -* **Pushsafer** (esp8266/esp32): Demonstrates sending status updates as a push notification via [Pushsafer](https://www.pushsafer.com). Supports iOS, Android, Windows desktop notifications, and Chrome/Firefox/Edge/Opera/Yandex browser popups (€0.99EUR or less per 1000 notifications). +* **Pushsafer** (esp8266/esp32): Demonstrates sending status updates as push notifications via [Pushsafer](https://www.pushsafer.com). Supports iOS, Android, Windows desktop notifications, and Chrome/Firefox/Edge/Opera/Yandex browser popups (€0.99EUR or less per 1000 notifications). -* **Twilio-SMS** (esp8266/esp32): Demonstrates sending status updates as an SMS text message via [Twilio](https://www.twilio.com) - thanks to [ColingNG](https://github.com/ColinNg) for contributing this example! +* **Twilio-SMS** (esp8266/esp32): Demonstrates sending status updates as SMS text messages via [Twilio](https://www.twilio.com) - thanks to [ColingNG](https://github.com/ColinNg) for contributing this example! -* **Email** (esp8266/esp32): Demonstrates sending status updates as an email. Email is sent using SMTPS (port 465) with SSL for encryption - this is necessary on the esp8266/esp32 until STARTTLS can be supported. For example, this will work with Gmail after changing the account settings to [allow less secure apps](https://support.google.com/accounts/answer/6010255). +* **Email** (esp8266/esp32): Demonstrates sending status updates as email. Email is sent using SMTPS (port 465) with SSL for encryption - this is necessary on the esp8266/esp32 until STARTTLS can be supported. For example, this will work with Gmail after changing the account settings to [allow less secure apps](https://support.google.com/accounts/answer/6010255). This can be used to send SMS text messages if the number's service provider has an [email to SMS gateway](https://en.wikipedia.org/wiki/SMS_gateway#Email_clients) - examples for the US: * T-mobile: 5558675309@tmomail.net @@ -367,7 +369,7 @@ Panel options affecting this interface, configured by `*8 + installer code` - se dsc.stop(); ... ``` -* Memory usage can be adjusted based on the number of partitions, zones, and data buffer size specified in [`src/dscKeybusInterface.h`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybusInterface.h). Default settings: +* Memory usage can be adjusted based on the number of partitions, zones, and data buffer size specified in [`src/dscKeybus.h`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybus.h) or [`src/dscClassic.h`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscClassic.h). Default settings: * Arduino: up to 4 partitions, 32 zones, 10 buffered commands * esp8266/esp32: up to 8 partitions, 64 zones, 50 buffered commands diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 55b273b..a7a127a 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -295,7 +295,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index d541bd1..fe54e58 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -277,7 +277,7 @@ void loop() { dsc.bufferOverflow = false; } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 7478b9d..d838d45 100644 --- a/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/Arduino/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,12 +1,9 @@ /* * DSC Keypad Interface-MQTT 1.2 (Arduino with Ethernet) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. This sketch uses MQTT to + * send pressed keypad keys and receive commands to control keypad lights and tones. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +19,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/Arduino/KeypadInterface/KeypadInterface.ino b/examples/Arduino/KeypadInterface/KeypadInterface.ino index 6e4cb24..de4bd80 100644 --- a/examples/Arduino/KeypadInterface/KeypadInterface.ino +++ b/examples/Arduino/KeypadInterface/KeypadInterface.ino @@ -1,12 +1,8 @@ /* * DSC Keypad Interface 1.2 (Arduino) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +18,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index 503bd45..1b019cc 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -216,7 +216,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index ef6e026..38a91e0 100644 --- a/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -296,7 +296,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino new file mode 100644 index 0000000..ed093bc --- /dev/null +++ b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino @@ -0,0 +1,217 @@ +/* + * HomeKit-HomeSpan 1.0 (esp32) + * + * Processes the security system status and allows for control using Apple HomeKit, including the Home app + * and Siri. This example uses HomeSpan to interface the esp32 directly with HomeKit without requiring + * a separate service or device. + * + * HomeSpan: https://github.com/HomeSpan/HomeSpan + * + * This sketch demonstrates using partition armed and alarm states as HomeKit Security System accessories, + * zone states as Contact Sensor accessories, fire alarm states as Smoke Sensor accessories, PGM output states + * as Contact Sensor accessories, and command outputs as Switch accessories. + * + * Usage: + * 1. Set the security system access code in the sketch settings to permit disarming and command outputs + * through HomeKit. + * 2. Define the security system components to add to HomeKit as accessories in the setup() section of + * the sketch, using the example accessories as a template. Each accessory requires its own + * SpanAccessory() definition (one per partition, zone, etc). + * 3. Upload the sketch. + * 4. Open the esp32 serial interface to configure WiFi using the HomeSpan interface as + * per https://github.com/HomeSpan/HomeSpan/blob/master/docs/CLI.md + * 5. Open the iOS Home app and add the new bridge accessory, which will include all accessories + * configured in setup(). By default, HomeSpan uses pairing code: 466-37-726 + * + * Release notes: + * 1.0 - Initial release + * + * Wiring: + * DSC Aux(+) --- 5v voltage regulator --- esp32 development board 5v pin + * + * DSC Aux(-) --- esp32 Ground + * + * +--- dscClockPin // Default: 18 + * DSC Yellow --- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * +--- dscReadPin // Default: 19 + * DSC Green ---- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * Classic series only, PGM configured for PC-16 output: + * DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+) + * | + * | +--- dscPC16Pin // Default: 17 + * +-- 33k ohm resistor ---| + * +--- 10k ohm resistor --- Ground + * + * + * Virtual keypad (optional): + * DSC Green ---- NPN collector --\ + * |-- NPN base --- 1k ohm resistor --- dscWritePin // Default: 21 + * Ground --- NPN emitter --/ + * + * Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should + * be suitable, for example: + * -- 2N3904 + * -- BC547, BC548, BC549 + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md) +//#define dscClassicSeries + +#include "HomeSpan.h" +#include + +// Settings +const char* accessCode = "1234"; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration. + +// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the +// virtual keypad. +#define dscClockPin 18 // 4,13,16-39 +#define dscReadPin 19 // 4,13,16-39 +#define dscPC16Pin 17 // DSC Classic Series only, 4,13,16-39 +#define dscWritePin 21 // 4,13,16-33 + +// Initialize components +#ifndef dscClassicSeries +dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin); +#else +dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode); +#endif +bool updatePartitions, updateZones, updatePGMs, updateSmokeSensors; + +#include "dscHomeSpanAccessories.h" // Processes security system components as HomeKit accessories + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(); + Serial.println(); + + homeSpan.begin(Category::Bridges,"DSC Security System"); // Customizable name for the HomeKit accessory bridge + + // Accessory identification + new SpanAccessory(); + new homeSpanIdentify("DSC Security System","DSC","000000","PC1864","2.5"); // Customizable name, manufacturer, serial number, model, firmware revision + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); // HomeKit requires specifying HAP protocol version 1.1.0 + + /* + * HomeKit accessories - define partitions, zones, fire alarms, PGM outputs, and command outputs each as a + * separate SpanAccessory() using the definitions below as a template + */ + + // Partition 1: Security System accessory + new SpanAccessory(); + new homeSpanIdentify("Partition 1","DSC","000000","Alarm","2.5"); + new dscPartition(1); // Set the partition number + + // Partition 8: Security System accessory + new SpanAccessory(); + new homeSpanIdentify("Partition 8","DSC","000000","Alarm","2.5"); + new dscPartition(8); // Set the partition number + + // Zone 1: Contact Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("Zone 1","DSC","000000","Sensor","2.5"); // Set the zone name + new dscZone(1); // Set the zone number + + // Zone 64: Contact Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("Zone 64","DSC","000000","Sensor","2.5"); // Set the zone name + new dscZone(64); // Set the zone number + + // Fire alarm partition 1: Smoke Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("Fire 1","DSC","000000","Sensor","2.5"); // Set the fire sensor name + new dscFire(1); // Set the partition number + + // Fire alarm partition 8: Smoke Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("Fire 8","DSC","000000","Sensor","2.5"); // Set the fire sensor name + new dscFire(8); // Set the partition number + + // PGM output 1: Contact Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("PGM 1","DSC","000000","Sensor","2.5"); // Set the PGM output name + new dscPGM(1); // Set the PGM output number + + // PGM output 14: Contact Sensor accessory + new SpanAccessory(); + new homeSpanIdentify("PGM 14","DSC","000000","Sensor","2.5"); // Set the PGM output name + new dscPGM(14); // Set the PGM output number + + // Command output 1: Switch accessory - this allows HomeKit to activate the PGM outputs assigned to the command output + new SpanAccessory(); + new homeSpanIdentify("Command 1","DSC","000000","Sensor","2.5"); // Set the command output name + new dscCommand(1, 1, 1); // Set the command output number (1-4), one of the PGM outputs (1-14) assigned to the command output, and the assigned partition: dscCommand(cmd, pgm, partition); + + // Command output 4: Switch accessory - this allows HomeKit to activate the PGM outputs assigned to the command output + new SpanAccessory(); + new homeSpanIdentify("Command 4","DSC","000000","Sensor","2.5"); // Set the command output name + new dscCommand(4, 4, 1); // Set the command output number (1-4), one of the PGM outputs (1-14) assigned to the command output, and the assigned partition: dscCommand(cmd, pgm, partition); + + // Starts the Keybus interface and optionally specifies how to print data. + // begin() sets Serial by default and can accept a different stream: begin(Serial1), etc. + dsc.begin(); +} + + +void loop() { + homeSpan.poll(); + + dsc.loop(); + + if (dsc.statusChanged) { // Checks if the security system status has changed + dsc.statusChanged = false; // Reset the status tracking flag + + // If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call + // loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h + if (dsc.bufferOverflow) { + Serial.println(F("Keybus buffer overflow")); + dsc.bufferOverflow = false; + } + + // Sends the access code when needed by the panel for arming or command outputs + if (dsc.accessCodePrompt) { + dsc.accessCodePrompt = false; + dsc.write(accessCode); + } + + // Publishes status per partition + for (byte partition = 0; partition < dscPartitions; partition++) { + + // Skips processing if the partition is disabled or in installer programming + if (dsc.disabled[partition]) continue; + + // Checks partition status and sets a flag to update security system partition accessories + if (dsc.armedChanged[partition] || dsc.exitDelayChanged[partition] || dsc.alarmChanged[partition]) { + updatePartitions = true; + } + + // Checks fire alarm status and sets a flag to update smoke sensor accessories + if (dsc.fireChanged[partition]) { + updateSmokeSensors = true; + } + } + + // Checks zone status and sets a flag to update zone accessories + if (dsc.openZonesStatusChanged) { + dsc.openZonesStatusChanged = false; // Resets the open zones status flag + updateZones = true; // Updates zone accessories + } + + // Checks PGM outputs status and sets a flag to update PGM accessories + if (dsc.pgmOutputsStatusChanged) { + dsc.pgmOutputsStatusChanged = false; // Resets the PGM outputs status flag + updatePGMs = true; // Updates PGM output and command output accessories + } + } +} diff --git a/examples/esp32/HomeKit-HomeSpan/dscHomeSpanAccessories.h b/examples/esp32/HomeKit-HomeSpan/dscHomeSpanAccessories.h new file mode 100644 index 0000000..69e1306 --- /dev/null +++ b/examples/esp32/HomeKit-HomeSpan/dscHomeSpanAccessories.h @@ -0,0 +1,449 @@ +/* + * HomeKit-HomeSpan 1.0 (esp32) + * + * This defines security system components as HomeKit accessories, processes security system status + * changes to update HomeKit, and handles HomeKit requests to change the security system state. + * + * All accessories are configured in HomeKit-HomeSpan.ino. + * + * Issues and (especially) pull requests are welcome: + * https://github.com/taligentx/dscKeybusInterface + * + * This example code is in the public domain. + */ + +// HomeKit security system states are defined as integers per HomeKit Accessory Protocol Specification R2 +#define HOMEKIT_STAY 0 +#define HOMEKIT_AWAY 1 +#define HOMEKIT_NIGHT 2 +#define HOMEKIT_DISARM 3 +#define HOMEKIT_ALARM 4 + +// Tracks which partitions, zones, and PGMs are configured in the sketch - only the configured accessories will be processed for status +bool configuredPartitions[8]; +byte configuredZones[8], configuredPGMs[2], configuredCommandPGMs[2], pendingPGMs[2]; + + +// Partitions are defined as separate Security System accessories +struct dscPartition : Service::SecuritySystem { + byte partition; + char exitState; + SpanCharacteristic *partitionCurrentState, *partitionTargetState; + dscPartition(byte setPartition) : Service::SecuritySystem() { + partition = setPartition - 1; + configuredPartitions[partition] = true; + partitionCurrentState = new Characteristic::SecuritySystemCurrentState(HOMEKIT_DISARM); // Sets initial state to disarmed + partitionTargetState = new Characteristic::SecuritySystemTargetState(HOMEKIT_DISARM); // Sets initial state to disarmed + } + + + // Handles requests received from HomeKit + boolean update() { + byte targetState = partitionTargetState->getNewVal(); + + // Sets night arm (no entry delay) while armed + if (targetState == HOMEKIT_NIGHT && dsc.armed[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + exitState = 'N'; + return(true); + } + + // Disables night arm while armed stay + if (targetState == HOMEKIT_STAY && dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + exitState = 'S'; + return(true); + } + + // Disables night arm while armed away + if (targetState == HOMEKIT_AWAY && dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad no entry delay + exitState = 'A'; + return(true); + } + + // Changes from arm away to arm stay after the exit delay + if (targetState == HOMEKIT_STAY && dsc.armedAway[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("s"); + exitState = 'S'; + return(true); + } + + // Changes from arm stay to arm away after the exit delay + if (targetState == HOMEKIT_AWAY && dsc.armedStay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write("w"); + exitState = 'A'; + return(true); + } + + // Resets the HomeKit target state if attempting to change the armed mode while not ready + if (targetState != HOMEKIT_DISARM && !dsc.ready[partition]) { + dsc.armedChanged[partition] = true; + dsc.statusChanged = true; + return(true); + } + + // Resets the HomeKit target state if attempting to change the arming mode during the exit delay + if (targetState != HOMEKIT_DISARM && dsc.exitDelay[partition] && exitState != 0) { + if (exitState == 'S') partitionTargetState->setVal(HOMEKIT_STAY); + else if (exitState == 'A') partitionTargetState->setVal(HOMEKIT_AWAY); + else if (exitState == 'N') partitionTargetState->setVal(HOMEKIT_NIGHT); + } + + // Stay arm + if (targetState == HOMEKIT_STAY && !dsc.armed[partition] && !dsc.exitDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('s'); // Keypad stay arm + exitState = 'S'; + return(true); + } + + // Away arm + if (targetState == HOMEKIT_AWAY && !dsc.armed[partition] && !dsc.exitDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('w'); // Keypad away arm + exitState = 'A'; + return(true); + } + + // Night arm + if (targetState == HOMEKIT_NIGHT && !dsc.armed[partition] && !dsc.exitDelay[partition]) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write('n'); // Keypad arm with no entry delay + exitState = 'N'; + return(true); + } + + // Disarm + if (targetState == HOMEKIT_DISARM && (dsc.armed[partition] || dsc.exitDelay[partition] || dsc.alarm[partition])) { + dsc.writePartition = partition + 1; // Sets writes to the partition number + dsc.write(accessCode); + return(true); + } + + return(true); + } + + + // Checks for partition status changes to send to HomeKit + void loop() { + if (updatePartitions) { + updatePartitions = false; + + if (dsc.armedChanged[partition]) { + if (dsc.armed[partition]) { + exitState = 0; + if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { // Night armed away + partitionTargetState->setVal(HOMEKIT_NIGHT); + partitionCurrentState->setVal(HOMEKIT_NIGHT); + } + else if (dsc.armedAway[partition]) { // Away armed + partitionTargetState->setVal(HOMEKIT_AWAY); + partitionCurrentState->setVal(HOMEKIT_AWAY); + } + else if (dsc.armedStay[partition] && dsc.noEntryDelay[partition]) { // Night armed stay + partitionTargetState->setVal(HOMEKIT_NIGHT); + partitionCurrentState->setVal(HOMEKIT_NIGHT); + } + else if (dsc.armedStay[partition]) { // Stay armed + partitionTargetState->setVal(HOMEKIT_STAY); + partitionCurrentState->setVal(HOMEKIT_STAY); + } + } + else { // Disarmed + partitionTargetState->setVal(HOMEKIT_DISARM); + partitionCurrentState->setVal(HOMEKIT_DISARM); + } + } + + // Updates exit delay status + if (dsc.exitDelayChanged[partition]) { + dsc.exitDelayChanged[partition] = false; // Resets the exit delay status flag + + if (dsc.exitDelay[partition]) { + + // Sets the arming target state if the panel is armed externally + if (exitState == 0 || dsc.exitStateChanged[partition]) { + dsc.exitStateChanged[partition] = 0; + switch (dsc.exitState[partition]) { + case DSC_EXIT_STAY: { + exitState = 'S'; + partitionTargetState->setVal(HOMEKIT_STAY); + break; + } + case DSC_EXIT_AWAY: { + exitState = 'A'; + partitionTargetState->setVal(HOMEKIT_AWAY); + break; + } + case DSC_EXIT_NO_ENTRY_DELAY: { + exitState = 'N'; + partitionTargetState->setVal(HOMEKIT_NIGHT); + break; + } + } + } + } + + // Disarmed during exit delay + else if (!dsc.armed[partition]) { + exitState = 0; + partitionTargetState->setVal(HOMEKIT_DISARM); + partitionCurrentState->setVal(HOMEKIT_DISARM); + } + } + + // Publishes alarm triggered status + if (dsc.alarmChanged[partition]) { + dsc.alarmChanged[partition] = false; // Resets the partition alarm status flag + if (dsc.alarm[partition]) { + partitionCurrentState->setVal(HOMEKIT_ALARM); // Alarm triggered + } + else if (!dsc.armedChanged[partition]) { + partitionTargetState->setVal(HOMEKIT_DISARM); + partitionCurrentState->setVal(HOMEKIT_DISARM); + } + } + + if (dsc.armedChanged[partition]) dsc.armedChanged[partition] = false; // Resets the partition armed status flag + + // Checks for changed status in additional partitions + for (byte checkPartition = 0; checkPartition < dscPartitions; checkPartition++) { + + // Skips processing if the partition is disabled, in installer programming, or not configured in the sketch + if (dsc.disabled[checkPartition] || !configuredPartitions[checkPartition]) continue; + + // Checks for changed status in a partition + if (dsc.armedChanged[checkPartition] || dsc.exitDelayChanged[checkPartition] || dsc.alarmChanged[checkPartition]) { + updatePartitions = true; + } + } + } + } +}; + + +// Zones are defined as Contact Sensor accessories +struct dscZone : Service::ContactSensor { + byte zoneGroup, zoneBit; + SpanCharacteristic *zoneState; + dscZone(byte zone) : Service::ContactSensor() { + zoneGroup = (zone - 1) / 8; + zoneBit = (zone - 1) - (zoneGroup * 8); + bitWrite(configuredZones[zoneGroup], zoneBit, 1); // Sets a zone as being configured + zoneState = new Characteristic::ContactSensorState(0); + } + + // Checks for zone status changes to send to HomeKit + void loop() { + if (updateZones) { + updateZones = false; + + if (bitRead(dsc.openZonesChanged[zoneGroup], zoneBit)) { // Checks an individual open zone status flag + bitWrite(dsc.openZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual open zone status flag + + if (bitRead(dsc.openZones[zoneGroup], zoneBit)) zoneState->setVal(1); // Set zone status open + else zoneState->setVal(0); // Set zone status closed + } + + // Checks if additional configured zones have changed + for (byte checkZoneGroup = 0; checkZoneGroup < dscZones; checkZoneGroup++) { + for (byte checkZoneBit = 0; checkZoneBit < 8; checkZoneBit++) { + if (bitRead(configuredZones[checkZoneGroup], checkZoneBit) && bitRead(dsc.openZonesChanged[checkZoneGroup], checkZoneBit)) { // Checks if additional zones have changed + updateZones = true; // Sets a flag to continue processing remaining changed zones that are configured + } + } + } + } + } +}; + + +// Fire alarms are defined as separate Smoke Sensor accessories +struct dscFire : Service::SmokeSensor { + byte partition; + SpanCharacteristic *fireState; + dscFire(byte setPartition) : Service::SmokeSensor() { + partition = setPartition - 1; + fireState = new Characteristic::SmokeDetected(0); + } + + // Checks for fire status changes to send to HomeKit + void loop() { + if (updateSmokeSensors) { + updateSmokeSensors = false; + + dsc.fireChanged[partition] = false; // Resets the fire status flag + + if (dsc.fire[partition]) fireState->setVal(1); // Fire alarm tripped + else fireState->setVal(0); // Fire alarm restored + + // Checks for changed status in additional partitions + for (byte checkPartition = 0; checkPartition < dscPartitions; checkPartition++) { + + // Skips processing if the partition is disabled, in installer programming, or not configured in the sketch + if (dsc.disabled[checkPartition] || !configuredPartitions[checkPartition]) continue; + + // Checks for changed fire status in a partition + if (dsc.fireChanged[checkPartition]) updateSmokeSensors = true; + } + } + } +}; + + +// PGM outputs are defined as Contact Sensor accessories +struct dscPGM : Service::ContactSensor { + byte pgmGroup, pgmBit; + SpanCharacteristic *pgmState; + dscPGM(byte pgm) : Service::ContactSensor() { + pgmGroup = (pgm - 1) / 8; + pgmBit = (pgm - 1) - (pgmGroup * 8); + bitWrite(configuredPGMs[pgmGroup], pgmBit, 1); // Sets a PGM output as being configured + pgmState = new Characteristic::ContactSensorState(0); + } + + // Checks for PGM status changes to send to HomeKit + void loop() { + if (updatePGMs) { + updatePGMs = false; + + if (bitRead(dsc.pgmOutputsChanged[pgmGroup], pgmBit)) { + + // Handles PGMs defined both as this contact sensor accessory and for a command switch output switch accessory + if (bitRead(configuredCommandPGMs[pgmGroup], pgmBit)) { + + // Sets processing status of the PGM depending on the order in which this accessory is handled + if (bitRead(pendingPGMs[pgmGroup], pgmBit)) { + bitWrite(pendingPGMs[pgmGroup], pgmBit, 0); + bitWrite(dsc.pgmOutputsChanged[pgmGroup], pgmBit, 0); + } + else bitWrite(pendingPGMs[pgmGroup], pgmBit, 1); + + if (bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) pgmState->setVal(1); // Set PGM output status on + else pgmState->setVal(0); // Set PGM output status off + } + + // Handles PGMs defined only for this contact sensor accessory + else { + bitWrite(dsc.pgmOutputsChanged[pgmGroup], pgmBit, 0); + if (bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) pgmState->setVal(1); // Set command output status on + else pgmState->setVal(0); // Set command output status off + } + } + + // Checks if additional configured PGM outputs have changed + for (byte checkPGMGroup = 0; checkPGMGroup < 2; checkPGMGroup++) { + for (byte checkPGMBit = 0; checkPGMBit < 8; checkPGMBit++) { + if (bitRead(dsc.pgmOutputsChanged[checkPGMGroup], checkPGMBit)) { + if (bitRead(configuredPGMs[checkPGMGroup], checkPGMBit) || bitRead(configuredCommandPGMs[checkPGMGroup], checkPGMBit)) { // Checks if additional PGM outputs have changed + updatePGMs = true; // Sets a flag to continue processing changed PGM outputs that are configured + } + } + } + } + } + } +}; + + +// Command outputs 1-4 are defined as Switch accessories - this allows HomeKit to view status and +// control the PGM outputs assigned to each command output +struct dscCommand : Service::Switch { + byte cmd, pgmGroup, pgmBit, partition; + SpanCharacteristic *cmdState; + dscCommand(byte setCMD, byte pgm, byte setPartition) : Service::Switch() { + cmd = setCMD; + partition = setPartition; + pgmGroup = (pgm - 1) / 8; + pgmBit = (pgm - 1) - (pgmGroup * 8); + bitWrite(configuredCommandPGMs[pgmGroup], pgmBit, 1); // Sets a PGM output as being configured + cmdState = new Characteristic::On(0); + } + + + // Handles requests received from HomeKit + boolean update() { + byte targetState = cmdState->getNewVal(); + + // HomeKit requests switch on - enables the command output if its assigned PGM output is inactive + if (targetState == 1 && !bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) { + dsc.writePartition = partition; + switch (cmd) { + case 1: dsc.write('['); break; + case 2: dsc.write(']'); break; + case 3: dsc.write('{'); break; + case 4: dsc.write('}'); break; + default: cmdState->setVal(0); + } + } + + // HomeKit requests switch off - resets the HomeKit state to On if the PGM output is still active + else if (targetState == 0 && bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) { + cmdState->setVal(1); + } + + return(true); + } + + + // Checks for PGM status changes to send to HomeKit + void loop() { + if (updatePGMs) { + updatePGMs = false; + + if (bitRead(dsc.pgmOutputsChanged[pgmGroup], pgmBit)) { + + // Handles PGMs defined both for this command switch output switch accessory and as a contact sensor accessory + if (bitRead(configuredPGMs[pgmGroup], pgmBit)) { + + // Sets processing status of the PGM depending on the order in which this accessory is handled + if (bitRead(pendingPGMs[pgmGroup], pgmBit)) { + bitWrite(pendingPGMs[pgmGroup], pgmBit, 0); + bitWrite(dsc.pgmOutputsChanged[pgmGroup], pgmBit, 0); + } + else bitWrite(pendingPGMs[pgmGroup], pgmBit, 1); + + if (bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) cmdState->setVal(1); // Set command output status on + else cmdState->setVal(0); // Set command output status off + } + + // Handles PGMs defined only for this command output switch accessory + else { + bitWrite(dsc.pgmOutputsChanged[pgmGroup], pgmBit, 0); + if (bitRead(dsc.pgmOutputs[pgmGroup], pgmBit)) cmdState->setVal(1); // Set command output status on + else cmdState->setVal(0); // Set command output status off + } + } + + // Checks if additional configured PGM outputs have changed + for (byte checkPGMGroup = 0; checkPGMGroup < 2; checkPGMGroup++) { + for (byte checkPGMBit = 0; checkPGMBit < 8; checkPGMBit++) { + if (bitRead(dsc.pgmOutputsChanged[checkPGMGroup], checkPGMBit)) { + if (bitRead(configuredPGMs[checkPGMGroup], checkPGMBit) || bitRead(configuredCommandPGMs[checkPGMGroup], checkPGMBit)) { // Checks if additional PGM outputs have changed + updatePGMs = true; // Sets a flag to continue processing changed PGM outputs that are configured + } + } + } + } + } + } +}; + + +// HomeSpan Identify +struct homeSpanIdentify : Service::AccessoryInformation { + homeSpanIdentify(const char *name, const char *manu, const char *sn, const char *model, const char *version) : Service::AccessoryInformation() { + new Characteristic::Name(name); + new Characteristic::Manufacturer(manu); + new Characteristic::SerialNumber(sn); + new Characteristic::Model(model); + new Characteristic::FirmwareRevision(version); + new Characteristic::Identify(); + } +}; diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index e0d9065..afbdb67 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -274,7 +274,7 @@ void loop() { dsc.bufferOverflow = false; } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 49782e4..dacdd1a 100644 --- a/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp32/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,12 +1,9 @@ /* * DSC Keypad Interface-MQTT 1.2 (esp32) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. This sketch uses MQTT to + * send pressed keypad keys and receive commands to control keypad lights and tones. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +19,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/esp32/KeypadInterface/KeypadInterface.ino b/examples/esp32/KeypadInterface/KeypadInterface.ino index f557e7c..72ed4fe 100644 --- a/examples/esp32/KeypadInterface/KeypadInterface.ino +++ b/examples/esp32/KeypadInterface/KeypadInterface.ino @@ -1,12 +1,8 @@ /* * DSC Keypad Interface 1.2 (esp32) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +18,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino index 21dca44..65067db 100644 --- a/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -226,7 +226,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp32/Telegram/Telegram.ino b/examples/esp32/Telegram/Telegram.ino index 812be0e..e0c35d3 100644 --- a/examples/esp32/Telegram/Telegram.ino +++ b/examples/esp32/Telegram/Telegram.ino @@ -187,7 +187,7 @@ void loop() { else sendMessage("Disconnected"); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index 0543566..8117cb0 100644 --- a/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -305,7 +305,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index b6f27e9..c942c0e 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -280,7 +280,7 @@ void loop() { dsc.bufferOverflow = false; } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino index 92571a5..13b1787 100644 --- a/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino +++ b/examples/esp8266/KeypadInterface-MQTT/KeypadInterface-MQTT.ino @@ -1,12 +1,9 @@ /* * DSC Keypad Interface-MQTT 1.2 (esp8266) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. This sketch uses MQTT to + * send pressed keypad keys and receive commands to control keypad lights and tones. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +19,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/esp8266/KeypadInterface/KeypadInterface.ino b/examples/esp8266/KeypadInterface/KeypadInterface.ino index 6be2173..9daa41d 100644 --- a/examples/esp8266/KeypadInterface/KeypadInterface.ino +++ b/examples/esp8266/KeypadInterface/KeypadInterface.ino @@ -1,12 +1,8 @@ /* * DSC Keypad Interface 1.2 (esp8266) * - * Interfaces directly to a DSC PowerSeries or Classic series keypad (without a DSC panel) to - * enable use of DSC keypads as physical inputs for any general purpose. - * - * This interface uses a different wiring setup from the standard Keybus interface, adding - * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though - * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * Emulates a DSC panel to directly interface DSC PowerSeries or Classic series keypads as physical + * input devices for any general purpose, without needing a DSC panel. * * PowerSeries keypad features: * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key @@ -22,6 +18,10 @@ * - Read keypad key button presses, including fire/aux/panic alarm keys: dsc.key * - Set keypad lights: Ready, Armed, Trouble, Memory, Bypass, Zones 1-6: dsc.lightReady, dsc.lightZone1, etc * + * This interface uses a different wiring setup from the standard Keybus interface, adding + * an NPN transistor on dscClockPin. The DSC keypads require a 12v DC power source, though + * lower voltages down to 7v may work for key presses (the LEDs will be dim). + * * Release notes: * 1.2 - Add Classic keypad support - PC1500RK * 1.1 - Add keypad beep, buzzer, constant tone diff --git a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino index b43198d..60a64f0 100644 --- a/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -227,7 +227,7 @@ void loop() { else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); diff --git a/examples/esp8266/Telegram/Telegram.ino b/examples/esp8266/Telegram/Telegram.ino index de55816..a45e48b 100644 --- a/examples/esp8266/Telegram/Telegram.ino +++ b/examples/esp8266/Telegram/Telegram.ino @@ -186,7 +186,7 @@ void loop() { else sendMessage("Disconnected"); } - // Sends the access code when needed by the panel for arming + // Sends the access code when needed by the panel for arming or command outputs if (dsc.accessCodePrompt) { dsc.accessCodePrompt = false; dsc.write(accessCode); From 1845c85980a4fb04306ca37e7e6dcbf51f32b079 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 25 Feb 2022 16:17:06 +0300 Subject: [PATCH 44/49] Fixed Homebridge-MQTT handling exit delay states while multiple partitions are arming --- README.md | 12 +++-- .../HomeAssistant-MQTT/HomeAssistant-MQTT.ino | 6 +-- .../Homebridge-MQTT/Homebridge-MQTT.ino | 47 ++++++++++--------- .../Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino | 6 +-- .../HomeKit-HomeSpan/HomeKit-HomeSpan.ino | 2 +- .../esp32/Homebridge-MQTT/Homebridge-MQTT.ino | 41 ++++++++-------- .../Homebridge-MQTT/Homebridge-MQTT.ino | 41 ++++++++-------- 7 files changed, 80 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 8b1b8c0..e1ee485 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, and emulating DSC panels to connect DSC keypads as general purpose input devices. +This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, unlocking installer codes, and emulating DSC panels to connect DSC keypads as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) -* Notifications: [Telegram](https://www.telegram.org) bot (with remote arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com) [Twilio SMS](https://www.twilio.com), E-mail +* Notifications: [Telegram](https://www.telegram.org) bot (with remote arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com), [Twilio SMS](https://www.twilio.com), E-mail * Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app * Keypad interface: Emulates a DSC panel to connect DSC keypads as physical input devices for any general purpose, without a DSC panel. * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes @@ -43,7 +43,7 @@ Example integrations: I was interested in finding a solution that directly accessed the pair of data lines that DSC uses for their proprietary Keybus protocol to send data between the panel, keypads, and other modules (instead of using the DSC IT-100 serial module). Tapping into the data lines is an ideal task for a microcontroller and also presented an opportunity to work with the [Arduino](https://www.arduino.cc) and [FreeRTOS](https://www.freertos.org) (via [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)) platforms. -While there has been excellent [discussion about the DSC Keybus protocol](https://www.avrfreaks.net/forum/dsc-keybus-protocol) and a several existing projects, there were a few issues that remained unsolved: +While there has been excellent [discussion about the DSC Keybus protocol](https://www.avrfreaks.net/forum/dsc-keybus-protocol) and several existing projects, there were a few issues that remained unsolved: * Error-prone Keybus data capture. * Limited data decoding - there was good progress for armed/disarmed states and partial zone status for a single partition, but otherwise most of the data was undecoded (notably missing the alarm triggered state). * Read-only - unable to control the Keybus to act as a virtual keypad. @@ -63,13 +63,13 @@ This library uses a combination of hardware and timer interrupts to accurately c - Write keys to the panel for all partitions - Trigger panel command outputs * Keypad interface: - - Use DSC PowerSeries and Classic series keypads as physical input devices for any general purpose without needing a DSC panel. + - Emulates a DSC panel to use DSC PowerSeries and Classic series keypads as physical input devices for any general purpose, without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code * Direct Keybus interface: - Does not require the [DSC IT-100 serial interface](https://www.dsc.com/alarm-security-products/IT-100%20-%20PowerSeries%20Integration%20Module/22). * Designed for reliable data decoding and performance: - - Pin change and timer interrupts for accurate data capture timing + - Hardware GPIO pin interrupts and timer interrupts for accurate data capture timing - Data buffering: helps prevent lost Keybus data if the sketch is busy - Extensive data decoding: the majority of Keybus data as seen in the [DSC IT-100 Data Interface developer's guide](https://cms.dsc.com/download.php?t=1&id=16238) has been reverse engineered and documented in [`src/dscKeybusPrintData.cpp`](https://github.com/taligentx/dscKeybusInterface/blob/master/src/dscKeybusPrintData.cpp). - Non-blocking code: Allows sketches to run as quickly as possible without using `delay` or `delayMicroseconds` @@ -110,9 +110,11 @@ This library uses a combination of hardware and timer interrupts to accurately c - New: esp32-s2 microcontroller support - Updated: `Homebridge-MQTT` support switching armed modes while armed - Updated: Added TLS root certificate to `Twilio-SMS` + - Updated: removed deprecated `handlePanel()` - Bugfix: `VirtualKeypad-Web` updated notes to switch to [this fork of ESPAsyncWebServer](https://github.com/arjenhiemstra/ESPAsyncWebServer) to resolve crashes with iOS and macOS clients. - Bugfix: `Pushbullet` example sketch updated TLS security certificate fingerprint - Bugfix: Workaround for [upstream esp32 TLS handshake issue](https://github.com/espressif/arduino-esp32/issues/6165) preventing making a TLS connection more than once. + - Bugfix: Fixed `Homebridge-MQTT` handling exit delay states while multiple partitions are arming * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT diff --git a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino index a7a127a..b7085bf 100644 --- a/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino +++ b/examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino @@ -219,9 +219,9 @@ const char* mqttPassword = ""; // Optional, leave blank if not required // MQTT topics - match to Home Assistant's configuration.yaml const char* mqttClientName = "dscKeybusInterface"; -const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition8 -const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone64 -const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire8 +const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition4 +const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone32 +const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire4 const char* mqttPgmTopic = "dsc/Get/PGM"; // Sends PGM status per PGM: dsc/Get/PGM1 ... dsc/Get/PGM14 const char* mqttTroubleTopic = "dsc/Get/Trouble"; // Sends trouble status const char* mqttStatusTopic = "dsc/Status"; // Sends online/offline status diff --git a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino index fe54e58..91c30eb 100644 --- a/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.6 (Arduino with Ethernet) + * Homebridge-MQTT 1.7 (Arduino with Ethernet) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -146,6 +146,7 @@ * Closed: "0" * * Release notes: + * 1.7 - Fixed exit delay states while multiple partitions are arming * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status @@ -211,9 +212,9 @@ const char* mqttPassword = ""; // Optional, leave blank if not required // MQTT topics - match to Homebridge's config.json const char* mqttClientName = "dscKeybusInterface"; -const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition8 -const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone64 -const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire8 +const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition4 +const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone32 +const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire4 const char* mqttPgmTopic = "dsc/Get/PGM"; // Sends PGM status per PGM: dsc/Get/PGM1 ... dsc/Get/PGM14 const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to write to the panel @@ -233,7 +234,7 @@ dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, access EthernetClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; -char exitState; +char exitState[4]; void setup() { @@ -292,7 +293,7 @@ void loop() { // Publishes armed/disarmed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; // Night armed away if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { @@ -327,21 +328,21 @@ void loop() { if (dsc.exitDelay[partition]) { // Sets the arming target state if the panel is armed externally - if (exitState == 0 || dsc.exitStateChanged[partition]) { + if (exitState[partition] == 0 || dsc.exitStateChanged[partition]) { dsc.exitStateChanged[partition] = 0; switch (dsc.exitState[partition]) { case DSC_EXIT_STAY: { - exitState = 'S'; + exitState[partition] = 'S'; publishState(mqttPartitionTopic, partition, "S", 0); break; } case DSC_EXIT_AWAY: { - exitState = 'A'; + exitState[partition] = 'A'; publishState(mqttPartitionTopic, partition, "A", 0); break; } case DSC_EXIT_NO_ENTRY_DELAY: { - exitState = 'N'; + exitState[partition] = 'N'; publishState(mqttPartitionTopic, partition, "N", 0); break; } @@ -351,7 +352,7 @@ void loop() { // Disarmed during exit delay else if (!dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; publishState(mqttPartitionTopic, partition, "D", "D"); } } @@ -461,7 +462,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } @@ -470,7 +471,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -479,7 +480,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -488,7 +489,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("s"); publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -497,7 +498,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("w"); publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -509,10 +510,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { } // Resets the HomeKit target state if attempting to change the arming mode during the exit delay - if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState != 0) { - if (exitState == 'S') publishState(mqttPartitionTopic, partition, "S", 0); - else if (exitState == 'A') publishState(mqttPartitionTopic, partition, "A", 0); - else if (exitState == 'N') publishState(mqttPartitionTopic, partition, "N", 0); + if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState[partition] != 0) { + if (exitState[partition] == 'S') publishState(mqttPartitionTopic, partition, "S", 0); + else if (exitState[partition] == 'A') publishState(mqttPartitionTopic, partition, "A", 0); + else if (exitState[partition] == 'N') publishState(mqttPartitionTopic, partition, "N", 0); } @@ -521,7 +522,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('s'); // Keypad stay arm publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -530,7 +531,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('w'); // Keypad away arm publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -539,7 +540,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad arm with no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } diff --git a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino index 1b019cc..c691245 100644 --- a/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino +++ b/examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino @@ -140,9 +140,9 @@ const char* mqttPassword = ""; // Optional, leave blank if not required // MQTT topics - match to the OpenHAB "things" configuration file const char* mqttClientName = "dscKeybusInterface"; -const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition8 -const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone64 -const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire8 +const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition4 +const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone32 +const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire4 const char* mqttPgmTopic = "dsc/Get/PGM"; // Sends PGM status per PGM: dsc/Get/PGM1 ... dsc/Get/PGM14 const char* mqttTroubleTopic = "dsc/Get/Trouble"; // Sends trouble status const char* mqttStatusTopic = "dsc/Status"; // Sends online/offline status diff --git a/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino index ed093bc..79bb192 100644 --- a/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino +++ b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino @@ -185,7 +185,7 @@ void loop() { dsc.write(accessCode); } - // Publishes status per partition + // Checks status per partition for (byte partition = 0; partition < dscPartitions; partition++) { // Skips processing if the partition is disabled or in installer programming diff --git a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino index afbdb67..44e52e8 100644 --- a/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.6 (esp32) + * Homebridge-MQTT 1.7 (esp32) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.7 - Fixed exit delay states while multiple partitions are arming * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status @@ -229,7 +230,7 @@ dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, access WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; -char exitState; +char exitState[8]; void setup() { @@ -289,7 +290,7 @@ void loop() { // Publishes armed/disarmed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; // Night armed away if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { @@ -324,21 +325,21 @@ void loop() { if (dsc.exitDelay[partition]) { // Sets the arming target state if the panel is armed externally - if (exitState == 0 || dsc.exitStateChanged[partition]) { + if (exitState[partition] == 0 || dsc.exitStateChanged[partition]) { dsc.exitStateChanged[partition] = 0; switch (dsc.exitState[partition]) { case DSC_EXIT_STAY: { - exitState = 'S'; + exitState[partition] = 'S'; publishState(mqttPartitionTopic, partition, "S", 0); break; } case DSC_EXIT_AWAY: { - exitState = 'A'; + exitState[partition] = 'A'; publishState(mqttPartitionTopic, partition, "A", 0); break; } case DSC_EXIT_NO_ENTRY_DELAY: { - exitState = 'N'; + exitState[partition] = 'N'; publishState(mqttPartitionTopic, partition, "N", 0); break; } @@ -348,7 +349,7 @@ void loop() { // Disarmed during exit delay else if (!dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; publishState(mqttPartitionTopic, partition, "D", "D"); } } @@ -458,7 +459,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } @@ -467,7 +468,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -476,7 +477,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -485,7 +486,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("s"); publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -494,7 +495,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("w"); publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -506,10 +507,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { } // Resets the HomeKit target state if attempting to change the arming mode during the exit delay - if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState != 0) { - if (exitState == 'S') publishState(mqttPartitionTopic, partition, "S", 0); - else if (exitState == 'A') publishState(mqttPartitionTopic, partition, "A", 0); - else if (exitState == 'N') publishState(mqttPartitionTopic, partition, "N", 0); + if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState[partition] != 0) { + if (exitState[partition] == 'S') publishState(mqttPartitionTopic, partition, "S", 0); + else if (exitState[partition] == 'A') publishState(mqttPartitionTopic, partition, "A", 0); + else if (exitState[partition] == 'N') publishState(mqttPartitionTopic, partition, "N", 0); } @@ -518,7 +519,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('s'); // Keypad stay arm publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -527,7 +528,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('w'); // Keypad away arm publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -536,7 +537,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad arm with no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } diff --git a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino index c942c0e..252a59a 100644 --- a/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino +++ b/examples/esp8266/Homebridge-MQTT/Homebridge-MQTT.ino @@ -1,5 +1,5 @@ /* - * Homebridge-MQTT 1.6 (esp8266) + * Homebridge-MQTT 1.7 (esp8266) * * Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app, * Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for @@ -147,6 +147,7 @@ * Closed: "0" * * Release notes: + * 1.7 - Fixed exit delay states while multiple partitions are arming * 1.6 - Added DSC Classic series support * 1.5 - Support switching armed modes while armed * 1.4 - Added PGM outputs 1-14 status @@ -235,7 +236,7 @@ dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, access WiFiClient ipClient; PubSubClient mqtt(mqttServer, mqttPort, ipClient); unsigned long mqttPreviousTime; -char exitState; +char exitState[8]; void setup() { @@ -295,7 +296,7 @@ void loop() { // Publishes armed/disarmed status if (dsc.armedChanged[partition]) { if (dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; // Night armed away if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) { @@ -330,21 +331,21 @@ void loop() { if (dsc.exitDelay[partition]) { // Sets the arming target state if the panel is armed externally - if (exitState == 0 || dsc.exitStateChanged[partition]) { + if (exitState[partition] == 0 || dsc.exitStateChanged[partition]) { dsc.exitStateChanged[partition] = 0; switch (dsc.exitState[partition]) { case DSC_EXIT_STAY: { - exitState = 'S'; + exitState[partition] = 'S'; publishState(mqttPartitionTopic, partition, "S", 0); break; } case DSC_EXIT_AWAY: { - exitState = 'A'; + exitState[partition] = 'A'; publishState(mqttPartitionTopic, partition, "A", 0); break; } case DSC_EXIT_NO_ENTRY_DELAY: { - exitState = 'N'; + exitState[partition] = 'N'; publishState(mqttPartitionTopic, partition, "N", 0); break; } @@ -354,7 +355,7 @@ void loop() { // Disarmed during exit delay else if (!dsc.armed[partition]) { - exitState = 0; + exitState[partition] = 0; publishState(mqttPartitionTopic, partition, "D", "D"); } } @@ -464,7 +465,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } @@ -473,7 +474,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -482,7 +483,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad no entry delay publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -491,7 +492,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("s"); publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -500,7 +501,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write("w"); publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -512,10 +513,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { } // Resets the HomeKit target state if attempting to change the arming mode during the exit delay - if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState != 0) { - if (exitState == 'S') publishState(mqttPartitionTopic, partition, "S", 0); - else if (exitState == 'A') publishState(mqttPartitionTopic, partition, "A", 0); - else if (exitState == 'N') publishState(mqttPartitionTopic, partition, "N", 0); + if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState[partition] != 0) { + if (exitState[partition] == 'S') publishState(mqttPartitionTopic, partition, "S", 0); + else if (exitState[partition] == 'A') publishState(mqttPartitionTopic, partition, "A", 0); + else if (exitState[partition] == 'N') publishState(mqttPartitionTopic, partition, "N", 0); } @@ -524,7 +525,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('s'); // Keypad stay arm publishState(mqttPartitionTopic, partition, "S", 0); - exitState = 'S'; + exitState[partition] = 'S'; return; } @@ -533,7 +534,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('w'); // Keypad away arm publishState(mqttPartitionTopic, partition, "A", 0); - exitState = 'A'; + exitState[partition] = 'A'; return; } @@ -542,7 +543,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { dsc.writePartition = partition + 1; // Sets writes to the partition number dsc.write('n'); // Keypad arm with no entry delay publishState(mqttPartitionTopic, partition, "N", 0); - exitState = 'N'; + exitState[partition] = 'N'; return; } From 129ef1cb44a5a10e54fa64830531048a8cb96635 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Tue, 8 Mar 2022 18:17:04 +0300 Subject: [PATCH 45/49] Update README.md --- README.md | 9 +++++---- library.json | 4 ++-- library.properties | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 38ad55a..590b6f5 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ This enables existing DSC security system installations to retain the features a The built-in examples can be used as-is or as a base to adapt to other uses: * Home automation integration: [Home Assistant](https://www.home-assistant.io), [Apple HomeKit & Siri](https://www.apple.com/ios/home/), [Google Home](https://assistant.google.com), [OpenHAB](https://www.openhab.org), [Athom Homey](https://www.athom.com/en/) -* Notifications: [Telegram](https://www.telegram.org) bot (with remote arming/disarming via chat), [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com), [Twilio SMS](https://www.twilio.com), E-mail -* Virtual keypad: Web interface, [Blynk](https://www.blynk.cc) mobile app +* Remote control: Web interface, [Blynk](https://www.blynk.cc) mobile app, [Telegram](https://www.telegram.org) bot (with remote arming/disarming via chat) +* Notifications: [Pushover](https://www.pushover.net), [PushBullet](https://www.pushbullet.com), [Pushsafer](https://www.pushsafer.com), [Twilio SMS](https://www.twilio.com), [TinyGSM SMS](https://github.com/vshymanskyy/TinyGSM), E-mail * Keypad interface: Emulates a DSC panel to connect DSC keypads as physical input devices for any general purpose, without a DSC panel. * Installer code unlocking: Automatic code search to unlock panels with unknown installer codes @@ -103,12 +103,13 @@ This library uses a combination of hardware and timer interrupts to accurately c ## Release notes * develop - - New: DSC Classic series panel support: PC1500, PC1550 + - New: DSC Classic series panel support: PC1500, PC1550, PC2550 - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - emulate a DSC panel to connect DSC PowerSeries and Classic keypads as physical input devices for any general purpose, without needing a DSC panel. - New: `HomeKit-HomeSpan` example sketch (esp32) - integrate directly with Apple HomeKit as a native standalone accessory using [HomeSpan](https://github.com/HomeSpan/HomeSpan) - New: [Pushover](https://www.pushover.net) and [Pushsafer](https://www.pushsafer.com) push notification example sketches for esp8266/esp32 - New: esp32-s2 microcontroller support - - Updated: `Homebridge-MQTT` support switching armed modes while armed + - New: Code restructured to support new features from sketches using #define flags - enables Classic series support and `KeypadInterface`. + - Updated: `Homebridge-MQTT` supports switching armed modes while armed - Updated: Added TLS root certificate to `Twilio-SMS` - Updated: removed deprecated `handlePanel()` - Bugfix: `VirtualKeypad-Web` updated notes to switch to [this fork of ESPAsyncWebServer](https://github.com/arjenhiemstra/ESPAsyncWebServer) to resolve crashes with iOS and macOS clients. diff --git a/library.json b/library.json index 88af0af..111aa5c 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "dscKeybusInterface", - "keywords": "dsc, home-automation, home-security, homebridge, homekit, home-assistant, homeassistant, homey, openhab, google-home, blynk, web, webserver, telegram, pushbullet, twilio, email, esp8266, esp32", - "description": "This library directly interfaces Arduino, esp8266, and esp32 microcontrollers to DSC PowerSeries security systems for integration with home automation (Home Assistant, Apple HomeKit, Homey), notifications on system events, and usage as a virtual keypad.", + "keywords": "dsc, home-automation, home-security, homebridge, homekit, home-assistant, homeassistant, homespan, homey, openhab, google-home, blynk, web, webserver, telegram, pushbullet, twilio, tinygsm, email, esp8266, esp32", + "description": "Directly interface Arduino, esp8266, and esp32 to DSC PowerSeries and Classic security systems for integration with home automation, remote control apps, notifications on alarm events, and emulating DSC panels to connect DSC keypads.", "repository": { "type": "git", diff --git a/library.properties b/library.properties index 5d060f9..03ef3c8 100644 --- a/library.properties +++ b/library.properties @@ -2,8 +2,8 @@ name=DSC Keybus Interface version=2.0 author=Nikhil Choudhary maintainer=Nikhil Choudhary -sentence=Directly interface Arduino, esp8266, and esp32 microcontrollers to DSC PowerSeries security systems for integration with home automation, alarm notifications, and usage as a virtual keypad. -paragraph=Includes examples to monitor armed/alarm/zone/fire/trouble status, integrate with Homebridge (Apple HomeKit, Google Home) and Home Assistant via MQTT, send email and push notifications via Telegram and Pushbullet, and decode the Keybus protocol. +sentence=Directly interface Arduino, esp8266, and esp32 to DSC PowerSeries and Classic security systems for integration with home automation, remote control apps, notifications on alarm events, and emulating DSC panels to connect DSC keypads. +paragraph=Includes examples to integrate with Homebridge (Apple HomeKit, Google Home), Home Assistant and OpenHAB via MQTT, remote control via web interface/Blynk/Telegram bot, send email and push notifications via Pushbullet/Pushover/Pushsafer, send SMS via Twilio/TinyGSM, unlock panel installer codes, and decode the Keybus protocol. category=Device Control url=https://github.com/taligentx/dscKeybusInterface architectures=avr,esp8266,esp32 From 424d850391d4a125c66471e6ec89370626646c0c Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Tue, 15 Mar 2022 12:22:04 +0300 Subject: [PATCH 46/49] Fix handling disarm access code #284 --- README.md | 3 ++- src/dscKeybusProcessData.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 590b6f5..bc36eca 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DSC Keybus Interface ![dscKeybusInterface](https://user-images.githubusercontent.com/12835671/105620980-5b356380-5dc8-11eb-93c2-e813751dda8a.png) -This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, notifications on alarm events, control of the system as a virtual keypad, unlocking installer codes, and emulating DSC panels to connect DSC keypads as general purpose input devices. +This library directly interfaces Arduino, esp8266, esp32, and esp32-s2 microcontrollers to [DSC PowerSeries](http://www.dsc.com/dsc-security-products/g/PowerSeries/4) and [Classic series](https://www.dsc.com/manual/29000203) security systems for integration with home automation, remote control as a virtual keypad, notifications on alarm events, unlocking installer codes, and emulating DSC panels to use DSC keypads as general purpose input devices. This enables existing DSC security system installations to retain the features and reliability of a hardwired system while integrating with modern devices and software for under $5USD in components. @@ -66,6 +66,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - Emulates a DSC panel to use DSC PowerSeries and Classic series keypads as physical input devices for any general purpose, without needing a DSC panel. * Panel time - retrieve current panel date/time and set a new date/time (including an example with NTP sync) * Panel installer code unlocking - determine the 4-digit panel installer code +* Virtual zone expander - the [expander branch](https://github.com/taligentx/dscKeybusInterface/tree/expander) can emulate a DSC zone expander module to add zones to the security system that are handled by the microcontroller - thanks to [Dilbert66](https://github.com/Dilbert66) for this contribution! * Direct Keybus interface: - Does not require the [DSC IT-100 serial interface](https://www.dsc.com/alarm-security-products/IT-100%20-%20PowerSeries%20Integration%20Module/22). * Designed for reliable data decoding and performance: diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index c8dcd25..3d00ec2 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -692,6 +692,13 @@ void dscKeybusInterface::processPanelStatus0(byte partition, byte panelByte) { processArmed(partitionIndex, false); processAlarmStatus(partitionIndex, false); processEntryDelayStatus(partitionIndex, false); + + // Disarmed by access codes 1-34, 40-42 + if (panelData[panelByte] >= 0xC0 && panelData[panelByte] <= 0xE4) { + byte dscCode = panelData[panelByte] - 0xBF; + processPanelAccessCode(partitionIndex, dscCode); + } + return; } @@ -731,13 +738,6 @@ void dscKeybusInterface::processPanelStatus0(byte partition, byte panelByte) { processPanelAccessCode(partitionIndex, dscCode); return; } - - // Disarmed by access codes 1-34, 40-42 - if (panelData[panelByte] >= 0xC0 && panelData[panelByte] <= 0xE4) { - byte dscCode = panelData[panelByte] - 0xBF; - processPanelAccessCode(partitionIndex, dscCode); - return; - } } From e8d9a974fc081555e9f83b8571f54e3c709840bc Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Tue, 15 Mar 2022 14:01:01 +0300 Subject: [PATCH 47/49] Skip setting armedChanged flag while arming until after access code is sent #284 --- src/dscKeybusInterface.cpp | 22 +++++++++++----------- src/dscKeybusProcessData.cpp | 6 ------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 9844ce0..48d6f18 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -209,17 +209,17 @@ bool dscKeybusInterface::loop() { // Processes valid panel data switch (panelData[0]) { - case 0x05: - case 0x1B: processPanelStatus(); break; - case 0x16: processPanel_0x16(); break; - case 0x27: processPanel_0x27(); break; - case 0x2D: processPanel_0x2D(); break; - case 0x34: processPanel_0x34(); break; - case 0x3E: processPanel_0x3E(); break; - case 0x87: processPanel_0x87(); break; - case 0xA5: processPanel_0xA5(); break; - case 0xE6: if (dscPartitions > 2) processPanel_0xE6(); break; - case 0xEB: if (dscPartitions > 2) processPanel_0xEB(); break; + case 0x05: // Panel status: partitions 1-4 + case 0x1B: processPanelStatus(); break; // Panel status: partitions 5-8 + case 0x16: processPanel_0x16(); break; // Panel configuration + case 0x27: processPanel_0x27(); break; // Panel status and zones 1-8 status + case 0x2D: processPanel_0x2D(); break; // Panel status and zones 9-16 status + case 0x34: processPanel_0x34(); break; // Panel status and zones 17-24 status + case 0x3E: processPanel_0x3E(); break; // Panel status and zones 25-32 status + case 0x87: processPanel_0x87(); break; // PGM outputs + case 0xA5: processPanel_0xA5(); break; // Date, time, system status messages - partitions 1-2 + case 0xE6: if (dscPartitions > 2) processPanel_0xE6(); break; // Extended status command split into multiple subcommands to handle up to 8 partitions/64 zones + case 0xEB: if (dscPartitions > 2) processPanel_0xEB(); break; // Date, time, system status messages - partitions 1-8 } return true; diff --git a/src/dscKeybusProcessData.cpp b/src/dscKeybusProcessData.cpp index 3d00ec2..395c749 100644 --- a/src/dscKeybusProcessData.cpp +++ b/src/dscKeybusProcessData.cpp @@ -406,12 +406,6 @@ void dscKeybusInterface::processPanel_0x27() { } armed[partitionIndex] = true; - if (armed[partitionIndex] != previousArmed[partitionIndex] || armedStay[partitionIndex] != previousArmedStay[partitionIndex]) { - previousArmed[partitionIndex] = armed[partitionIndex]; - previousArmedStay[partitionIndex] = armedStay[partitionIndex]; - armedChanged[partitionIndex] = true; - if (!pauseStatus) statusChanged = true; - } processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; From 2eb6bcdf0d1a979f8716c611200b10df4c7f99e7 Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 18 Mar 2022 17:25:27 +0300 Subject: [PATCH 48/49] Release 3.0 --- README.md | 3 ++- examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino | 6 ++--- .../HomeKit-HomeSpan/HomeKit-HomeSpan.ino | 22 +++++++++---------- library.json | 2 +- library.properties | 2 +- src/dscKeybusInterface.cpp | 1 + 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index bc36eca..440a61f 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - Unlock 6-digit installer codes ## Release notes -* develop +* 3.0 - New: DSC Classic series panel support: PC1500, PC1550, PC2550 - New: `KeypadInterface` and `KeypadInterface-MQTT` example sketches - emulate a DSC panel to connect DSC PowerSeries and Classic keypads as physical input devices for any general purpose, without needing a DSC panel. - New: `HomeKit-HomeSpan` example sketch (esp32) - integrate directly with Apple HomeKit as a native standalone accessory using [HomeSpan](https://github.com/HomeSpan/HomeSpan) @@ -117,6 +117,7 @@ This library uses a combination of hardware and timer interrupts to accurately c - Bugfix: `Pushbullet` example sketch updated TLS security certificate fingerprint - Bugfix: Workaround for [upstream esp32 TLS handshake issue](https://github.com/espressif/arduino-esp32/issues/6165) preventing making a TLS connection more than once. - Bugfix: Fixed `Homebridge-MQTT` handling exit delay states while multiple partitions are arming + - Bugfix: Resolved access codes not updating on disarm, changed arming access codes to update before armed status changes * 2.0 - New: [Telegram](https://www.telegram.org) bot example sketch - New: [OpenHAB](https://www.openhab.org) integration example sketch using MQTT diff --git a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino index 8dc4f30..d412801 100644 --- a/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino +++ b/examples/Arduino/TinyGSM-SMS/TinyGSM-SMS.ino @@ -83,17 +83,15 @@ const char* sendToPhoneNumbers[] = { #define phone_number_count (sizeof (sendToPhoneNumbers) / sizeof (const char *)) -// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the -// virtual keypad. +// Configures the Keybus interface with the specified pins. #define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3 #define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12 #define dscReadPin 5 // Arduino Uno: 2-12 -#define dscWritePin 6 // Arduino Uno: 2-12 #define SIM800RxPin 9 // Arduino Uno: 2-12 #define SIM800TxPin 10 // Arduino Uno: 2-12 // Settings -//NOTE: I kept getting "Keybus buffer overflow" when sketch was sending multiple messages at once (example: AC power trouble and trouble status ON) +// NOTE: I kept getting "Keybus buffer overflow" when sketch was sending multiple messages at once (example: AC power trouble and trouble status ON) bool notifyOnPartitionAlarm = true; bool notifyOnPowerTroubles = false; bool notifyOnKeypadAlarm = false; diff --git a/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino index 79bb192..2a645bc 100644 --- a/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino +++ b/examples/esp32/HomeKit-HomeSpan/HomeKit-HomeSpan.ino @@ -99,7 +99,7 @@ void setup() { // Accessory identification new SpanAccessory(); - new homeSpanIdentify("DSC Security System","DSC","000000","PC1864","2.5"); // Customizable name, manufacturer, serial number, model, firmware revision + new homeSpanIdentify("DSC Security System","DSC","000000","PC1864","3.0"); // Customizable name, manufacturer, serial number, model, firmware revision new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); // HomeKit requires specifying HAP protocol version 1.1.0 @@ -110,52 +110,52 @@ void setup() { // Partition 1: Security System accessory new SpanAccessory(); - new homeSpanIdentify("Partition 1","DSC","000000","Alarm","2.5"); + new homeSpanIdentify("Partition 1","DSC","000000","Alarm","3.0"); new dscPartition(1); // Set the partition number // Partition 8: Security System accessory new SpanAccessory(); - new homeSpanIdentify("Partition 8","DSC","000000","Alarm","2.5"); + new homeSpanIdentify("Partition 8","DSC","000000","Alarm","3.0"); new dscPartition(8); // Set the partition number // Zone 1: Contact Sensor accessory new SpanAccessory(); - new homeSpanIdentify("Zone 1","DSC","000000","Sensor","2.5"); // Set the zone name + new homeSpanIdentify("Zone 1","DSC","000000","Sensor","3.0"); // Set the zone name new dscZone(1); // Set the zone number // Zone 64: Contact Sensor accessory new SpanAccessory(); - new homeSpanIdentify("Zone 64","DSC","000000","Sensor","2.5"); // Set the zone name + new homeSpanIdentify("Zone 64","DSC","000000","Sensor","3.0"); // Set the zone name new dscZone(64); // Set the zone number // Fire alarm partition 1: Smoke Sensor accessory new SpanAccessory(); - new homeSpanIdentify("Fire 1","DSC","000000","Sensor","2.5"); // Set the fire sensor name + new homeSpanIdentify("Fire 1","DSC","000000","Sensor","3.0"); // Set the fire sensor name new dscFire(1); // Set the partition number // Fire alarm partition 8: Smoke Sensor accessory new SpanAccessory(); - new homeSpanIdentify("Fire 8","DSC","000000","Sensor","2.5"); // Set the fire sensor name + new homeSpanIdentify("Fire 8","DSC","000000","Sensor","3.0"); // Set the fire sensor name new dscFire(8); // Set the partition number // PGM output 1: Contact Sensor accessory new SpanAccessory(); - new homeSpanIdentify("PGM 1","DSC","000000","Sensor","2.5"); // Set the PGM output name + new homeSpanIdentify("PGM 1","DSC","000000","Sensor","3.0"); // Set the PGM output name new dscPGM(1); // Set the PGM output number // PGM output 14: Contact Sensor accessory new SpanAccessory(); - new homeSpanIdentify("PGM 14","DSC","000000","Sensor","2.5"); // Set the PGM output name + new homeSpanIdentify("PGM 14","DSC","000000","Sensor","3.0"); // Set the PGM output name new dscPGM(14); // Set the PGM output number // Command output 1: Switch accessory - this allows HomeKit to activate the PGM outputs assigned to the command output new SpanAccessory(); - new homeSpanIdentify("Command 1","DSC","000000","Sensor","2.5"); // Set the command output name + new homeSpanIdentify("Command 1","DSC","000000","Sensor","3.0"); // Set the command output name new dscCommand(1, 1, 1); // Set the command output number (1-4), one of the PGM outputs (1-14) assigned to the command output, and the assigned partition: dscCommand(cmd, pgm, partition); // Command output 4: Switch accessory - this allows HomeKit to activate the PGM outputs assigned to the command output new SpanAccessory(); - new homeSpanIdentify("Command 4","DSC","000000","Sensor","2.5"); // Set the command output name + new homeSpanIdentify("Command 4","DSC","000000","Sensor","3.0"); // Set the command output name new dscCommand(4, 4, 1); // Set the command output number (1-4), one of the PGM outputs (1-14) assigned to the command output, and the assigned partition: dscCommand(cmd, pgm, partition); // Starts the Keybus interface and optionally specifies how to print data. diff --git a/library.json b/library.json index 111aa5c..8b6991d 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/taligentx/dscKeybusInterface.git" }, - "version": "2.0", + "version": "3.0", "frameworks": "arduino", "platforms": "atmelavr, espressif8266, espressif32" } diff --git a/library.properties b/library.properties index 03ef3c8..38bf71f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DSC Keybus Interface -version=2.0 +version=3.0 author=Nikhil Choudhary maintainer=Nikhil Choudhary sentence=Directly interface Arduino, esp8266, and esp32 to DSC PowerSeries and Classic security systems for integration with home automation, remote control apps, notifications on alarm events, and emulating DSC panels to connect DSC keypads. diff --git a/src/dscKeybusInterface.cpp b/src/dscKeybusInterface.cpp index 48d6f18..e694851 100644 --- a/src/dscKeybusInterface.cpp +++ b/src/dscKeybusInterface.cpp @@ -501,6 +501,7 @@ void IRAM_ATTR dscKeybusInterface::dscClockInterrupt() { else switch (isrPanelData[0]) { static byte previousCmd05[dscReadSize]; static byte previousCmd1B[dscReadSize]; + case 0x05: // Status: partitions 1-4 if (redundantPanelData(previousCmd05, isrPanelData, isrPanelByteCount)) skipData = true; break; From 5635272b58feb488c909fa710d94de3d12144ddf Mon Sep 17 00:00:00 2001 From: Nikhil Choudhary Date: Fri, 18 Mar 2022 17:28:13 +0300 Subject: [PATCH 49/49] Release 3.0 --- .github/workflows/compile_examples.yml | 175 ------------------------- .github/workflows/compile_library.yml | 53 -------- .github/workflows/cpp_lint.yml | 26 ---- 3 files changed, 254 deletions(-) delete mode 100644 .github/workflows/compile_examples.yml delete mode 100644 .github/workflows/compile_library.yml delete mode 100644 .github/workflows/cpp_lint.yml diff --git a/.github/workflows/compile_examples.yml b/.github/workflows/compile_examples.yml deleted file mode 100644 index 7aedd49..0000000 --- a/.github/workflows/compile_examples.yml +++ /dev/null @@ -1,175 +0,0 @@ -name: Compile Examples - -on: - push: - paths-ignore: - - '.github/workflows/cpp_lint.yml' - - '.github/workflows/compile_library.yml' - pull_request: - paths-ignore: - - '.github/workflows/cpp_lint.yml' - - '.github/workflows/compile_library.yml' - -jobs: - arduino: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino" - - "examples/Arduino/KeybusReader/KeybusReader.ino" - - "examples/Arduino/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/Arduino/Status/Status.ino" - - "examples/Arduino/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/Arduino/Unlocker/Unlocker.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/arduino-libraries/Ethernet \ - https://github.com/PaulStoffregen/Time - - - name: Run PlatformIO Examples - run: pio ci --board=uno - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} - - esp8266: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/esp8266/Email/Email.ino" - - "examples/esp8266/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/esp8266/Homey/Homey.ino" - - "examples/esp8266/KeybusReader/KeybusReader.ino" - - "examples/esp8266/KeybusReaderIP/KeybusReaderIP.ino" - - "examples/esp8266/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/esp8266/Pushbullet/Pushbullet.ino" - - "examples/esp8266/Status/Status.ino" - - "examples/esp8266/Telegram/Telegram.ino" - - "examples/esp8266/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/esp8266/Twilio-SMS/Twilio-SMS.ino" - - "examples/esp8266/Unlocker/Unlocker.ino" - - "examples/esp8266/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" - - "examples/esp8266/VirtualKeypad-Web/VirtualKeypad-Web.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ - https://github.com/blynkkk/blynk-library \ - https://github.com/me-no-dev/ESPAsyncWebServer - - - name: Run PlatformIO Examples - run: pio ci --board=nodemcuv2 - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} - - esp32: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - "examples/esp32/Email/Email.ino" - - "examples/esp32/HomeAssistant-MQTT/HomeAssistant-MQTT.ino" - - "examples/esp32/Homebridge-MQTT/Homebridge-MQTT.ino" - - "examples/esp32/Homey/Homey.ino" - - "examples/esp32/KeybusReader/KeybusReader.ino" - - "examples/esp32/KeybusReaderIP/KeybusReaderIP.ino" - - "examples/esp32/OpenHAB-MQTT/OpenHAB-MQTT.ino" - - "examples/esp32/Pushbullet/Pushbullet.ino" - - "examples/esp32/Status/Status.ino" - - "examples/esp32/Telegram/Telegram.ino" - - "examples/esp32/TimeSyncNTP/TimeSyncNTP.ino" - - "examples/esp32/TinyGSM-SMS/TinyGSM-SMS.ino" - - "examples/esp32/Twilio-SMS/Twilio-SMS.ino" - - "examples/esp32/Unlocker/Unlocker.ino" - - "examples/esp32/VirtualKeypad-Blynk/VirtualKeypad-Blynk.ino" - - "examples/esp32/VirtualKeypad-Web/VirtualKeypad-Web.ino" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install 3rd party dependecies - run: | - pio lib -g install \ - https://github.com/SofaPirate/Chrono \ - https://github.com/bblanchon/ArduinoJson \ - https://github.com/taligentx/dscKeybusInterface \ - https://github.com/knolleary/pubsubclient \ - https://github.com/athombv/homey-arduino-library \ - https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot \ - https://github.com/blynkkk/blynk-library \ - https://github.com/me-no-dev/ESPAsyncWebServer \ - https://github.com/vshymanskyy/TinyGSM - - - name: Run PlatformIO Examples - run: pio ci --board=lolin32 - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/compile_library.yml b/.github/workflows/compile_library.yml deleted file mode 100644 index 86b20de..0000000 --- a/.github/workflows/compile_library.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Compile Library - -on: - push: - paths-ignore: - - '.github/workflows/cpp_lint.yml' - - '.github/workflows/compile_examples.yml' - - 'examples/**' - pull_request: - paths-ignore: - - '.github/workflows/cpp_lint.yml' - - '.github/workflows/compile_examples.yml' - - 'examples/**' - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - board: - - "nodemcuv2" - - "lolin32" - - "uno" - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - - name: Create main file - run: | - echo "#include " >> src/main.ino - echo "void setup() {}" >> src/main.ino - echo "void loop() {}" >> src/main.ino - - - name: Run PlatformIO - run: pio ci --board=${{ matrix.board }} src diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml deleted file mode 100644 index ed81cc7..0000000 --- a/.github/workflows/cpp_lint.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: cpplint - -on: - pull_request: - paths-ignore: - - '.github/workflows/compile_*.yml' -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: cpplint - uses: reviewdog/action-cpplint@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - reporter: github-pr-check - flags: --linelength=100 - filter: "-whitespace/tab\ - ,-readability/braces\ - ,-whitespace/braces\ - ,-whitespace/comments\ - ,-whitespace/indent\ - ,-whitespace/newline\ - ,-whitespace/operators\ - ,-whitespace/parens\ - " \ No newline at end of file