Ethernet Doorbell

Yep, today the old Doorbell has been removed and my new Ethernet Doorbell is in use! As you can see the white LED was on when I took the picture on the left, which means that it was dark outside at that time. By polling my Domotica system, the doorbell ‘knows’ whether it’s dark outside or not. Of course it’s not the doorbell itself, but the hardware behind it that does the ‘knowing’: a JeeNode with Ethercard.

All I had to do today was some soldering, finishing the doorbell sketch and pulling the UTP cable through the hole in the wooden door frame. Those last 2 items made it all take a total of 9 hours before I could actually say that the job was completely finished…

Using XMLRPC to communicate to my Domotica system was a bit too much for the 2 kilobytes of RAM on the ATMega. Strings like

<?xml version="1.0"?><methodCall><methodName>GetDevice</methodName><params>

just to get the current value of a dusk/dawn sensor made the amount of free RAM disappear very quickly, so I decided to use another approach I already used some years ago. It hasn’t been used for some time, but it was still available and ready to use. Now all I have to send to my Domotica system is a


and a Indy IdCmdTCPServer takes care of supplying the response. This saves a lot of RAM! Free RAM went up to 900 bytes again, where the XMLRPC method made RAM drop below 200 bytes. And an additional bonus: no need for XML parsing of the response 😉


The picture below shows the hardware that’s making it all possible: a JeeNode, an EtherCard and a modified Utility Plug. The cable at the bottom goes to the doorbell, the yellow cable on the right goes to an Ethernet switch and the top right cable is the power supply.


And below the sketch that’s currently running on the JeeNode:


// Doorbell
#include <EtherCard.h>
#include <Ports.h>
#include <RF12.h> // needed to avoid a linker error :(
// ethernet interface mac address, must be unique on the LAN
//char website[] PROGMEM = "virtualxp.hekkers.lan";
char website[] PROGMEM = "domoticaserver.hekkers.lan";
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
static byte myip[] = { 192,168,10,75 };
static byte gwip[] = {192,168,10,60 };
static byte dnsip[] = {192,168,10,10 };
static byte hisip[] = {192,168,10,40 };
Stash stash;
MilliTimer DuskDawn;
MilliTimer HeartBeat;
byte Ethernet::buffer[600];
byte HP3=0;
byte VP3=0;
byte night = 0;
byte vnight = 0;
#define DDINTERVAL 60000  // 10 minutes = 600 seconds
#define HBINTERVAL 9000   // 1.5 minutes = 90 seconds
// LED & button related stuff
Port BlueLed (1);
Port Button (3);
Port WhiteLed (4);
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
void(* resetFunc) (void) = 0; //declare reset function @ address 0
void setup () {
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println("Failed to access Ethernet controller");
if (!ether.staticSetup(myip, gwip, dnsip))
Serial.print("IP address setup failed");
ether.printIp("IP:  ", ether.myip);
ether.printIp("GW:  ", ether.gwip);
ether.printIp("DNS: ", ether.dnsip);
if (!ether.dnsLookup(website))
Serial.print("DNS failed");
ether.printIp("SRV: ", ether.hisip);
ether.hisport = 8000;
// port 3 = doorbell button
Button.digiWrite(HIGH);   // pull-up DIO
// BlueLed = push LED
// WhiteLed = night LED
static void send_callback (byte status, word off, word len) {
Ethernet::buffer[off+600] = 0;
Serial.print((const char*) Ethernet::buffer + off);
static void poll_callback (byte status, word off, word len) {
const char* res = (const char*) Ethernet::buffer + off;
Ethernet::buffer[off+600] = 0;
if((res[0]=='R')&&((res[1]=='='))) {
night = (res[2]=='1')?0:1;
Serial.println(night, DEC);
if(res[0]=='E') {
if (night!=vnight)
vnight = night;
void PollDuskDawn(){
byte sd = stash.create();
// generate the header with payload - note that the stash size is used,
// and that a "stash descriptor" is passed in as argument using "$H"
Stash::prepare(PSTR("GET $H"), sd);
// send the packet - this also releases all stash buffers once done
void SendStatus() {
byte sd = stash.create();
stash.println(HP3, DEC);;
// generate the header with payload - note that the stash size is used,
// and that a "stash descriptor" is passed in as argument using "$H"
Stash::prepare(PSTR("PUT $H"), sd);
// send the packet - this also releases all stash buffers once done
void loop(){
if (ether.clientWaitingGw()) return;
if (DuskDawn.poll(DDINTERVAL)) {
if (HeartBeat.poll(HBINTERVAL)) {
HP3 = !Button.digiRead();
if (HP3 != VP3)
if (HP3) {
Serial.println("Button Pressed!");
Serial.print("Button Releasedrn");

That’s it! On to the next adventure 😉


Tagged , . Bookmark the permalink.

4 Responses to Ethernet Doorbell

  1. Gijs van Dulmen says:

    Nice job Robert! Looks pretty nice everything stashed into that box. When can I visit again to press that button? 😉

    Greetings from Arnhem

    • Hi Gijs,

      You know you’re welcome anytime you like! 😉
      Oh, but please only push the button for at least 0.7 but not more than 0.8 seconds, or otherwise you’ll trigger a bug! LOL

  2. Roelof says:

    Why did you choose Ethernet for the interface and not an XBee? The amount of data is quite low, and it saves you the wire, and in your newest post I see you using XBee too. Or just because you had an ethernbet borad and wanted to use it? Or is ther another reason you want to share?

    BTW it inspired me to think about such a doorbel. I live in an appartment, so there are actualy two doorbels (one conventional one at the door, and one combined with some sort of intercom).
    I an curious how many times there is someone at the door when I am not at home 🙂 so want to log it.

    • Hi Roelof,
      I chose the EtherCard just because I wanted to try it, see how it works and how stable it is – and because I like to explore new things.
      And you’re right, it’s handy to be able to see if there’s been someone at the door. In my case, the doorbell also triggers the camera to take a snapshot so I can also see who it was 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *