A Doorbell I Can Hear
3rd Jan 2010, 21:43:22
When normal people want to be able to know that someone is at the door even when they are in a remote part of the house or garden, they often purchase a wirless doorbell of the sort sold in DIY shops. After an incident involving a day off work and a nearly missed UPS delivery, I decided to see if this could work for me.
I started out with a simple wireless unit. This worked OK, but I didn't like the fact that the doorbell now relied on a battery in order to operate. This isn't resilient enough for a mission-critical service like my doorbell. I did what cam naturally to me; I took the chime unit apart.
To my delight, I found that the innerds of the chime are very simple, there's a tiny PCB with a few wires connecting up the LED and loudspeaker. Maybe this can be useful. By way of a little Googling, I found that some helpful fellow, Roo, had already done this project with the exact same kit using something called an Arduino board. Excellent.
A couple of days later, my Arduiono Duemillanove arrived in the post from oomlout.co.uk. I wasted no time in getting the soldering iron heated up:
The black wire at the top of the doorbell PCB is connected to the Arduino board's analogue pin 0, this senses when a circuit is made and triggers an event. The other black wire is connected to ground, the red wire connects to the +3V pin which obviates the need for any batteries; the whole thing runs off USB power.
I used the Arduino sketch from
Roo's site to monitor the doorbell and connected the board's USB port to my Debian Power Mac.
The Linux kernel already has the drivers needed, so the Arduino just shows up as
/dev/ttyUSB0
. I then used this simple perl script to check for an event:
#!/usr/bin/perl # # James Stocks cleverly stole this from: # http://www.perlmonks.org/?node_id=276111 # # Author: David Drake # Date: July 18, 2003 # Requirements: Device::SerialPort 0.22 (from cpan July 2003) # # Version: 1.0 use Device::SerialPort; use Time::gmtime; $LOGDIR = "/var/log"; # path to data file $LOGFILE = "arduino.log"; # file name to output to $PORT = "/dev/ttyUSB0"; # port to watch # # Serial Settings # #make the serial port object $ob = Device::SerialPort->new ($PORT) || die "Can't Open $PORT: $!"; $ob->baudrate(9600) || die "failed setting baudrate"; $ob->parity("none") || die "failed setting parity"; $ob->databits(8) || die "failed setting databits"; # not needed #$ob->stty_icrnl(1) || die "failed setting convert cr to new line" ; $ob->handshake("none") || die "failed setting handshake"; $ob->write_settings || die "no settings"; # # open the logfile, and Port # open(LOG,">>${LOGDIR}/${LOGFILE}") ||die "can't open smdr file $LOGDIR/$LOGFILE for append: $SUB $!\n"; select(LOG), $| = 1; # set nonbuffered mode, gets the chars out NOW open(DEV, "<$PORT") || die "Cannot open $PORT: $_"; # # Loop forver, logging data to the log file # while($_ =){ # print input device to file $gmc = gmctime(); print LOG $gmc," ",$_; } undef $ob;
All very interesting, but not yet useful. With this proof of concept working, I started getting this hooked up to my Asterisk system. I modified the perl script so that as well as logging doorbell presses it could ring the house phones to let me know someone was there:
# # Loop forver, logging data to the log file # @wgetargs = ("/usr/bin/wget", "http://pabx/cgi-bin/arduino.pl"); while($_ =){ # print input device to file $gmc = gmctime(); print LOG $gmc," ",$_; system(@wgetargs) == 0 or die "system @wgetargs failed: $?" } undef $ob;
The CGI script on my Asterisk box places a call to all the house phones using the Asterisk Manager API like so:
#!/usr/bin/perl use Net::Telnet (); # make a connection to the asterisk system and send the call details my $tn = new Net::Telnet (Port => 5038, Prompt => '/.*[\$%#>] $/', Output_record_separator => '', Errmode => 'return' ); $tn->open("127.0.0.1"); $tn->waitfor('/0\n$/'); $tn->print("Action: Login\nUsername: foo\nSecret: bar\n\n"); $tn->waitfor('/Authentication accept*/') or die "Asterisk Manager Interface login failed, verify username and password: ", $tn->lastline; ### Make a call now $tn->print("Action: originate\nExten: *56\nContext: special\nChannel: IAX2/pabx-gw/*55\n Priority: 1\nTimeout: 15000\nCallerid: Ding-dong <22*22*22*22>\n\n"); $tn->waitfor('/Event: Newchannel.*/') or die "Unable to determine call status", $tn->lastline; # wait for asterisk to process $tn->print("Action: Logoff\n\n"); print "Called house phones";
This works OK. The house phones get called for 15 seconds with a caller ID of "Ding-dong 22*22*22*22" and then stop. If anyone does pick up the phone (which should not be needed) it plays a message stating that someone has rung the doorbell. I haven't figured out how to get the perl script to wait long enough after this for the call to fully complete, this upsets Asterisk a bit but it's not a disaster - I'll mop this up later.