As I’ve been making more custom automation devices, I’ve come to the realisation that I don’t actually have to be restricted by the limitations of the Rako protocol. I could start again from scratch with my own custom protocol and have no restrictions at all.

The latest annoyance that’s triggered this line of thought is caused by the Rako protocol being one way. It’s possible to tell a device to go to a certain brightness level but it’s not possible to ask it what its current level is. Up until now this limitation has only manifested itself in the iPhone/Android app, where the level sliders are always at zero when the app is opened, which is a fairly minor annoyance. However, it’s now become a problem with my bathroom light touch switch.

The problem occurs because the touch switch and the dimmer are disconnected and only communicate wirelessly, and because the touch switch relies on knowing the current state of the light so that it can alternate between turning the light on or off for each press. This means that if a message goes missing due to interference or some other reason, then the switch and the dimmer are out of sync and it takes three presses of the switch before they’re back in sync again, all with no visible feedback to the user that anything is actually happening at all.

If the protocol was bi-directional then this wouldn’t be an issue as the switch would always be able to find out the actual state of the light, so… time for a new protocol.

Radio Transceiver

ha-zigbit-module

I started out by searching the Farnell catalogue for radio transceiver modules, with the criteria:

  • They should be self contained so that I don’t have to worry about RF board layouts
  • They should be small so that they could fit behind a light switch
  • They should be able to get reliable coverage thoughout a house
  • They should be cheap

In the end I decided to use Atmel Zigbit modules, which combine an ATmega1281 microcontroller, a 2.4GHz 802.15.4 radio transceiver and a chip antenna, and are intended to be used with the Zigbee protocol. These certainly meet the first two criteria, but they are a bit expensive at £20 in single unit quantities, though that’s offset a bit by not needing a separate microcontroller.

As they operate at 2.4GHz the signal isn’t going to get around the house anywhere near as well as the 433MHz devices used by Rako, in fact I’d guess they’ll be lucky to get through more than one wall. However, in reality they should be much better than the Rako radios as they’re meant to operate in a mesh network rather than point-to-point, so each individual radio link only needs to go a few meters before it finds another device in the mesh. So it should be possible to cover fairly huge areas, certainly much bigger than my house.

Transport Protocol / Software Stack

Contiki-C64

When I ordered the modules, I’d originally intended to use the Zigbee protocol and BitCloud, the free-to-use Zigbee implementation from Atmel. There’s even a sub-protocol within the Zigbee standard for home automation. However it turns out that the software stack, while free, is closed source and reportedly a bit buggy, and really needs a Windows machine for development, all of which made me think twice about whether I really wanted to tie myself into this software stack.

I had a hunt around for an open source Zigbee stack, but FreakZ, which is the only project I could find, seems to have been abandoned by its author after falling out with the Zigbee Alliance, who control the specification, and who also expect to be paid for any commercial use of the protocol, which is yet another reason to avoid Zigbee.

While I was looking for an open source Zigbee stack I came across Contiki. I’d seen Contiki in a story on Slashdot a few years ago, where it was running a web browser and IP stack on a Commodore-64. At the time I hadn’t paid it much attention and assumed that that was all it did, but it turns out its real purpose isn’t web browsing on a Commodore-64. It’s actually a tiny operating system designed to run on heavily resource constrained systems, like 8-bit microcontrollers, and is used a lot for wireless sensor networks.

Not only are a lot of those wireless sensor networks running over 802.15.4, but Contiki has already been ported to the Atmel Zigbit modules. It also has a fully compliant IPv6 and 6LoWPAN stack and can operate in a mesh network using the RPL routing protocol. Contiki itself is open source and all of these protocols are completely free and open.

With all of that in mind Contiki and IPv6 seemed like a much better choice than Zigbee and is what I’ve decided to use. It also has the added bonus, that after years of hearing people describe IPv6 as having a big enough address space to give every light bulb its own address, I can actually go ahead and give every light bulb and switch in my house an IP address!

Application Protocol

mqtt-inside

Unlike Zigbee and its predefined support for home automation, using IPv6 just provides UDP and TCP and I still needed an application protocol. Rather than build a completely custom protocol I decided to use MQTT as a base, which is a very simple and lightweight protocol that’s primarily intended for delivering telemetry data from sensors.

It works on a publish/subscribe system with a message broker. Sensors publish data to named locations (called “topics”) whenever and as often as they want. Other parts of the system can subscribe to those topics and will receive a message whenever new data is published.

A home automation system is essentially the reverse of a sensor network. Instead of having many sensors publishing data which is subscribed to by a small number of devices, there is instead a small number of publishers: the light switches and control application, and many subscribers: the lights, blinds, door locks, etc.

So for my home automation system I could have each light dimmer subscribe to a MQTT topic named “[IPv6_Address]/level”, where “[IPv6_Address]” is the individual dimmer’s address, then the control application on my phone (or a light switch) could publish a new value to the dimmer’s topic whenever it wanted the level to change.

Connecting to the Modules

ha-zigbit-breakout-boardha-loading-contiki

With a rough idea of how the system should work I could build a proof of concept prototype. The first job was to make a pair of breakout boards for two of the modules, so that I’d have some way to connect them to a breadboard, and to make them a bit more prototype friendly than they normally are with their 1mm pitch SMD footprint.

Once the modules were mounted on breakout boards I could attach an ISP programming header (pinout in the table below) and connect the module’s UART to my laptop via a BusPirate, ready to see any output it might generate.

NOTE: The UART pins on the Zigbit modules are labelled backwards, so rather than connecting RX to TX and TX to RX as would be normal, it should be wired RX to RX and TX to TX.

ISP Pin ISP Name Zigbit Pin Zigbit Name
1 MISO  39  USART0 TXD
2 VCC  24/25  VCC
3 SCK  1  SPI CLK
4 MOSI  38  USART0 RXD
5 RESET  8  RESET
6 GND  22/23  GND

Hello Contiki World

ha-contiki-hello-worldThe next step was to build the Contiki hello-world example, but before that a couple of changes had to be made to the zigbit platform files within Contiki.

Rather than using the official Contiki source tree I’m actually using the version from the 6lbr project, the reason for which will become clear later on. The 6lbr version can be retrieved from the 6lbr github.

However, the following changes also apply to the official Contiki source tree.

The first and most important change was to adjust the serial port baud rate from the default 115200 to 38400. Apparently the UART in the Zigbit modules can’t reliably do 115200. This was changed in platform/avr-zigbit/contiki-avr-zigbit-main.c:

-  rs232_init(RS232_PORT_1, USART_BAUD_115200,
+  rs232_init(RS232_PORT_1, USART_BAUD_38400,

The second change was in the same file and just turns on some debug messages to make it more obvious if things are working:

-#define ANNOUNCE_BOOT 0    //adds about 600 bytes to program size
-#define DEBUG 0
+#define ANNOUNCE_BOOT 1    //adds about 600 bytes to program size
+#define DEBUG 1

The third and final change was made in platform/avr-zigbit/Makefile.avr-zigbit, changing the definitions for the programming device from jtag2 to avrispmkii:

-AVRDUDE_PROGRAMMER=jtag2
-AVRDUDE_PORT=usb:00B000000D79
+AVRDUDE_PROGRAMMER=avrispmkii
+AVRDUDE_PORT=usb

Next the fuse bits needed to be changed in the Zigbit module so that it would run at 8MHz:

avrdude

Then the hello-world example could be built:

cd examples/hello-world
make TARGET=avr-zigbit

The EEPROM contents could be uploaded (which contain the MAC address):

make TARGET=avr-zigbit hello-world.eu

And the code could be uploaded:

make TARGET=avr-zigbit hello-world.u

NOTE: Appending “.u” to the target name gets the Makefile to upload the code, and appending “.eu” gets it to upload the EEPROM contents. Alternatively they can be manually extracted from the ELF file and uploaded using avrdude, like so:

avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma=.eeprom=0 -O ihex hello-world.avr-zigbit eeprom.hex
avr-objcopy -R .eeprom -R .fuse -R .signature -O ihex hello-world.avr-zigbit 6lbr-demo.avr-zigbit.hex
avrdude -v -P usb -c avrispmkii -p atmega1281 -U flash:w:hello-world.avr-zigbit.hex
avrdude -v -P usb -c avrispmkii -p atmega1281 -U eeprom:w:eeprom.hex

Hello 802.15.4 World

ha-contiki-udp-example

With the Contiki hello-world example working, it was time to see if the radios were working by running the UDP client and server from the udp-ipv6 example on a pair of modules.

This was almost as simple as just running make and then uploading the client to one module and the server to another, but doing so doesn’t actually work… they both get the same MAC address, and as a result, they can’t talk to each other.

When building for the avr-zigbit target, the MAC address is hard-coded in platform/avr-zigbit/contiki-avr-zigbit-main.c, and needs to be changed for one of the two:

-uint8_t mac_address[8] = {2, 0, 0, 0, 0, 0, 0xaa, 0xaa};
+uint8_t mac_address[8] = {2, 0, 0, 0, 0, 0, 0xaa, 0xbb};

Once the two modules had different MAC addresses they started talking to each other shortly after power-up, printing the messages they were exchanging to the UART.

Routing Between Ethernet & 802.15.4

ha-contiki-6lbr

I want the main control for the lights to be an app on my phone, so the next part I needed to get working was routing between the wireless network and my main Ethernet network.

It turns out that even though the zigbit modules are talking IPv6 over the wireless network, and my main Ethernet network is running IPv6, connecting the two isn’t as simple as it might seem. There are lots of little details that need handling, for example the main Ethernet network uses ICMPv6 based neighbour discovery, but the wireless network doesn’t, it uses a neighbour discovery mechanism optimised for the 802.15.4 network.

Luckily, it can all be handled by the 6lbr router, which is why I’m using their Contiki source tree and not the official one.

To get the routing working I installed Raspian onto a Raspberry Pi, built and installed 6lbr on the Raspberry Pi, and connected the UART from one of the Zigbit modules to the UART on the GPIO header on the Raspberry Pi (via a couple of 1K resistors, without which the zigbit module would fail to start). I then built examples/ipv6/slip-radio and uploaded it to the Zigbit module that was attached to the Raspberry Pi.

The activity of 6lbr can be followed in /var/log/6lbr/6lbr.log, after it starts it prints messages complaining about an inability to get a MAC address, powering up the attached Zigbit module at this point allows it to get a MAC address and it then begins routing between the two networks. Powering up the Zigbtt module before 6lbr starts leaves 6lbr unable to get a MAC address.

By default the 6lbr router uses the subnet bbbb::/64 on the Ethernet side and aaaa::/64 on the wireless side. The router itself has the address bbbb::100, pointing a web browser to http://[bbbb::100]/ shows a web interface that has various statistics and a graph showing the structure of the wireless network.

NOTE: Running the web browser on the Raspberry Pi doesn’t work, it must  be on another machine on the Ethernet network, the Raspberry Pi itself can’t access bbbb::100.

Once 6lbr was running I added a route to the wireless network to my main desktop machine:

ip -6 route add aaaa::/64 via bbbb::100

At which point I was able to ping the second Zigbit module (the one not attached to the Raspberry Pi) from my desktop:

ping6 aaaa::aabb

Timers & PWMs

One of the advantages of using the Zigbit modules is that they have a very capable microcontroller built in, which should be suitable for implementing pretty much any home automation device. One device I’m going to need a lot of is dimmers, which ideally need hardware PWMs (Pulse Width Modulators), so I want as many useable PWMs as possible.

After looking to see which of the ATmega1281′s pins are available on the Zigbit module’s pins (see the table at the bottom of the page), I could see that I could have six PWMs if I had access to timers 0, 1 and 3. However, reading the avr-zigbit platform support code in Contiki revealed that timers 0 and 3 were in use, which would mean I would only have two PWMs, which isn’t even enough for a single RGB device.

While I was checking which timers were in use, I noticed that there were some #defines which could move Contiki’s main clock to timer 2, provided timer 2 was driven by an external 32kHz crystal. As luck would have it, the Zigbit modules have this crystal, so moving the clock from timer 0 to timer 2 was as simple as adding one #define to platform/avr-zigbit/contiki-conf.h:

#define AVR_CONF_USE32KCRYSTAL 1

Freeing up timer 0 meant I now had four PWMs, but it would be nicer still if I could have all six. The ATmega1281 has five timers and timers 3, 4 and 5 are functionally identical, so it seemed logical that moving Contiki’s rtimer from timer 3 to timer 5 should just be a case of finding all of the accesses to the timer 3 registers and replacing them with timer 5 registers. Ignoring a few issues when I missed some of the places the registers are accessed, it was this simple, so I now have six hardware PWMs and the timer usage looks like this:

Timer Original Usage Modified Usage
Timer0 Contiki Clock Unused / 2xPWM
Timer1 Unused / 2xPWM Unused / 2xPWM
Timer2 Unused Contiki Clock
Timer3 Contiki rtimer Unused / 2xPWM
Timer4 Unused Unused
Timer5 Unused Contiki Rtimer

Six Channel Dimmer Prototype

ha-proof-of-conceptha-android-app
With everything else working, I just needed a few bits of software to get a working prototype of a six channel dimmer.

First I needed an MQTT message broker, so I temporarily installed Mosquitto on my desktop machine. I intend to eventually run this on the Raspberry Pi that’s routing between Ethernet and 802.15.4, but at the moment, even though it’s doing the routing, the Raspberry Pi is the one machine that can’t actually access the 802.15.4 network. So until I iron out the routing issues Mosquitto is running on my desktop.

Next I needed an Android app to control the dimmer, so with the help of the Eclipse Paho MQTT client library, I wrote a very quick and dirty app with six sliders that publish their positions to the Mosquitto message broker.

Lastly I needed to implement the dimmer itself, which needed an MQTT client for Contiki, which surprisingly, I wasn’t able to find. So, I spent a couple of days writing an MQTT client for Contiki, before finally adding the 100 or so lines of code necessary to implement a simple six channel dimmer, which is what the video at the top of the page demo’s.

Here’s what the dimmer code looks like:

PROCESS_THREAD(mqtt_client_process, ev, data)
{
  static uint8_t in_buffer[64];
  static uint8_t out_buffer[64];
  static uip_ip6addr_t server_address;
  static mqtt_connect_info_t connect_info =
  {
    .client_id = "contiki",
    .username = NULL,
    .password = NULL,
    .will_topic = NULL,
    .will_message = NULL,
    .keepalive = 60,
    .will_qos = 0,
    .will_retain = 0,
    .clean_session = 1,
  };

  static const char* topics[] =
  {
    "0", "1", "2", "3", "4", "5", NULL
  };

  PROCESS_BEGIN();

  uip_ip6addr(&server_address,
              0xbbbb, 0, 0, 0, 0, 0, 0, 0x110);

  TIFR0 = 0;
  TIMSK0 = 0;
  OCR0A = 64;
  OCR0B = 64;
  TCCR0A = 0xA3;
  TCCR0B = 1;

  OCR1A = 128;
  OCR1B = 128;
  OCR1C = 128;
  TCCR1A = 0xA1;
  TCCR1B = 0x09;

  OCR3A = 128;
  OCR3B = 128;
  TCCR3A = 0xA1;
  TCCR3B = 0x09;

  PORTB |= (1 << PORTB7);
  PORTG |= (1 << PORTG5);
  DDRB |= (1 << DDB7); // OC0A is output
  DDRG |= (1 << DDG5); // OC0B is output
  DDRB |= (1 << DDB5); // OC1A is output
  DDRB |= (1 << DDB6); // OC1B is output
  DDRE |= (1 << DDE3); // OC3A is output
  DDRE |= (1 << DDE4); // OC3B is output

  PROCESS_PAUSE();

  mqtt_init(in_buffer, sizeof(in_buffer),
            out_buffer, sizeof(out_buffer));
  mqtt_connect(&server_address, UIP_HTONS(1883),
               1, &connect_info);
  PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event);

  if(mqtt_connected())
  {
    static int i;
    for(i = 0; topics[i] != NULL; ++i)
    {
      mqtt_subscribe(topics[i]);
      PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event);
    }
  }
  else
    printf("mqtt service connect failed\n");

  while(1)
  {
    PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event);

    if(mqtt_event_is_publish(data))
    {
      const char* topic = mqtt_event_get_topic(data);
      const char* message = mqtt_event_get_data(data);
      int level = 0;

      while(*message != '\0')
        level = (level * 10) + *message++ - '0';

      printf("Setting level: %c = %u\n", topic[0], level);

      switch(topic[0])
      {
        case '0':
          OCR0A = level;
          break;
        case '1':
          OCR0B = level;
          break;
        case '2':
          OCR1A = level;
          break;
        case '3':
          OCR1B = level;
          break;
        case '4':
          OCR3A = level;
          break;
        case '5':
          OCR3B = level;
          break;
      }

      printf("publishing change...\n");
      sprintf((char*)message, "%u", level);
      mqtt_publish("newval", message, 0, 1);
    }
  }

  PROCESS_END();
}

ATmega1281 Pins

For reference, here’s how the pins on the ATmega1281 are used in the Zigbit modules:

Pins Reserved

Pin Function Usage
PA0 – 6 AD0 – AD6 Not Connected
PA7 AD7 Radio Reset
PB0 /SS Radio SPI Select
PB4 OC2A, PCINT4 Radio SLP
PC0 – 7 A8 Not Connected
PE5 OC3C, INT5 Radio IRQ
PG3 TOSC2 32kHz Oscillator
PG4 TOSC1 32kHz Oscillator

Pins Shared

Pin Function Zigbit Label
PB1 SCK SPI Clock
PB2 MOSI SPI MOSI
PB3 MISO SPI MISO

Pins Available

Pin Function Zigbit Label
PB5 OC1A, PCINT5 GPIO0
PB6 OC1B, PCINT6 GPIO1
PB7 OC0A, OC1C, PCINT7 GPIO2
PD0 SCL, INT0 I2C Clock
PD1 SDA, INT1 I2C Data
PD2 RXD1, INT2 UART TXD
PD3 TXD1, INT3 UART RXD
PD4 ICP1 UART RTS
PD5 XCK1 UART CTS
PD6 T1 GPIO6
PD7 T0 GPIO7
PE0 RXD0, PCINT8, PDI USART0 RXD
PE1 TXD0, PDO USART0 TXD
PE2 XCK0, AIN0 USART0 EXTCLK
PE3 OC3A, AIN1 GPIO8
PE4 OC3B, INT4 UART DTR
PE6 T3, INT6 IRQ6
PE7 ICP3, CLKO, INT7 IRQ7
PG0 /WR GPIO3
PG1 /RD GPIO4
PG2 ALE GPIO5
PG5 OC0B 1WR
PF0 ADC0 BAT
PF1 ADC1 ADC_INPUT_1
PF2 ADC2 ADC_INPUT_2
PF3 ADC3 ADC_INPUT_3
PF4 ADC4, TCK JTAG_TCK
PF5 ADC5, TMS JTAG_TMS
PF6 ADC6, TDO JTAG_TDO
PF7 ADC7, TDI JTAG_TDI

17 Thoughts on “Contiki / Zigbit Home Automation

  1. Dear,

    first I want to give congratulations for the post, this is exactly what I needed. I would like also to know if you can help me to compile the contiki for zigbit. Every time I tryed to compile the error appears in init_lowlevel function, and rime_udp_init (), you know what it is.

    Thank’s

  2. Yes, I changed the baud rate and the fuse bits are 0xe2 0x9d 0xff, but is not working, mabe I has one problem with the eeprom file, really I don’t know, I’m tryng….

  3. Hi, Sorry about that but I need ask one more thing, when I ping the ip bbbb::100 or even zigbit aaaa::aabb, the connection is intermittent, and I’m not able to connect another device, I tried to use your touch-design as example but the serial port shows the message “no dag yet …”, have you some idea why the border router is intermittent.?
    Thanks and have a nice new year.

  4. Jon California on January 8, 2015 at 6:47 am said:

    Thanks for this helpful post. I am about to try this out.
    A question for Henrique – Which ZigBit are you using?

    • Hi Jon.

      I’m using the atzb-24-a2

      • jon California on January 10, 2015 at 1:08 am said:

        Thanks Henrique.
        That part (atzb-24-a2) has become hard to buy – designated as “not recommended for new design” by Atmel. Contiki doesn’t yet have out-of-the-box support for newer Atmel ZigBits using ATmega256RFR2, Atxmega256A3U and AT86RF233. Please let me know if you come across Contiki packages/projects that’s been adapted for these devices.

        • Hi Jon,

          It is true the atzb-24-a2 is obsolete now, I can imagine why… If I have some notice about port to the new device I will post here, but now I thinking to use another platform like the Jennic IC from NXP.

Leave a Reply to esar Cancel reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Post Navigation