Wednesday, July 22, 2009

Burglar Alarm System which automatically updates Twitter status

As mentioned in my first blog , I was looking at possible home automation applications for my media server machine which has tons of IO (4 digital inputs and 4 digital outputs ,either open drain or push pull) and came across this idea to build my own burglar alarm system which automatically updates my twitter status.

My media server/burglar alarm controller :




This was just a base idea, and idea of updating twitter status based upon any event can be extended onto many applications.

This was just a base idea, and idea of updating twitter status based upon any event can be extended onto many applications. The whole burgler alarm system consists of my media server which acts as burglar alarm controller, a Java code which monitors the controller IO and updates my twiter status and a C code which serves as a simulated Burglar alarm sensor which toggles the IO.

The Java code is basically a Java native interface (jni) on top of C code which talks to the digital IO on my server box (green strip). The C code provides functions which talk to the underlying registers for the IO using inb and outb, which are eventually called by the Java application. I used twitter4j, which is basically an open source Java API which interacts directly with the twitter API. If you use eclipse as your java IDE all you have to do is include twitter4j.jar file in your reference libraries and you can start using the twitter4j classes directly.

I used the ant utility to generate the jni header file and then develop the underlying c code. This is how my code looks like:

Java class Process.java which provides the JNI interface :


public class Process {

private static final String LABEL = "Process";

private boolean lock;

static {

System.loadLibrary("java_apollo_system");

}

/* definitions for input and output ports and on off actions. The port numbers and the action values are the values that are passed to gpio_toggle function to toggle individual ports and to gpio_read to read individual input port status*/

public static final int input1 = 0x6A3;

public static final int input2 = 0x6A4;

public static final int input3 = 0x6A5;

public static final int input4 = 0x6A6;

public static final int output1 = 0x6A7;

public static final int output2 = 0x6A8;

public static final int output3 = 0x6A9;

public static final int output4 = 0x6AA;

public static final int on = 1;

public static final int off = 0;

public synchronized void set_up_port(int port, int porttype) {

this.lock = true;

com.pravin.linux.util.Process.native_set_up_port(port,porttype);

this.lock = false;

}

public synchronized void toggle_port(int command) {

this.lock = true;

com.pravin.linux.util.Process.native_toggle_port(command);

this.lock = false;

}

public synchronized int read_data_port_status() {

this.lock = true;

int i = com.pravin.linux.util.Process.native_read_data_port_status();

this.lock = false;

return i;

}

public synchronized void set_data_port_pushpull() {

this.lock = true;

com.pravin.linux.util.Process.native_set_data_port_pushpull();

this.lock = false;

}

public synchronized void set_data_port_opendrain() {

this.lock = true;

com.pravin.linux.util.Process.native_set_data_port_opendrain();

this.lock = false;

}

public synchronized void gpio_toggle(int portnumber,int action) {

this.lock = true;

com.pravin.linux.util.Process.native_gpio_toggle(portnumber,action);

this.lock = false;

}

public synchronized int gpio_read(int portnumber) {

this.lock = true;

int i = com.pravin.linux.util.Process.native_gpio_read(portnumber);

this.lock = false;

return i;

}

private synchronized static native void native_set_up_port(int port, int porttype);/* sets up individual ports as pushpull or opendrain. argument is portnumber from output1 to outpu4 and input1 to input4 and 2nd argument is 0 for push pull and 1 for open drain */

private synchronized static native void native_toggle_port(int command);/* sets up o/p ports as per bitwise mask supplied as arument. 0 for bit corresponding to a particular port turns on teh port and 1 makes it off. The bits are arranged from o/p4 being msb followed by o/p 3 o/p2 o/p1 i/p4 i/p3 i/p2 i/p 1 (i/p 1 being lsb) so to set up output4 on teh command to be supplied as argument would be (0x70 == 0111 xxxx where x is dont care since you cnat write to bits corresponding to inputs)*/

private synchronized static native int native_read_data_port_status();

/* return bitwise status of i/p and o/p ports with same masking pattern followed for togle port method*/

private synchronized static native void native_set_data_port_pushpull();/* sets up all ports as push pull*/

private synchronized static native void native_set_data_port_opendrain();/* sets up all ports as open drain*/

private synchronized static native void native_gpio_toggle(int portnumber,int action);/* sets up individual output ports as on or off while retaining previous status of rest of the ports. Argument passed is portnumber from outpu1 to output4 and action as on or off*/

private synchronized static native int native_gpio_read(int portnumber);/* read status of inividual ports. The argument supplied is port number from input1 to input4 and the return value is 0 for port off and 1 for port on. */

public Process ()

{ toggle_port(0xF0);

}

}


Java test class which uses the process class methods and calls for twitter update on receiving a burglar event which is toggling of input port from 1 to 0, in this case I monitor input 4 which is connected to output 1 (output 1 acts as simulated burglar sensor). The map is as follows (op1 op2 op3 op4 ip1 ip2 ip3 ip4) ,so at startup all inputs and outputs are set to low and the bit map is as follows : 1111 1111 (note : for the digital I/O’s 1 corresponds to off). So the burglar alarm will set when the bit map becomes 0111 1110.

package com.pravin.linux.util.test;

import java.util.Random;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;

import com.pravin.linux.util.Process;//import the class implementing JNI

public class test {

public static void main (String[] args)

{
Process p = new Process();//instantiate the process class
p.set_data_port_opendrain();//set all ports as opendrain
Random randomGenerator = new Random();
int k = p.read_data_port_status();//read status of ports
while (true){
k = p.read_data_port_status();//read status of ports
if ((k & 0x0001)== 0x0000){
System.out.println("val: "+k);
int randomInt = randomGenerator.nextInt(100);
String twitterStatus = ("generate alarm with"+ randomInt +"number");

try {
Twitter twitter = new Twitter("username","password");
System.out.println("val: "+k);
Status status = twitter.updateStatus(twitterStatus);
System.out.println("val: "+k);
break;
} catch (TwitterException e) {
System.out.println("twitter not updated");
e.printStackTrace();
}

}
}
}
}

Note the use of twitter4j api and you should use your actual user name and password in the twitter api. Initially while testing I was updating the status with the same string and the API didn’t allow me to update the same status more than once , so I just used a random number generator to change the status on every event ( I know there can be other better methods but it was 1 am when I was about to complete the whole app). Those whole follow me on twitter must have noted the status message yesterday “my twitter hack works if you read this “once and for subsequent testing the twitter4j api didn’t allow me to update the same status.

This is the underlying c code which talks to the IO:

/* user space code to toggle the front panel relays in appollo boards. The main should include the function iopl(3) to access io ports from user space */

/*header files*/

#include

#include stat.h>

#include

#include

#include

#includeio.h>

#include "c/include/com_pravin_linux_util_Process.h"

/* Port configuration addresses on GPIO*/

#define D0 0x6A3

#define D1 0x6A4

#define D2 0x6A5

#define D3 0x6A6

#define D4 0x6A7

#define D5 0x6A8

#define D6 0x6A9

#define D7 0x6AA

/*GPIO data register*/

# define data_reg 0x6CB

/* output types*/

#define output1 D4

#define output2 D5

#define output3 D6

#define output4 D7

/* input types*/

#define input1 D0

#define input2 D1

#define input3 D2

#define input4 D3

/* action definitions */

#define on 1

#define off 0

/*function prototypes*/

void toggle_data_ports (int);

void set_up_port (int ,int );

int read_data_port_status(void);

void set_data_port_pushpull (void);

void set_data_port_opendrain (void);

void gpio_toggle (int ,int);

int gpio_read (int);

/*function to set up all individual ports (D0-D3 i/p and D4-D7 o/p). Should be used as part of initialization routine.port=port number and for port type (0 = push pull ; 1 = open drain) */

void set_up_port(int port,int porttype)

{

iopl(3);

int portnumber =0;

portnumber = (port == D0)||(port == D1)||(port == D2)||(port == D3);

if (portnumber == 1)

{

switch (porttype)

{

case 0 : outb (1,port);

break;

case 1 : outb (0x81,port);

break;

default : printf ("invalid porttype");

break;

}

} else

{switch (porttype)

{

case 0 : outb (0,port);

break;

case 1 : outb (0x80,port);

break;

default : printf ("invalid porttype");

break;

}

}

}

/* function to toggle o/p ports D4-D7.

1 sets up a port to false while a 0 means on. for ex to set up D7 as on write 0111 0000 to the GPIO reg */

void toggle_data_ports (int command)

{

iopl(3);

outb ((command ),data_reg);

}

/* function to check status of data ports. 0 corresponds to on and 1 corresponds to off */

int read_data_port_status(void)

{

iopl(3);

return inb (data_reg);

}

/* function to set all ports as pushpull*/

set_data_ports_pushpull ()

{

iopl (3);

/* set up ports*/

set_up_port(D0,0);

set_up_port(D1,0);

set_up_port(D2,0);

set_up_port(D3,0);

set_up_port(D4,0);

set_up_port(D5,0);

set_up_port(D6,0);

set_up_port(D7,0);

}

/* function to set all ports as opendrain*/

set_data_ports_opendrain ()

{

iopl (3);

/* set up ports*/

set_up_port(D0,1);

set_up_port(D1,1);

set_up_port(D2,1);

set_up_port(D3,1);

set_up_port(D4,1);

set_up_port(D5,1);

set_up_port(D6,1);

set_up_port(D7,1);

}

/* this function is used to toggle the individual gpio output ports while retaining previous status of rest of ports. The argument supplied is the port number from output1 to output4 and the action which can be on or off */

gpio_toggle (int portnumber,int action)

{

iopl(3);

int j;

switch (portnumber)

{

case output1 : if (action==1)

{j = read_data_port_status();

toggle_data_ports((j& 0xE0));

} else

{j = read_data_port_status();

toggle_data_ports((j|0x10));

};

break;

case output2 : if (action==1)

{j = read_data_port_status();

toggle_data_ports((j& 0xD0));

} else

{j = read_data_port_status();

toggle_data_ports((j|0x20));

};

break;

case output3 : if (action==1)

{j = read_data_port_status();

toggle_data_ports((j& 0xB0));

} else

{j = read_data_port_status();

toggle_data_ports((j|0x40));

};

break;

case output4 : if (action==1)

{j = read_data_port_status();

toggle_data_ports((j& 0x70));

} else

{j = read_data_port_status();

toggle_data_ports((j|0x80));

};

break;

default : printf ("invalid portnumber");

break;

}

}

/* this function is used to find the status of individual input ports. The argument supplied is the port number input1 to input 4 and the return value is 0 for off and 1 for on. */

int gpio_read (int portnumber)

{

iopl(3);

int i,j;

switch (portnumber)

{

case input1 : j = read_data_port_status();

if ((j|0xFE)==0xff){

i=0;

} else

{i=1;

};

break;

case input2 : j = read_data_port_status();

if ((j|0xFD)==0xff){

i=0;

} else

{i=1;

};

break;

case input3 : j = read_data_port_status();

if ((j|0xFB)==0xff){

i=0;

} else

{i=1;

};

break;

case input4 : j = read_data_port_status();

if ((j|0xF7)==0xff){

i=0;

} else

{i=1;

};

break;

default : printf ("invalid portnumber");

break;

}

return i;

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_set_up_port

* Signature: (II)V. JNI function to set up individual port as pushpull or open drain. (argument is portnumber and porttype(0 for push pull/1 open drain)

*/

JNIEXPORT void JNICALL Java_com_pravin_linux_util_Process_native_1set_1up_1port

(JNIEnv *env, jclass class, jint port, jint porttype) {

set_up_port( port, porttype);//set up individual ports

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_toggle_port

* Signature: (I)V. JNI function to toggle individual ports by write to data register at ox6cb (0 - on ; 1 is off (D7 D6 ....D0))

*/

JNIEXPORT void JNICALL Java_com_pravin_linux_util_Process_native_1toggle_1port

(JNIEnv *env, jclass class, jint command){

toggle_data_ports (command);

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_read_data_port_status

* Signature: ()I JNI function to read individual port status.

*/

JNIEXPORT jint JNICALL Java_com_pravin_linux_util_Process_native_1read_1data_1port_1status

(JNIEnv *env, jclass class) {

int i = read_data_port_status();

return i;

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_set_data_port_pushpull

* Signature: ()V JNI function to set all ports as pushpull

*/

JNIEXPORT void JNICALL Java_com_pravin_linux_util_Process_native_1set_1data_1port_1pushpull

(JNIEnv *env, jclass class){

set_data_ports_pushpull ();

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_set_data_port_opendrain

* Signature: ()V JNI function to set all ports as opendrain

*/

JNIEXPORT void JNICALL Java_com_pravin_linux_util_Process_native_1set_1data_1port_1opendrain

(JNIEnv *env, jclass class){

set_data_ports_opendrain ();

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_gpio_toggle

* Signature: (CC)V

JNI function to set up output ports as on or off. Argument passed is portnumber from outpu1 to output4 and action as on or off

*/

JNIEXPORT void JNICALL Java_com_pravin_linux_util_Process_native_1gpio_1toggle

(JNIEnv *env, jclass class, jint portnumber,jint action){

gpio_toggle (portnumber,action);

}

/*

* Class: com_pravin_linux_util_Process

* Method: native_gpio_read

* Signature: (I)I

JNI function to read status of inividual ports. The argument supplied is port number from input1 to input4 and the return value is 0 for port off and 1 for port on.

*/

JNIEXPORT jint JNICALL Java_com_pravin_linux_util_Process_native_1gpio_1read

(JNIEnv *env, jclass class, jint portnumber) {

gpio_read (portnumber);

}


The c code is developed based upon the header file generated by the ant utility using the following build.xml file:



dist" basedir=".">

src" location="src"/>

dist" location="dist"/>

classpath">

init">

dir="${build}"/>

init"

description="compile the source " >

srcdir="${src}" destdir="${build}">

refid="classpath"/>

dist" depends="compile"

description="generate the distribution" >

dir="${dist}/lib"/>

jarfile="${dist}/lib/java_system.jar" basedir="${build}"/>

javah" depends="compile"

description="Generate C header file">

dir="c/include"/>

destdir="c/include"

class="com.pravin.linux.util.Process"

classpath="build"/>

javah"

description="Compile the JNI shared library">

Compiling the shared library using "Makefile"

dir="c/lib"/>

dir="c/src"

output="c/lib/make.out.txt"

failonerror="true">

description="clean up" >

dir="${build}"/>

dir="${dist}"/>



The simulated burglar sensor is nothing but the same c code which operates the IO but runs as standalone executable and toggles output 1 from 1 to 0 which makes input 4 from 1 to 0 which is detected by the test class and updates my twitter status.

I started off with a motion sensor which detected motion and toggled its output but I ended blowing it up and switched to simulated burglar sensor.

The app works perfectly fine now and my initial thoughts were to write a PHP script which calls the C code and updates the twitter status using curl and twitter api calls through http. But I found twitter4j and it made the code much easier to write.

So with a media server streaming media across my apartment and to my PS3, and a burglar alarm system functioning it looks like my server is heading good towards being a complete home automation kit. If you plan to use the code and if your system has digital IO please replace the IO address map in the code with your systems IO map. Using wrong address map can cause serious damage to your system.