October 2012
I spotted a post by Lior Hass on Hackaday a little while back where he described his
Alarmino project where his alarm system now connects with an Arduino and a custom board to simulate the phone line and interpret Contact-ID protocol. He could then get the system to SMS him in the case of an alarm. In his case, he did this in lieu of using a central monitoring service and it only reported events that would have been sent to the central monitoring service. But suppose someone was using a central monitoring service and just wanted to augment the system with additional potentially detailed and configurable notifications.System designers often choose to transmit system state through serial protocols. For instance, one such system is described
here. Systems like this use a common bus between keypads and the central system with power, ground, and clock and data lines. It would be nice to make use of such system state information on computers like the Raspberry Pi. But one has to be careful not to damage the Pi's digital inputs as the voltages may be quite a bit higher (e.g. 12V) than the Raspberry Pi can handle. Opto-isolators can be useful in protecting the Raspberry Pi or micro-controllers like the Arduino. Depending on the speeds of the serial signals you're seeking to monitor, reading them reliably on the Raspberry Pi can be a challenge. For the signals I was seeking to read, while the Raspberry Pi was capable of reading them, reliability was poor even when I gave the process priority because of the possibility of it being pre-empted. And if the serial signal being transmitted has no checksums or other mechanism built in to detect errors, one really can't afford to miss any parts of the transmission. This is a perfect example of where the Raspberry Pi can work cooperatively with a micro-controller like the Arduino. If the Arduino could do the monitoring and report state changes to the Raspberry Pi over its serial connection, the Pi could handle the more sophisticated signal interpretation and notifications.In the configuration shown below, the Raspberry Pi both powers and receives serial information from an Arduino Uno and custom shield by USB. The Raspberry Pi is shown with
WiFi dongle,
case, and
low-profile microSD card adapter from Adafruit.
I used an
Adafruit Proto Shield to mount the terminal blocks and opto-isolator for the Arduino. I happened to have some TLP-624-4 chips on hand so I used one but I only needed two of the four channels that chip provides (to handle clock and data). In my case, I needed 1K resistors on the input side and actually 180 Ohm pull-ups on the output side to read the signal reliably. In the case of the Arduino, the output pull-ups are to 5v and earlier when I was reading the outputs using the Gpio lines on the Raspberry Pi, they were pulled up to the 3.3v line. The terminal blocks on the left of the photo are to the monitored system. I added the optional terminal blocks on the right if I wanted to pass the signals through to another device on the bus. The higher voltage red power wire is only there for the purpose of the pass through to other devices. Any two digital lines on the Arduino would suffice for reading the signal -- I chose 6 & 7 (actually originally I chose 7 & 8 but realized that I might want to try the shield with the Electric Imp Shield for wireless transmission which uses 8 & 9 by default). I've included a Fritzing-generated breadboard and schematic diagram later in the article to make the circuit more clear.
The logic trace below shows an example of data being received from the bus. The SPI protocol analyzer on the
Saleae Logic Analyzer does a good job of interpreting the bytes being transmitted (Caution: make sure you know the voltage levels you are testing are within the allowable range for your analyzer). In this case, I took data to be valid on the trailing edge of the clock pulse.
The trace below shows a similar system state (but a different sample) as seen after the opto-isolator. As a result of the circuit, the original signal is inverted -- which actually turns out to be convenient in interpreting system state.
In the case of this serial signal, I'm interested in elements of the system state which are being repeatedly transmitted (e.g. the first 5 bytes of the transmitted 7 byte groups). So I'd like the Arduino to transmit any groups where a change happens in the first 5 bytes. A
simple Arduino program to do this is shown below.
//Capture Serial Bytes
#define CLK 7 //clock pin
#define DAT 6
//data pin
#define LEN 7 //# of serial bytes to read per group
#define SIG 5 //# of signicant bytes to detect state change
int buf[LEN];
int last[LEN];
int thresh=4000; //minimum delay per group start (on 328p)
int msk=0x80;
int cnt,i,j,scnt,pnt,cur,bit,bcnt,dif;
void setup(){
Serial.begin(9600);
pinMode(CLK,INPUT); //needed external pullups
digitalWrite(CLK,1);
pinMode(DAT,INPUT); //needed external pullups
digitalWrite(DAT,1);
}
void loop(){
pnt=0;
bcnt=0;
msk=0x80;
cur=0;
while(1) {
while(digitalRead(CLK)==0);
cnt=0;
while(digitalRead(CLK)==1) {
cnt++;
}
if (cnt>thresh) break;
}
scnt=cnt;
while(1) {
while(digitalRead(CLK)==0);
bit=digitalRead(DAT);
bcnt++;
if (bit==1) {
cur|=msk;
}
msk>>=1;
if (msk==0) {
msk=0x80;
buf[pnt++]=cur;
cur=0;
}
if (bcnt>(LEN*8-1)) break;
while(digitalRead(CLK)==1);
}
dif=0;
for(i=0;i<SIG;i++) {
if (last[i]!=buf[i]) {
dif=1;
break;
}
}
if (dif==1) {
//Serial.print("scnt=");
//Serial.println(scnt);
Serial.write(LEN);
for(i=0;i<LEN;i++) {
//Serial.print(String(buf[i],HEX));
//Serial.print(" ");
Serial.write(buf[i]);
}
//Serial.println();
for(i=0;i<SIG;i++) last[i]=buf[i];
}
}
My relatively simple protocol was to send a length count followed by that number of bytes from the Arduino every time a state change was observed.On the Raspberry Pi, I wrote
a Python program to interpret the state information coming back from the Arduino and to email change notifications. The interpretation starts at the bit level and uses a set of re-write rules to provide a flexible way to build up higher-level interpretations or to re-arrange the way the text appears. One shortcoming is that I don't yet detect cycles in the state information. In certain situations, a sequence of messages itself represents a state (e.g. when one or more doors/windows are open). Because the current logic waits for state changes to stop before sending -- the result could be a pretty long notification. I'm planning to update the code shortly to recognize cyclic sets of state notifications as a single state. Additionally, the code should be updated to provide different levels of detail for different addresses or means of notification. The code shown here sends every displayed state change (like a door opening) -- while one might want only priority notifications of actual alarm conditions when the system is armed.In order to run the Python program as a service which automatically starts on the Pi, I used
a script which I stored as /etc/init.d/monitor. Then I used "update-rc.d monitor defaults" to ensure that it starts on boot. You can start and stop the program manually with "/etc/init.d/monitor start" and "/etc/init.d/monitor stop".