Let me start with asking you a question. How many of you uses facebook messenger in your smart phone? Most of you will. But how many of you know that you are getting your notifications through MQTT protocol? Probably not many. I came to know it just a few days ago. Why FB is using MQTT in their messenger? Now a days, if you go to any IoT related sites, you willl be seeing this term MQTT quite often. It seems like MQTT is the HTTP of IoT age. But then I asked a question myself -- why should I choose MQTT over some well known, widely used, universally supported protocol like HTTP?
( from http://litmusautomation.com/2014/05/rest-good-mqtt-better/ )
What is MQTT?
"MQ Telemetry Transport (MQTT) is a publish/subscribe messaging protocol particularly well-suited for working with limited computational power and lean network connectivity. IBM and systems provider Eurotech first developed MQTT, and then contributed the protocol to OASIS( the Organization for the Advancement of Structured Information Standards ). The "MQ Integrator SCADA Device Protocol" is an old name for what is now known as MQTT. MQTT is designed to be open, simple and easy to implement, allowing thousands of lightweight clients to be supported by a single server. These characteristics make it ideal for use in constrained environments or low-bandwidth networks with limited processing capabilities, small memory capacities and high latency. The MQTT design minimizes network bandwidth requirements while attempting to ensure reliability of delivery. The protocol is already used in a wide variety of embedded systems. Hospitals use the protocol to communicate with pacemakers and other medical devices. Oil and gas companies use MQTT to monitor thousands of miles of oil pipelines."[more here]
"MQTT is extremely efficient publish/subscribe reliable messaging protocol. A protocol that enabled devices to open a connection, keep it open using very little power and receive events or commands with as little as 2 bytes of overhead. It offers reliable behavior in an unreliable or intermittently connected wireless environments. "Last will & testament” lets all other subscribers know immediately if a client disconnects abruptly. "Retained message” enables any new node connecting to a broker to get messages on topic that are retained by the broker."[more here]
Complete specification of MQTT is available at http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/MQTT_V3.1_Protocol_Specific.pdf
How it works?
To use MQTT, first thing you need is a broker. A broker is a server that routes published messages to subscribers. You can either setup one yourself with MQTT broker softwares like Mosquitto which is closely related to Eclipse Paho project or use those available on internet for free use such as iot.eclispe.org port 1883, CloudMQTT etc. You can also choose the option of a more secure connection SSL/TLS connection to broker and this is available with test.mosquitto.org.
Now you have fixed your broker, you can tell your client to connect to that broker and publish/subscribe messages. Each message is associated with a topic. A topic is a place holder to store your data and forward it to clients whom want to listen to your client. Lets pretend you have a MQTT sensor node in your garden and it will update measured field temperature and soil measure to a remote server. What you can do is publish the data "temp=21C,hum=65%" to a topic like "user/vish/area/garden/zone1/data". As you can see topic name resembles closely to some kind of REST request. Also remember - MQTT is data agnostic - so you can send a JSON object like {"temp":"21C","hum":"65%"} also, which may make it easy for a client to parse. And if you want to send it in XML, you can do that also. If some client wants this data, they can just subscribe to that topic from the broker and broker takes care of forwarding it to them. If more than one client wants this information, they all can subscribe to the same topic and broker will take care of those information deliveries. Your client can just publish it once and go. Once your client is connecting to a broker, it can also set 'Last Will and Testament' which will be the message published to that topic's subscribers in case the client leaves the 'chat' ungracefully. You can also set QoS which is like how reliable you want to make the connection, in case of intermittent connection and packet losses.
QoS( Quality of Service ) : QoS describes how reliable you want your data delivery to be which in a way implies how hard the broker/client will try to ensure that a message is received. MQTT offers three levels of QoS settings:
1. QoS0 : Atmost once delivery - The broker/client will deliver the message once, with no confirmation.
2. QoS1 : Atleast once delivery - The broker/client will deliver the message at least once, with confirmation required.
3. QoS2 : Exactly once delivery - The broker/client will deliver the message exactly once by using a four step handshake.
Last Will and Testament : While connecting to a broker, a client can tell that it has a will statement which is a message that it wishes the broker to send if it disconnects unexpectedly. The will message has a topic, QoS and retain status just the same as any other message.
Retained Topics : A retained message is message the broker will keep even after sending to all current subscribers of that topic. If a new client wants to subscribe to that topic in future, then this stored message is forwarded to that client by the broker. So if you have infrequently updating topic( such us your garden temperature which is set to update once in an hour ), a new client which want to subscribe that topic can get "last known good" message in that topic.
Why should I care?
IoT is the place of billions of devices and most of them are power constrained. When they communicate, the network load is definitely going to shoot up. We have been using HTTP for these device to device communications for a while. But let's check what happens when I want to update some status to a remote server using HTTP. I fired my terminal and send a simple message to some server and this is the result :
Can you see that?? To send just 21 bytes of payload, I ended up sending 201 bytes of header data. Let's calculate efficiency( I'm not sure whether I can call this efficiency, but for now please bare with me ).
21 / (21+201) = 9.5%
That clearly means we are in trouble if we want to sens some sensor readings to a remote server via HTTP . Let's check what happens when you send same data through MQTT to a broker. MQTT has thing thing called "Fixed Header" of 2 bytes. And if your payload is less than 128 bytes, then this will be the only additional information in the packet you send. And for every additional 127 bytes of payload, you need to add 1 byte of variable header to the packet, uptill a payload of 256MB. So now the efficiency is
21 + (21+2) = 91.3%
That's almost 10 times increase. . Let's do one more experiment. This time, send 127 bytes and calculate efficiency :
HTTP : 127 / (127+201) = 38.6%
MQTT : 127 / (127+2) = 98.4%
It's clear, MQTT rocks!! This because HTTP is a (basically)document centric protocol while MQTT is agnostic of data content and transfers simple byte arrays, making drip-feeds of updating information trivial. Also if you want to send data to multiple clients with HTTP, you will end up sending it to every subscriber individually. With MQTT, send it once to broker and broker takes care of rest. Again thumps up MQTT!
Now what about those power constrained devices who can't afford to spend too much of their battery on sending data? Luckily Stephen Nicholas has done an excellent job on power profiling MQTT on android and I'm citing an overview of his results :
> 93x faster throughput
> 11.89x less battery to send
> 170.9x less battery to receive
> 1/2 as much power to keep connection open
> 8x less network overhead
A detailed report can be found at
1. http://stephendnicholas.com/archives/219
2. http://stephendnicholas.com/archives/1217
Let's get started with MQTT
With that, I hope you are convinced that MQTT is the language of IoT. Then let's get started with MQTT. What I'm going to do is to first walk through a few tools I think will be very useful to fiddle with MQTT and then I will go ahead setup my local MQTT broker, add a few launchpads/arduinos as clients to it and finally add openHAB to send and receive MQTT packets and control my home network.
Tools
Before getting started, let's go through a few tools which will help you to "sniffer" to your network's mqtt communication.
1. MQTT Lens( Chrome App ) : I'm a fan of this, for it's simplicity.I will be using this in my demo. This is quite intuitive to use and opensource too. You can add it to your chrome from chrome store.
2. MyMQTT( Android App ) : This is the only android app I find descent in Play store. Quite handy, but not open source .
3. MQTT Inspector( iOS ) : If you are ready to pay to get a client on your iDevice.
4. mqtt-spy( Linux/Mac/Win ) : An open source GUI desktop client for Linux/Mac/Windows.
Setting Up a MQTT network
Step 1 : Setup local broker( skip this step if are using a public broker )
I chose to setup a local broker because I don't want all the ON/OFF, OPEN/CLOSE this/that information to be sent through internet. I could have setup my local broker in my raspberry pi, but I chose to do it on my beaglebone black running latest version of debian build. After checking through Eclipse Paho project, I decided to install Mosquitto as my broker. You can install it by typing ( works with rPi raspbian and BBB debian )
sudo apt-get install mosquitto mosquitto-clients python-mosquitto
Once installed, mosquitto will automatically start at port 1883 and will add itself to startup so that you don't have to add it everytime you turn on your rPi/BBB. If you want to stop mosquitto, just execute :
sudo pkill mosquitto
Mosquitto( I think all MQTT brokers ) by default runs at port 1883. If for some reason want to change this, you can start mosquitto with this command :
mosquitto -p 1729
where 1729 is the port number I want mosquitto to listen to.
Also if you want to have more tighter control over your mosquitto broker, you can make your own configuration file and specify it while starting mosquitto like :
mosquitto -c myconfig.conf -p 1729
Using configuration files, you can set additional security options like TLS/SSL encryption, username/password protection etc. A detailed explanation on how to do this is available at mosquitto.conf
Testing broker : To test the broker, I chose to use MQTT Lens from my PC and MyMQTT from my smartphone. I made both my clients to subscribe to topic "localnet/device/intro". Then from my smartphone, using MyMQTT, I send a plain text message "Hello MQTT, this is MyMQTT" to the same topic which I expected to be received by both MQTT Lens and MyMQTT.
I also send a JSON object from my smart phone on same topic, which also MQTT Lens displayed nicely. Test pass!!
Step 2 : Setup your arduino/Lauchpad client
If you are using arduino, this part is quite easy. PubSubClient is a library enables arduino to talk MQTT. It works with arduino Ethernet board and with most of the ethernet sheilds. Lots of people are using it and it works quite well for them, but I failed miserably when I tried to use it with Adruino Galileo.
If you are using TI Launchpad MSP430F5529, then you can follow this blog where Adrian uses a modified version of PubSubClient library to run with energia IDE.
But what I have is a EK-TM4C1294XL Launchpad and his modified library gracefully crashed on my launchpad. So I ended up debugging the library whole weekend( it was fun ) and modified it to work with my launchpad.( If any one wants modified code, send me a message. I'll happy to share it with you. )
Testing launchpad MQTT client : I want my launchpad to listen to rocker switch related messages from openHAB and toggle my bed lamp accordingly. Also I want my node to identify itself when it connects to broker. So when connected, this node should send a hello message on the topic "localnet\device\intro" with message "Hello, I'm Launchpad". I should be able to see this message on MQTT Lens. This is my entire code( I hope it's self explanatory ). It will subscribe to particular messages from openHAB and switch leds accordingly. This code is supposed to work with arduino too.
// Launchpad MQTTT client // #include "Ethernet.h" #include "PubSubClient.h" unsigned char led1Status = HIGH; unsigned char led2Status = HIGH; unsigned char led3Status = HIGH; unsigned char led4Status = HIGH; // Enter a MAC address for your controller below. // Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0x00, 0x1A, 0xB6, 0x02, 0xAE, 0xA0 }; // MQTT broker client byte mqttServer[] = { 192, 168, 1, 79 }; EthernetClient client; PubSubClient mqttNode( mqttServer, 1883, mqttCallback, client ); // topic to subscribe from broker char *subTopic = "localnet/openHAB/devices/#"; void mqttCallback( char *topic, byte *payload, unsigned int length ) { Serial.print( topic ); Serial.print( " : " ); Serial.print( (char*) payload ); Serial.print( " : " ); Serial.println( length, DEC ); int position = 0; int ledId = 0; while( topic[position] != '\0' ) { position++; } position--; if( topic[position] == '0' ) { ledId = D1_LED; } else if( topic[position] == '1' ) { ledId = D2_LED; } else if( topic[position] == '2' ) { ledId = D3_LED; } else if( topic[position] == '3' ) { ledId = D4_LED; } else { Serial.print( "Unknown ID : " ); Serial.print( (char*)topic[position] ); Serial.print( " @ " ); Serial.println( position, DEC ); return; // we don't have tp process this message } // check whether ON or OFF if( payload[0] == 'O' && payload[1] == 'N' ) { digitalWrite( ledId, HIGH ); } else if( payload[0] == 'O' && payload[1] == 'F' && payload[2] == 'F' ) { digitalWrite( ledId, LOW ); } else { Serial.print( "Unknown command : " ); Serial.println( (char*)payload ); } } void setup() { // initialize LEDs pinMode( D1_LED, OUTPUT ); pinMode( D2_LED, OUTPUT ); pinMode( D3_LED, OUTPUT ); pinMode( D4_LED, OUTPUT ); digitalWrite( D1_LED, led1Status ); digitalWrite( D2_LED, led2Status ); digitalWrite( D3_LED, led3Status ); digitalWrite( D4_LED, led4Status ); // start the serial library: Serial.begin( 115200 ); Serial.print("Accquiring IP address..........."); // start the Ethernet connection: if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); // no point in carrying on, so do blinking forever forevermore: led1Status = LOW; led2Status = HIGH; led3Status = LOW; led4Status = HIGH; for(;;) { digitalWrite( D1_LED, led1Status ); digitalWrite( D2_LED, led2Status ); digitalWrite( D3_LED, led3Status ); digitalWrite( D4_LED, led4Status ); led1Status = ~led1Status; led2Status = ~led2Status; led3Status = ~led3Status; led4Status = ~led4Status; delay( 1000 ); } } Serial.println( "[OK]" ); printEthernetData(); led1Status = LOW; led2Status = LOW; led3Status = LOW; led4Status = LOW; digitalWrite( D1_LED, led1Status ); digitalWrite( D2_LED, led2Status ); digitalWrite( D3_LED, led3Status ); digitalWrite( D4_LED, led4Status ); // initialize MQTT connection if( mqttNode.connect("launchpad") != false ) { Serial.println( "Connected to broker" ); mqttNode.publish( "localnet/device/intro", "Hello, I'm Launchpad" ); mqttNode.subscribe( subTopic ); } else { Serial.println( "Failed to connect to broker.\n" ); } } void loop() { mqttNode.loop(); } void serialEvent() { while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read(); switch(inChar) { case 'h': printHelp(); break; case 'i': printEthernetData(); break; default: Serial.println(); Serial.println("Invalid menu option"); } } } void printHelp( void ) { Serial.println(); Serial.println("Help menu:"); Serial.println("\th: This help menu"); Serial.println("\ti: IP address information"); } void printEthernetData( void ) { // print your IP address: Serial.println(); Serial.println("IP Address Information:"); IPAddress ip = Ethernet.localIP(); Serial.print("IP Address:\t"); Serial.println(ip); // print your MAC address: IPAddress subnet = Ethernet.subnetMask(); Serial.print("NetMask:\t"); Serial.println(subnet); // print your gateway address: IPAddress gateway = Ethernet.gatewayIP(); Serial.print("Gateway:\t"); Serial.println(gateway); // print your gateway address: IPAddress dns = Ethernet.dnsServerIP(); Serial.print("DNS:\t"); Serial.println(dns); }
Wildcard subscriptions : In addition to allowing clients to subscribe to specific topics, mosquitto also allows the use of two wildcards in subscritions. + is the wildcard used to match a single level of hierarchy and # is used to match all subsequent levels of hierarchy.
Suppose you want to subscribe to topic "localnet/openHAB/devices/led0", "localnet/openHAB/devices/led1", "localnet/openHAB/devices/led2", "localnet/openHAB/devices/led3" etc., rather can subscribing individually to each topic, you can use
"localnet/openHAB/devices/#"
which will subscribe to all led topic messages.
And say now you want to subscribe to "localnet/openHAB/kitchen/temperature" and "localnet/openHAB/garden/temperature", you can use
"localnet/openHAB/+/temperature"
which will do both.
More information on wildcard subscriptions can be found at mosquitto
Step 3 : Setting up openHAB
First thing you I did was to configure my broker settings in openHAB configuration file. I'm using my local broker, so I configured it with my broker's IP. In case you are using public broker, modify accordingly.
# openhab.cfg mqtt:localbroker.url=tcp://192.168.1.79:1883 mqtt:localbroker.clientId=openHAB
Now add this code as mqttDemo.items :
Group All // EnOcean devices Switch switch0 (All) {enocean="{id=00:1A:34:82, eep=F6:02:01, channel=B}",mqtt=">[localbroker:localnet/openHAB/devices/led0:command:ON:default],>[localbroker:localnet/openHAB/devices/led0:command:OFF:default]"} Switch switch1 (All) {enocean="{id=00:1A:34:82, eep=F6:02:01, channel=A}",mqtt=">[localBroker:localnet/openHAB/devices/led1:command:ON:default],>[localbroker:localnet/openHAB/devices/led1:command:OFF:default]"} // ordinary devices Switch switch2 (All) {mqtt=">[localbroker:localnet/openHAB/devices/led2:command:ON:default],>[localbroker:localnet/openHAB/devices/led2:command:OFF:default]"} Switch switch3 (All) {mqtt=">[localbroker:localnet/openHAB/devices/led3:command:ON:default],>[localbroker:localnet/openHAB/devices/led3:command:OFF:default]"}
and mqttDemo.sitemap will look like :
sitemap mqttDemo label="MQTT Demo" { Frame label="Enocean Devices" { Switch item=switch0 label="Rocker 0" Switch item=switch1 label="Rocker 1" Switch item=switch2 label="Switch 2" Switch item=switch3 label="Switch 3" } }
Alternatively, you can configure openHAB to send all the events happening on the eventbus by configuring it like this.
(Screen shot of openhab.cfg)
mqtt:localbroker.url=tcp://192.168.1.79:1883 mqtt:localbroker.clientId=openHAB mqtt-eventbus:broker=localbroker mqtt-eventbus:statePublishTopic=openHAB/stateUpdates/${item}/state mqtt-eventbus:commandPublishTopic=openHAB/commandUpdates/${item}/command
Testing openHAB MQTT : Use MQTT Lens to subscribe to topic "localnet/openHAB/#" ( or "openHAB/#" if you are using the second configuration ) which will list all the messages in topic starting with "localnet/openHAB/". You will be able to see messages from openHAB like this upon toggling the Switches.
Demo Time
This is how my network looks now :
cheers,
vish
References :
http://www.slideshare.net/andypiper/introducing-mqtt
https://github.com/mqtt/mqtt.github.io/wiki
http://en.wikipedia.org/wiki/MQTT
http://stephendnicholas.com/archives/1217
http://mobilebit.wordpress.com/2013/05/03/rest-is-for-sleeping-mqtt-is-for-mobile/
The Seven Best MQTT Client Tools
Beyond MQTT
http://www.eclipse.org/community/eclipse_newsletter/2014/february/article2.php
Top Comments