The following series of posts will document my journey to full Home Automation using the MQTT protocol, all custom hardware (Arduino and ESP8266 based), and all tied together using OpenHAB
- Part 1: Setting up the server / environment
- Part 2: Publish, Subscribe, Command, State, and WTFs
- Part 3: Hardware: Arduino with Ethernet Shield
- Part 4: Hardware: ESP8266 with NodeMCU firmware
- Part 5: Hardware: Sensors
- Part 5.1: Graphing Sensor Data
- Part 6: OpenHAB Automation Rules
If you are not familiar with the ESP8266, I will be very brief since the NodeMCU Wiki is a huge treasure chest of information! See https://github.com/nodemcu/nodemcu-firmware/wiki
The ESP8266 is a CHEAP (Undcer $5) Wifi module with built in microcontroller. NodeMCU is a replacement firmware for the default Espessif firmware, that turns the onboard microcontroller into a LUA interpreter. In my plans these will be for simple little inline power switches on table lamps, desk fans, and the like. I am designing a PCB with a ESP8266, 220v->5v PSU (HLK-PM01), 3.3v LDO Regulator, Omron G3MB-202P Solid state relay (2A @240v = 480w max, so ideal for 60w desk lamps, etc) and all the supporting circuitry
So lets dive right in.
Hardware setup:

LUA Sketch: Upload as init.lua (Again – this assumes you have played with NodeMCU before)
Also assumes you have already configured Wifi on the ESP8266
--Init
DeviceID="esp01"
RoomID="1"
Broker="192.168.0.100"
--GPIO0 is connected to switch with internal pullup enabled
gpio.mode(3,gpio.INPUT,gpio.PULLUP)
--GPIO2 is connected to LED via resistor, initially off
gpio.mode(4,gpio.OUTPUT)
gpio.write(4,gpio.HIGH)
m = mqtt.Client("ESP8266".. DeviceID, 180, "user", "password")
m:lwt("/lwt", "ESP8266", 0, 0)
m:on("offline", function(con)
print ("Mqtt Reconnecting...")
tmr.alarm(1, 10000, 0, function()
m:connect(Broker, 1883, 0, function(conn)
print("Mqtt Connected to:" .. Broker)
mqtt_sub() --run the subscription function
end)
end)
end)
-- Pin to also toggle the status so you don't have to keep pulling out your phone to turn on device
gpio.trig(3, "down",function (level)
local PinValue=gpio.read(4)
--Change the state
if (PinValue==0) then
--The read resets the output to 0, put it back
gpio.write(4,1)
print("Light was on, turn off")
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","OFF",0,0)
else
gpio.write(4,0)
print("Light was off, turn on")
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","ON",0,0)
end
end)
-- on publish message receive event
m:on("message", function(conn, topic, data)
print("Recieved:" .. topic .. ":" .. data)
if (data=="ON") then
print("Enabling Output")
gpio.write(4,gpio.LOW)
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","ON",0,0)
elseif (data=="OFF") then
print("Disabling Output")
gpio.write(4,gpio.HIGH)
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","OFF",0,0)
else
print("Invalid - Ignoring")
end
end)
function mqtt_sub()
m:subscribe("/home/".. RoomID .."/" .. DeviceID .. "/p1/com",0, function(conn)
print("Mqtt Subscribed to OpenHAB feed for device " .. DeviceID)
end)
end
tmr.alarm(0, 1000, 1, function()
if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then
tmr.stop(0)
m:connect(Broker, 1883, 0, function(conn)
print("Mqtt Connected to:" .. Broker)
mqtt_sub() --run the subscription function
end)
end
end)
You should see some familiarity with the Arduino sketch
- We connect to a Broker and subscribes to the topic
/home/".. RoomID .."/" .. DeviceID .. "/p1/com
RoomID and DeviceID is configured near the top of the sketch as each device needs a different ID and each room in the house too
- When the device is turned on or off (either from the physical button on GPIO0, or from a MQTT Command, we send back the status to MQTT by publishing the state to
/home/".. RoomID .."/" .. DeviceID .. "/p1/state
Right, left add this to our OpenHAB configuration:
sudo vi /etc/openhab/configurations/sitemaps/dolphin.sitemap
and add the line for lamp1
sitemap dolphin label="Main Menu"
{
Frame label="MQTT" {
Switch item=mqttsw1 label="MQTT Switch 1"
Switch item=mqttsw2 label="MQTT Switch 2"
Switch item=lamp1 label="Office Lamp"
}
}
Lets configure the new item:
sudo vi /etc/openhab/configurations/items/dolphin.items
Add the Switch lamp1 item:
Group All
Switch mqttsw1 "Switch 1" (all) {mqtt=">[broker:/testsw/1:command:on:1],>[broker:/testsw/1:command:off:0]"}
Switch mqttsw2 "Switch 2" (all) {mqtt=">[broker:/testsw/2:command:off:0],>[broker:/testsw/2:command:on:1]"}
Switch lamp1 "Office Lamp" (all){mqtt=">[broker:/home/1/esp01/p1/com:command:on:ON],>[broker:/home/1/esp01/p1/com:command:off:OFF],<[broker:/home/1/esp01/p1/state:state:default]"}
Save and exit, restart OpenHAB
sudo /etc/init.d/openhab restart
Once it has started, access your new sitemap: http://127.0.0.1:8080/openhab.app?sitemap=dolphin

Now, you may notice that the line in dolphin.items looks a little different… We have a third parameter in the configuration: (Compare it to the two switches above it)
In a MQTT Binding on OpenHAB we define the direction of the message with a > (outgoing) or a < (Incoming) before the configuration
I color coded the three parameters below to highlight the point. The first to OUTPUTS (>) a message into /home/1/esp01/p1/com, whereas the third parameter INPUTS (<) a message from /home/1/esp01/p1/state
{mqtt=">[broker:/home/1/esp01/p1/com:command:on:ON],>[broker:/home/1/esp01/p1/com:command:off:OFF],<[broker:/home/1/esp01/p1/state:state:default]"}
Remember in Part 2 I mentioned you want to stay sane? Well, this is how simple that is. By configuring the device to PUBLISH its status whenever it changes, and configuring the OpenHAB item to listen for that change – we ensure the dashboard always matches the physical (malfunction like a dead bulb or stuck relay not withstanding of coarse)
I have the State Publish code in the Arduino sketch too, we just didnt add it on the mqtt binding for the two earlier items for simplicity’s sake. As an exercise you could use the third as an example and configure it now (:
The second reason for pushing the state back to OpenHAB is that I may not always want OpenHAB to be the ONLY control mechanism. I mean do you really want to pull out your phone each time you walk into a room just to turn on the light? No, many people would prefer a physical button on/near the device you want to turn on/off.
If you look at the lua sketch above you should see I already added it into this device:
This section of the sketch above handles just that. By reading the status of a button on GPIO3, and accordingly toggles GPIO4 to the opposite of its current state (Note it toggles the pin state) and then also publishes the change to OpenHAB (to update the item’s state)
-- Pin to also toggle the status so you don't have to keep pulling out your phone to turn on device
gpio.trig(3, "down",function (level)
local PinValue=gpio.read(4)
--Change the state
if (PinValue==0) then
--The read resets the output to 0, put it back
gpio.write(4,1)
print("Light was on, turn off")
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","OFF",0,0)
else
gpio.write(4,0)
print("Light was off, turn on")
m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","ON",0,0)
end
end)