The most complicated doorbell in the world?

My wife has been complaining about not being able to hear the doorbell when she is in the back bedroom or the garden. So, I decided to rig some extra chimes for her...

We live in a Victorian two story house. It has to be said that it is a bit of a nightmare to get wiring through, the ground floor floors are solid, so are the exterior walls and many of the internal ones, and there are quite a lot of them. So I needed a wireless option. We also needed the ability to have multiple bells. We could have opted for a commercial wireless bell system, but Margie said the one I selected was too expensive, and anyway, where is the fun in that?

Detecting the bell push

As I technologist, I have a fundamental distrust of technology, so I decided that I was not going to change the existing doorbell in any way. A quick check with the meter confirmed my suspicions, it was running off 14.4 volts of AC, probably with nasty spikes going up and down it when the current went off. So I needed something to monitor the current through the existing doorbell coils. I used a bridge rectifier fed into a 4N35 optoisolator through a 1K resistor. This way, I could have thousand volt spikes on the doorbell side and not blow up the Arduino.



Bellsensor
Doorbell current sensor


This circuit monitors the current through the existing doorbell and pulls an Arduino digital I/O pin low when there is current. Now to connect my current sensor to the rest of the house.

Wireless link

I'm really excited about the new ESP8266 Wifi SoC chip, which I'll write about later, and I'll almost certainly use this for most future IoT type projects. However, at the moment I've only got a couple of ESP's while I wait for a larger batch from China, and I also have a collection of wireless modules that I have collected over the years that need to be used up.

I decided to create a wireless gateway box to channel the input from a variety of non-Wifi technologies onto the internal house net. A very old Arduino Decilmilia with an Ethernet shield was pressed into service, and a quick shield knocked up with a A434 OOK receiver on board. On the doorbell end, an A434 transmitter with exactly 164mm of antenna would send the bell push events over to the gateway.



Doorbellphysical
Doorbell Physical Arch


Great. Software next.

Messaging

I've decided that I'm going to standardise on MQTT for most of my future IoT projects. Its simple, its quick, and I can implement it easily without recourse to third parties. Software as service is nice, but I'm not sure I really want one of the many IoT platform companies knowing when I'm going to the toilet, or to rely on them staying in business long enough for me to flush.

For those who don't know MQTT provides pub/sub messaging for small platforms, and indeed, the ESP8266 can directly talk to it, which is a huge plus. The MQTT broker (the server bit), can also be hosted on something as small as a home router, which is exactly what I've done for my portable demo rig.

The house broker is on one of the virtual machines on my main home server. I use the Mosquitto broker for the moment, and if you are thinking of hacking me, yes, even in the internal network I've set up security on that.



Doorbelllogical
Doorbell Logical Arch


The doorbell to gateway software

I use the RadioHead Arduino library to provide me with a simple interface to the A434's. The transmit end just sits in a loop waiting for pin9 to go low, and then sends the string 'doorbell:doorbell' over the wireless, followed by a delay to debounce the bell push...

    #include <RH_ASK.h>
    #include <SPI.h> // Not actually used but needed to compile

RH_ASK driver;

void setup()
{
    Serial.begin(9600);   // Debugging only
    pinMode(9, INPUT);
    if (!driver.init())
         Serial.println("init failed");

}

void loop()
{
    const char *msg = "doorbell:doorbell";

    if(digitalRead(9) == LOW) {
      driver.send((uint8_t *)msg, strlen(msg));
      driver.waitPacketSent();
          // Wait for doorbell release
      while(digitalRead(9) == LOW){
           delay(100);
      }

    }
}

The gateway end is equally simple, I wait for a message from RadioHead, split it on the colon, and then use the first string as the MQTT topic and the second as the message. This allows for future expansions with other nodes.


#include 
#include 
#include 
#include 

RH_ASK driver(2000,6,7,9,false);


byte mac[]    = {  
  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
byte server[] = { 
  192, 168, 10, 22 };
byte ip[]     = { 
  192, 168, 10, 43 };

void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
}

EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
uint8_t buflen = sizeof(buf);

void setup()
{
  Ethernet.begin(mac, ip);
  if (client.connect("WirelessGateway", "xxxx", "xxxxx")) {

    client.subscribe("inTopic");
  }

  if (!driver.init())
    Serial.println("init failed");
  Serial.println("Initialised");
}

void loop()
{

  if (driver.recv(buf, &buflen)) // Non-blocking
  {
    int i;
    char * topic;
    char * message;

    // Message with a good checksum received, dump it.
    driver.printBuffer("Got:", buf, buflen);
    buf[buflen] = 0;
    topic = strtok((char*)buf, ":");
    message = strtok((char*)buf, ":");
    client.publish(topic,message);
  }

  client.loop();
}

Cooking now.

Make a noise

Sunday was drawing on by now, and I wanted a quick way to play an alert noise. Later, I'll probably use an mp3 module on a ESP or an Arduino, but it was getting late, so I decided to put some thing onto my Big Mac, which runs continuously, as it hosts a couple of virtual machines, one of which has this site on it. Ruby is my current weapon of choice for scripting, although I really mean to get to grips with Python for the better GUI libraries and for MicroPython. Anyway, a few minutes and a trip to the free mp3 site later, I had this...

 require 'rubygems'
 require 'mqtt'

# Subscribe example
MQTT::Client.connect('mqtt://xxxx:xxxxx@192.168.10.22') do |c|
  c.get('doorbell') do |topic,message|
    `afplay doorbell.wav`
    puts "#{topic}: #{message}"
  end
end

Process.daemon(true)

That last line serves to turn the script into a daemon. As I didn't want to start the script every time I rebooted, I decided to make a launchd plist to tell OSX to run it at boot time. This was probably the hardest part of the the whole exercise, but after a couple of tries I generated this. (Thanks to: http://notes.jerzygangi.com/creating-a-ruby-launchd-task-with-rvm-in-os-x/)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
   "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.modernindustry.automation</string>
 <key>ProgramArguments</key>
 <array>
 <string>/usr/local/bin/automation.sh</string>
 </array>
 <key>KeepAlive</key>
 <dict>
 <key>SuccessfulExit</key>
 <false/>
 </dict>
 <key>RunAtLoad</key>
 <true/>
 <key>StartInterval</key>
 <integer>21600</integer>
</dict>
</plist>

which was installed in ~/Library/LaunchAgents/com.modernindustry.automation.plist

loaded with:

launchctl load ~/Library/LaunchAgents/com.modernindustry.automation.plist

and voila.... doorbell noises all over the house...