Note: Nov. 27, 2015, this is a quick first draft. I will be adding more details over the next few days including code snippets.
Here are some insights gained from building an actual MQTT based UI.
Using a system consisting of a Raspberry Pi 2 with Raspbian Jessie installed on an 8GB class 10 SD card I wrote a simple Python program to control a seed germination system for my garden.
The system includes a temperature probe to measure soil temperature and controls for mains power for an LED light source and a plant propagation heating pad. It also has sensors for air temperature, humidity and soil moisture.
The program’s user interface is browser based consisting of a web page including controls for:
- The time for lights to turn on
- Duration of photoperiod
- Target soil temperature
Buttons for:
- Manual control of power
- Restarting the software
- Rebooting the Pi
- Emergency stop
There are also live data displays for:
- Soil temperature
- Soil moisture
- Humidity
- Air temperature
- Pi’s CPU temperature
The system uses Mosquitto with websockets enabled as both MQTT message broker and web server and it works quite well. Bootstrap was used to make the mobile-first UI so it can be used on a large screen monitor, a phone or anything in between.
A simple messaging protocol
In developing the UI I had to create a messaging protocol (API) for my particular application.
MQTT messages consist of a topic and a payload. The topic is a slash separated list of words that the MQTT broker (mosquitto) uses to distribute the messages. The payload is the actual content of the message. For example in the message uicomm/pj/setSw_1 true , ‘uicomm’ is a generic part of the topic that identifies the message as part of the UI system, the ‘pj’ part indicates that it is a message from Python to JavaScript, ‘setSw_1’ is the subject of the message and ‘true’ is the payload. JavaScript uses the subject and the payload to update the web page. In this case when a timer in the Python program turns on a light, it sends this message to mosquitto. Mosquitto then forwards the message to any clients that have subscribed to UI messages from Python, i.e. any messages starting with uicomm/pj/. When the client (browser with the appropriate page loaded), receives the message, JavaScript updates the UI page to indicate that the light is on.
When a control on the UI page is clicked/tapped, JavaScript sends a message to Python via mosquitto such as uicomm/jp/ch-4 true. This tells Python, which is subscribed to any messages from JavaScript, to turn on power channel 4.
There is no polling involved and messages are only sent when something actually changes. This results in very low network traffic and minimal bandwidth.
There are a couple of other things that need to happen to make it all work nicely.
- When Python receives a message and perform an action it echoes a message back telling JavaScript that it is OK to update the web page with the new status. The sending client will have already done the update but any other clients subscribed the the Python messages will also be updated in real-time.
- Mosquitto has the ability to retain messages so that any client that later subscribes to messages from Python will receive the latest status info without Python having to do anything. New messages replace older retained messages that have the same topic.
In the Python program there is a function that is used whenever the program needs to send a message to the UI. It includes an argument that tells mosquitto whether to retain the message or not.
Starting mosquitto at boot under Raspbian Jessie (systemd)
When I first compiled mosquitto on the Jessie release of Raspbian (Debian) there were problems getting the program to start at system boot. Jessie uses a more efficient system for starting programs called systemd. In many cases the new system will work with older init scripts located in /etc/init.d. Not so with mosquitto. The solution was to write a new init file for use with systemd.
The new unit file is much simpler that the older init script and should be located at /etc/systemd/system/ with a file name of mosquitto.service. The mosquito.service file I am using is:
[Unit] Description=Mosquitto MQTT Broker daemon ConditionPathExists=/etc/mosquitto/mosquitto.conf After=network.target Requires=network.target [Service] Type=forking RemainAfterExit=no StartLimitInterval=0 PIDFile=/var/run/mosquitto.pid ExecStart=/usr/local/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf -d ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=2 [Install] WantedBy=multi-user.target
Once this file is placed in /etc/systemd/system/ as mosquitto.service it can be enabled with:
sudo systemctl enable mosquitto.service
You can either reboot the system to load mosquitto or use the command:
sudo systemctl daemon-reload
Once mosquitto is running you can use the following commands:
sudo systemctl start mosquitto
sudo systemctl stop mosquitto
sudo systemctl status mosquitto
sudo systemctl restart mosquitto
Mosquitto as web server
Mosquitto can be used as a web server if a listener (client) is using the web-sockets protocol. This is great for serving pages of a browser based UI. It would not be practical for a high traffic production site of course.
I created a directory structure in /usr/share/mosquitto/www and placed the necessary CSS, fonts and JaveScript files in the following subdirectories:
- css
- fonts
- js
The index.html and any other HTML files(s) for the UI are in the www directory.
To enable mosquitto as an HTTP server, edit the mosquitto config file, /etc/mosquitto/mosquitto.conf.
at ~line 301, in the Extra listeners section, uncomment http_dir and add the location of the web directory:
http_dir /usr/share/mosquitto/www
After saving the config file and restarting mosquitto:
sudo systemctl restart mosquitto
Pointing your favorite browser to the URL of your Raspi with the websockets port appended, e.g. 192.168.1.XXX:9001 should bring up the home page of the UI.
Thank you – very useful instructions!