<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="sodnpoo.xsl" type="text/xsl"?>
<xml page="/?t=doorbell"><a href="http://sodnpoo.com/?x=html">HTML version</a><post>
  <tag value="doorbell"/>
  <tag value="reverse engineering"/>
  <title>1 by one doorbell (sxd043) to gpio mod</title>
  <date>16 Aug 2015</date>
  <p>
  </p>
  <image src="/posts.assets/doorbell_433_1.jpg"/>
  <p>
We've recently moved house and needed a new doorbell - as I <a href="http://www.sodnpoo.com/posts.xml/simple_ac_doorbell_to_gpio_interface.xml">mentioned previously</a>, I'm often working with headphones on and can easily miss a delivery, so I need to be able to trigger a real-time alert to my phone. We chose one of the many, many "1 by one" wireless, 433MHz kits (the one with the big red button).
  </p>
  <p>
The simpliest solution seemed to be to use an <a href="http://goughlui.com/2013/12/20/rtl-sdr-433-92mhz-askook-decoding-of-various-devices-with-rtl_433/">RTL SDR dongle to sniff the 433MHz band</a>, and then trigger the existing <a href="/posts.xml/doorbell_to_real_time_android_alert.xml">UDP 'latch' that the code on my phone is polling for</a>. However - for whatever reason - my SDR dongle doesn't seem to see any 433MHz traffic. (It has spent the last year living in the shed so maybe it's not so good anymore...)
  </p>
  <p>
A second option was to use a cheap 433MHz receiver board I have, and an arduino to do the sniffing and triggering. This sounded feasible but also sounded like mostly software dev; I write a lot of code in the day job and decided that a hardware hack would be more interesting.
  </p>
  <image src="/posts.assets/doorbell_433_2.jpg"/>
  <p>
The PCB in the receiver is made up of four ICs:
<ul>
  <li>PT4303 (red) - 433MHz receiver/decoder</li>
  <li>NT200M (blue) - controller</li>
  <li>SXD043 (green) - audio</li>
  <li>LM4890 (yellow) - amplifier</li>
</ul>
Of these I can only find a datasheet for the TI LM4890 and a pin out diagram for the PT4303. The NT200M and the SXD043 appear to be undocumented. The board also has three buttons on the other side: learn, volume and a chime selector.
  </p>
  <p>
Probing the board with the scope revealed the overall design and exposed some of the power saving features (this is a battery powered receiver):
<ol>
  <li>The NT200M pulls the PT4303's CE line high briefly every second (minimising battery use by not running the receiver constantly)</li>
  <li>If the data sampled from the PT4303 has the learnt button code</li>
  <li>Trigger the SXD043 to start playing</li>
  <li>The SXD043 raises the LM4890's 'shutdown' line, enabling the amplifier for the duration of the audio playback (again minimising battery use by shutting down the amplifier when not playing anything)</li>
</ol>
The learn button is connected to the NT200M and when pressed, the NT200M enables the PT4303 for several seconds continuously while sampling for a new code. The volume and chime buttons are connect to the SXD043.
  </p>
  <p>
Based on this, two options are available: the line from the NT200M to the SXD043, essentially triggering on detection of the learnt code; or the line from the SXD043 to the LM4890's shutdown pin, triggering on any playback.
  </p>
  <p>
I decided to use to use the 'playback' line on the SXD043, just because it's easier to test - both the volume and chime buttons trigger playback (or I can ground the trigger line), whereas I'd need to press the doorbell - already attached to the front door frame - to test from the NT200M.
  </p>
  <image src="/posts.assets/doorbell_433_3.jpg"/>
  <p>
<ul>
  <li>Red - Ground</li>
  <li>Blue - Trigger</li>
  <li>Green - VCC</li>
  <li>Yellow - audio out</li>
  <li>Orange - playing</li>
</ul>
  </p>
  <p>
The line is fed through an NPN transistor to invert the signal, suitable to patch into the same old DG834v4's GPIO button <a href="http://www.sodnpoo.com/posts.xml/doorbell_to_real_time_android_alert.xml">I was using before</a>.
  </p>
  <image src="/posts.assets/doorbell_433_5.jpg"/>
  <p>
Image of the transmitter board:
  </p>
  <image src="/posts.assets/doorbell_433_4.jpg"/>
  <p>
  </p>
</post><post>
  <tag value="doorbell"/>
  <title>doorbell to real time android alert</title>
  <date>7 Jul 2014</date>
  <p>
With the <a href="/posts.xml/simple_ac_doorbell_to_gpio_interface.xml">doorbell physical hardware interface</a> working reliably, I just needed to connect up some software to send an alert to my phone. Initially I thought I'd avoid having to write any notification code by leaning on something in the cloud; I found <a href="http://www.realtime-security.de/">Server Push Notifications</a> that has a free/lite android app and has simple URL based API - this is important as although we have a full OS on the DG834v4, it has only 16MB RAM; with only about 800K free. The hotplugd system on openwrt can run arbitrary scripts on button presses and was configured to call wget with the appropriate URL to generate the notification. This worked well in testing but in real use, the latency of the notification going out to the internet, being processed somewhere and then coming back again meant I still sometimes missed the door.
  </p>
  <p>
To reduce the latency I decided all network traffic should be local-only. A few months ago, at work I wrote some UDP broadcast P2P-discovery code; broadcast traffic is link-local and UDP is very lightweight - this seemed ideal: broadcast packets could be generated on the DG384v4 using socat and some sl4a+python code running on my phone could simply listen for them. Again early testing indicated success, but as soon as the phone's screen switched off and it went into sleep, the network stack just stopped passing the broadcast traffic to the python code. It seems obvious now that this would happen, keeping the networking running on the off chance of receiving a broadcast is pretty wasteful from a power/battery point-of-view.
  </p>
  <p>
Making the phone poll is a much better idea; the phone can wake up, check the doorbell and go back to sleep again. Polling once every few seconds shouldn't have a measurable impact on battery life, the phone will likely be waking up at least that often to service one of the many long running processes it already has to deal with.
  </p>
  <p>
If the phone is now polling, rather than listening for the doorbell notification, the state will need to be remembered for a short time to ensure that the phone has seen it. Implemented in a simple UDP server, is a latch-like mechanism where normally, when polled it'll return a false response unless it has been triggered, where it'll return a true for a short time instead. 
  </p>
  <p>
I verified that the phone continued to poll while asleep and although it does slow the rate down to around once per minute when doing nothing, most importantly it polls at the correct rate when playing music - which is the time I need it most :)
  </p>
  <p>
The UDP server has been coded in C to keep the memory footprint small, so it can be run directly on the memory-squeezed DG834v4. The original proof-of-concept was written in python, running on my desktop machine; it's memory usage weighed in at around 6MB, the C version is around 400K. Neither code base has been optimised at all however. 
  </p>
  <p>
To compile, you'll need to download and build the openwrt toolchain. First, clone the git repo:  
  </p>
  <pre>
git clone git://git.openwrt.org/openwrt.git
  </pre>
  <p>
Then use the menu system to select your architecture:
  </p>
  <pre>
make menuconfig
  </pre>
  <p>
And to just build the toolchain:
  </p>
  <pre>
make prepare
  </pre>
  <p>
Next we need to set the 'STAGING_DIR' environment variable - the actual directory will vary depending on the architecture, gcc version etc:
  </p>
  <pre>
STAGING_DIR=/full/path/to/openwrt/staging_dir/toolchain-mips_mips32_gcc-4.8-linaro_uClibc-0.9.33.2/
export STAGING_DIR
  </pre>
  <p>
And finally to actually do the compile (again - will vary on arch/versions):
  </p>
  <pre>
/full/path/to/openwrt/staging_dir/toolchain-mips_mips32_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-gcc udp-doorbell-server.c -o udp-doorbell-server
  </pre>
  <p>
Here's the C source (udp-doorbell-server.c):  
  </p>
  <pre>
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;stdio.h&gt;
#include &lt;signal.h&gt;

time_t last = 0;

void sig_handler(int signo){
  if (signo == SIGUSR1) {
    printf("received SIGUSR1\n");
    last = time(NULL);
  }
}

int main(int argc, char**argv){
  if (signal(SIGUSR1, sig_handler) == SIG_ERR){
    printf("\ncan't catch SIGUSR1\n");
  }

  int sockfd,n;
  struct sockaddr_in servaddr,cliaddr;
  socklen_t len;
  char mesg[1000];

  sockfd=socket(AF_INET,SOCK_DGRAM,0);

  bzero(&amp;servaddr,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
  servaddr.sin_port=htons(32000);
  bind(sockfd,(struct sockaddr *)&amp;servaddr,sizeof(servaddr));

  for (;;){
    len = sizeof(cliaddr);

    n = recvfrom(sockfd, mesg, 1, 0, (struct sockaddr *)&amp;cliaddr,&amp;len);
    
    char result = '0';
    time_t now = time(NULL);
    if((int)now-30 &lt; (int)last){
      result = '1';
    }
    
    printf("now: %d,\tlast: %d\n", (int)now, (int)last);
    printf("result: %c\n", result);

    sendto(sockfd, &amp;result, 1, 0, (struct sockaddr *)&amp;cliaddr,sizeof(cliaddr));
  }
}
  </pre>
  <p>
It listens on port 32000 and is triggered by a SIGUSR1 signal - it'll remain in the triggered state for 30 seconds; in the hotplugd button config is this:  
  </p>
  <pre>
killall -USR1 udp-doorbell-server
  </pre>
  <p>
Last but not least is the SL4A/Python code that runs on the phone:
  </p>
  <pre>
import android
import time
import socket


default_soundfile = '/system/media/audio/notifications/S_Good_News.ogg'
doorbell_net = '"WIRELESS"'
doorbell_server = ('192.168.1.250', 32000)

device = None # running locally or via adb

def alert(msg, title='ALERT!', soundfile=default_soundfile, silent=False):
  droid.vibrate(2000)
  droid.makeToast('%s %s' % (title,msg))
  droid.notify(title, msg)
  
  if silent is False:
    droid.mediaPlay(soundfile, soundfile)
    while droid.mediaIsPlaying(soundfile) is True:
      time.sleep(0.1)
  
droid = android.Android(device)
alert("starting", title="startup", silent=True)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(1)
c = doorbell_server
while True:

  try:
  
    network = droid.wifiGetConnectionInfo()[1]['ssid']
    if network == doorbell_net:
      
      s.connect(c)
      s.sendto('0', c)
      data = s.recv(1)
      print "d:", data
      
      d = int(data)
      if d == 1:
        alert("Door bell")

  except socket.error as e:
    print "e: %s" % e
  
  time.sleep(5)
  </pre>
  <p>
It'll only try and poll when the phone is connected to the 'WIRELESS' network; if it is, and it detects the trigger it'll make the phone run a of bunch alerts (vibrate/toast/notify/play media) - you'll likely need to change default_soundfile, doorbell_net and doorbell_server to match your environment.
  </p>
  <p>
Hopefully I won't miss any more parcels now :)  
  </p>
</post><post>
  <tag value="doorbell"/>
  <title>simple ac doorbell to gpio interface</title>
  <date>6 Jul 2014</date>  
  <p>
  </p>
  <image src="/posts.assets/doorbell1.jpg"/>
  <p>
I keep missing packages being delivered - I've usually got headphones on when I'm coding. I listen to music from my phone, so if I can generate a real-time alert when someone is at the door, that should solve the problem.
  </p>
  <p>
The door bell is just an electromagnet and an 10 volt, AC power supply; when the button is pressed it completes the circuit and the electromagnet pushes a small bar of metal to strike the chimes. I didn't want to modify the doorbell or make it dependent on other electronics so I've patched a simple circuit on to the electromagnet; an integrated rectifier and PNP transistor. The transistor pushes a line to ground when the AC voltage (the two white crocodile clips; tapped from the electromagnet) is applied to rectifier. (This is probably less than optimal, but it works from parts I had to hand.)
  </p>
  <p>
The line is patched onto the reset button of an old Netgear DG834v4 running OpenWRT, where hotplugd sees it as a GPIO button and can easily be configured to run any script when the 'button' is clicked.
  </p>
  <image src="/posts.assets/doorbell2.jpg"/>
  <p>
  </p>
</post></xml>
