OpenHAB, MQTT, Arduino and ESP8266: Part 4: Hardware: ESP8266 with NodeMCU firmware

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:

schematic-node

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

  1. 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

  2. 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

mqtt

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)
Advertisements