Use of a More Recent Browser is Recommended
This site uses modern HTML5 features and may not render correctly on earlier browsers. If you use Internet Explorer, version 11 and later is recommended.
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".