I toyed a bit with the Paradox security protocol last year, tapping into the keypad’s interface in an attempt to decode the messages being exchanged between the base station and the keypads. I had some success: I was able to see the keys being pressed, but what was more important to me was the state of the alarm system and each room’s PIR + each door/window’s reed switch. That didn’t work out very well and I quickly abandoned the project.
Until today. I decided to revisit my work and inspect the options at the base station Spectra SP7000. I noticed a 4 pin port labeled ‘Serial’ and hooked my oscilloscope to identify what’s on there. Looked like the port provides ~13V and TX/RX lines at 5V. I hooked my FTDI cable set to 9600 Baud, 1 start bit, 8 data bits, 1 stop bit and no parity and it started spitting out chunks of 37 bytes. I then remembered that someone sent me a Paradox protocol documentation over the email following my last year’s attempt to hack in, but it seemed quite different from what I was getting at the keypad level and I just ignored it. Looking at it again today showed me exactly what I was looking for: the 37 bytes protocol:
I am interested in the event group, event sub-group and the command byte.
Event codes 00 and 01 are zone closed and zone opened respectively and the event sub-group specifies the zone. There are other event codes as well, but I am only interested in these.
I decided to use a JeeNode v6 to tap into the serial port and transmit the event to my home automation system. The JeeNode runs on 3.3V so I had to create a voltage divider (used 10K:10K, one of the resistors isn’t clearly visible on the picture below) to bring down the 5V to more Jee-friendly levels:
The MCP1702 on the JeeNode will handle the ~13V that the serial port is providing (it is at the limit indeed). Some more pictures of the setup:
Finally here is my code:
#include <JeeLib.h> // https://github.com/jcw/jeelib #define myNodeID 3 // RF12 node ID in the range 1-30 #define network 210 // RF12 Network group #define freq RF12_868MHZ // Frequency of RFM12B module #define RETRY_PERIOD 250 // How soon to retry (in ms) if ACK didn't come in #define RETRY_LIMIT 5 // Maximum number of times to retry #define ACK_TIME 25 // Number of milliseconds to wait for an ack char inData[38]; // Allocate some space for the string byte index = 0; // Index into array; where to store the character #define LED 6 typedef struct { byte armstatus; byte event; byte sub_event; byte dummy; } Payload; Payload paradox; void setup() { pinMode(LED,OUTPUT); blink(100); delay(1000); rf12_initialize(myNodeID,freq,network); // Initialize RFM12 with settings defined above Serial.begin(9600); Serial.flush(); // Clean up the serial buffer in case previous junk is there Serial.println("Paradox serial monitor is up"); blink(1000); serial_flush_buffer(); } void readSerial() { while (Serial.available()<37 ) {} // wait for a serial packet { index=0; while(index < 37) // Paradox packet is 37 bytes { inData[index++]=Serial.read(); } inData[++index]=0x00; // Make it print-friendly } } void loop() { readSerial(); Serial.println(inData); if((inData[0] & 0xF0)==0xE0){ // Does it look like a valid packet? paradox.armstatus=inData[0]; paradox.event=inData[7]; paradox.sub_event=inData[8]; rfwrite(); // Send data via RF blink(50); } else //re-align buffer { blink(200); serial_flush_buffer(); } } void serial_flush_buffer() { while (Serial.read() >= 0) ; // do nothing } void blink(int duration) { digitalWrite(LED,HIGH); delay(duration); digitalWrite(LED,LOW); } // Wait a few milliseconds for proper ACK static byte waitForAck() { MilliTimer ackTimer; while (!ackTimer.poll(ACK_TIME)) { if (rf12_recvDone() && rf12_crc == 0 && rf12_hdr == (RF12_HDR_DST | RF12_HDR_CTL | myNodeID)) return 1; } return 0; } //-------------------------------------------------------------------------------------------------- // Send payload data via RF //------------------------------------------------------------------------------------------------- static void rfwrite(){ for (byte i = 0; i <= RETRY_LIMIT; ++i) { // tx and wait for ack up to RETRY_LIMIT times while (!rf12_canSend()) rf12_recvDone(); rf12_sendStart(RF12_HDR_ACK, ¶dox, sizeof paradox); rf12_sendWait(0); // Wait for RF to finish sending byte acked = waitForAck(); // Wait for ACK if (acked) { return; } // Return if ACK received delay(RETRY_PERIOD); // If no ack received wait and try again } }
The code is running for few hours already and all is as expected.
I have a Raspberry Pi with RFM2Pi board running a read-only Raspbian that does my home automation, it captures the wireless packets using a node-red flow. With this new data available, my smart house will be able to know where its inhabitants are (PIR) and take various decisions based on that. I can also use the window/door state information (Reed switches) for smarter climate control, i.e. turn off the thermostat in a room that has the window open.
I see you never use special register after rf_initialize for less baudrate and other. I use the 433 MHz Modules, but i have only connect for some meters (3-4) and after that, no connect. If you use some tricks or if your connect better cuz you use the 868 MHz modules. At your photo on top of every website i see you build the antenna as a spiral, if this better to connect the RFM Modules or just for fun? 🙂
Great work!
I’m looking to build a similar system but with an ethernet interface on arduino.
Could you possibly send me the Paradox protocol documentation doc you have?
Have you perhaps gotten any further with this project?