XBee to MQTT gateway
So far I've posted about hardware and theoretical stuff like network architecture or naming conventions. I think it's time to move to the software side.
The core of the sensor network I'm deploying at home is the Mosquitto broker that implements MQTT protocol. It manages the messaging queue, listening to messages posted by publishers and notifying the subscribers.
I've been working in parallel to have at least some pieces in place to get and store information from the pulse counter sensor. These are an XBee to MQTT gateway and a couple of consumers: one storing info into a MySQL database and another one pushing it to cosm.com.
I want to introduce you the first piece: the xbee2mqtt daemon. It's already available on github under GPL v3 license. It publishes the messages received by an XBee radio to the Mosquitto instance. The radio must have a Coordinator API firmware loaded. Right now the gateway understands frame IDs 0x90 and 0x92 which account for “Zigbee received packet” (i.e. data sent through the serial link of the transmitting radio) and “Zigbee IO data sample” (that's an automatic sample of selected analog and/or digitals pins on the transmitting radio).
I've tailored the daemon to my needs, but trying to be as generic as possible. The design is based on small components:
- The “xbee” component takes care of the connection to the radio and the packet parsing.
- The “router” maps xbee addresses/ports to MQTT topics.
- The “processor” pre-processes values before publishing them.
- The “mqtt” component takes care of the message publishing.
- And the XBee2MQTT class (which extends Sander Merechal's fabulous daemon class) glues everything together.
You can read the code to get a full insight of what it does but I'd like to explain here some decisions I've taken.
I've abstracted the message source to an address and a port. The address is the 8 bytes physical serial number of the radio (SH and SL) and the port is the pin (adc0, adc1,… dio1, dio2,…). 0x90 packets are mapped to virtual ports. The sender can define the name of the virtual port (like “battery:4460\n”) or otherwise a generic name will be used (“serial” by default).
The routing is a basic functionality. As I already explained in my previous post about topic naming conventions I think the mapping should be done in the gateway because no other component should have to know about the physical structure of the wireless (XBee) network. So the xbee2mqtt daemon maps all the messages to MQTT topics with semantic meaning. You can also allow default topic mapping which will publish any message received by an undefined address/port combination to a topic like
The processor uses a strategy pattern to pre-process any incoming value. I will be using this to do some calculations on the adc7 value the XBees report (that's the voltage monitor pin) to convert it to the real voltage the batteries are providing.
All the components have been designed so they can be injected to any code that depends on them. This is a common pattern (dependency injection) that favours decoupling and provides a clean way to define strategies at runtime, for instance when mocking components in the unit tests.
As always, comments are more than welcome!