Programming the ESP8266 With Lua

The ESP8266 is a wonderful little ecosystem and I've become quite fond of it lately. The Feather HUZZAH ESP8266 comes pre-flashed with the NodeMCU Lua interpreter, as does the HUZZAH ESP8266 breakout. This means you can program the WiFi chip directly using Lua.

When you are programming the ESP8266 with Lua, you can write any Lua programs you like to the board's flash memory. If you create a program named init.lua it will run when the board resets.

If you have an infinite loop in init.lua you WILL brick your device!!! The only solution is to re-flash the firmware.

I use an init.lua that I found by a search on the Internet while I was trying to learn enough about Lua and the ESP8266 to avoid bricking my device (and I still did it once). If you have an error in init.lua, one of two things will happen. First, it could fall through to the Lua interpreter. Second, it could go into an infinite loop that you cannot interrupt, bricking the device. If (when?) this happens, see the page titled "How to Re-flash Your ESP8266."

Step 1 - Download the Software

There are two programs for the ESP8266 that make up the security system. They are init.lua and security.lua. Here is init.lua:

SSID    = "YOUR_WIFI_SSID"
APPWD   = "YOUR_WIFI_PASSWORD"
CMDFILE = "ping.lua"   -- File that is executed after connection

wifiTrys     = 15     -- Counter of trys to connect to wifi
NUMWIFITRYS  = 200    -- Maximum number of WIFI Testings while waiting for connection

function launch()
  print("Connected to WIFI!")
  print("IP Address: " .. wifi.sta.getip())
  -- Call our command file. Note: if you foul this up you'll brick the device!
  dofile("security.lua")
  makeConn()
end

function checkWIFI() 
  if ( wifiTrys > NUMWIFITRYS ) then
    print("Sorry. Not able to connect")
  else
    ipAddr = wifi.sta.getip()
    if ( ( ipAddr ~= nil ) and  ( ipAddr ~= "0.0.0.0" ) )then
      tmr.alarm( 1 , 500 , 0 , launch )
    else
      -- Reset alarm again
      tmr.alarm( 0 , 2500 , 0 , checkWIFI)
      print("Checking WIFI..." .. wifiTrys)
      wifiTrys = wifiTrys + 1
    end 
  end 
end

print("-- Starting up! ")

-- Lets see if we are already connected by getting the IP
ipAddr = wifi.sta.getip()
if ( ( ipAddr == nil ) or  ( ipAddr == "0.0.0.0" ) ) then
  -- We aren't connected, so let's connect
  print("Configuring WIFI....")
  wifi.setmode( wifi.STATION )
  wifi.sta.config( SSID , APPWD)
  print("Waiting for connection")
  tmr.alarm( 0 , 2500 , 0 , checkWIFI )
else
 -- We are connected, so just run the launch code.
 launch()
end

Just download the above program to your laptop for now. Don't upload it to your ESP8266 yet!

Here is the program security.lua:

-- ###############################################################
-- secrity.lua - functions to support reed switch magnetic door and
-- window normally-open, which will be part of a security system.
--
-- Note: these are just dumb sensors programmed to insert their
-- values into the local network MQTT queue (aka topic). The 
-- Raspberry Pi (either the one running the broker or another one)
-- has all the smarts about what to do with the data in the topic
-- queue.
--
-- Note 2: I'll be improving this code as I learn more about Lua.
-- I recommend the book "Programming in Lua" by Roberto
-- Ierusalimschy (one of the designers of Lua).
--
-- Phil Moyer
-- Adafruit
-- 
-- May 2016
--
-- This code is open source, released under the BSD license. All
-- redistribution must include this header.
-- ###############################################################


-- ###############################################################
-- Global variables and parameters.
-- ###############################################################

sensorID = "security_001"	-- a sensor identifier for this device
tgtHost = "MQTT_BROKER_IP_ADDR"	-- target host (broker)
tgtPort = 1883			-- target port (broker listening on)
mqttUserID = "MQTT_USER_ID"		-- account to use to log into the broker
mqttPass = "MQTT_USER_PASSWORD"		-- broker account password
mqttTimeOut = 120		-- connection timeout
dataInt = 1			-- data transmission interval in seconds
topicQueue = "/security"	-- the MQTT topic queue to use

-- You shouldn't need to change anything below this line. -Phil --

-- ###############################################################
-- Functions
-- ###############################################################

-- Function pubEvent() publishes the sensor value to the defined queue.

function pubEvent()
	rv = adc.read(0)				-- read light sensor
	pubValue = sensorID .. " " .. rv		-- build buffer
	print("Publishing to " .. topicQueue .. ": " .. pubValue)	-- print a status message
	mqttBroker:publish(topicQueue, pubValue, 0, 0)	-- publish
end


-- Reconnect to MQTT when we receive an "offline" message.

function reconn()
	print("Disconnected, reconnecting....")
	conn()
end


-- Establish a connection to the MQTT broker with the configured parameters.

function conn()
	print("Making connection to MQTT broker")
	mqttBroker:connect(tgtHost, tgtPort, 0, function(client) print ("connected") end, function(client, reason) print("failed reason: "..reason) end)
end


-- Call this first! --
-- makeConn() instantiates the MQTT control object, sets up callbacks,
-- connects to the broker, and then uses the timer to send sensor data.
-- This is the "main" function in this library. This should be called 
-- from init.lua (which runs on the ESP8266 at boot), but only after
-- it's been vigorously debugged. 
--
-- Note: once you call this from init.lua the only way to change the
-- program on your ESP8266 will be to reflash the NodeCMU firmware! 

function makeConn()
	-- Instantiate a global MQTT client object
	print("Instantiating mqttBroker")
	mqttBroker = mqtt.Client(sensorID, mqttTimeOut, mqttUserID, mqttPass, 1)

	-- Set up the event callbacks
	print("Setting up callbacks")
	mqttBroker:on("connect", function(client) print ("connected") end)
	mqttBroker:on("offline", reconn)

	-- Connect to the Broker
	conn()

	-- Use the watchdog to call our sensor publication routine
	-- every dataInt seconds to send the sensor data to the 
	-- appropriate topic in MQTT.
	tmr.alarm(0, (dataInt * 1000), 1, pubEvent)
end


-- ###############################################################
-- "Main"
-- ###############################################################

-- No content. -prm

You will need to change the parameters appropriately for your network and MQTT configuration. Also, mark your sensors somehow with the sensorID value. Make sure you change this in security.lua for every device you make. The sensorID needs to be unique or the alarm system will generate alarms with the wrong ingress point listed! You should mark the sensorID on the sensor somehow (masking tape and sharpie, zip-top bag and a sticky note, toe tag, etc.) so you don't get confused when you're installing them around the house or office.

I use sequential numbers for the sensor ID, as you can see from the security.lua code.

At this point you could re-write the code to keep track of state changes in the sensor and only send and event to the MQTT broker when the state changes. I'm only beginning to learn Lua, so that was a little beyond my skills, so I put all the smarts of the system into the Python monitor program. Speaking of learning Lua, I recommend Roberto Ierusalimschy's book "Programming in Lua," which is a great introduction to Lua by one of Lua's creators. Learning Lua will, I think, significantly enhance your enjoyment of the ESP8266 devices.

Step 2 - Load Flash Memory on the ESP8266

Once you have the code for the two Lua programs saved on your local machine and edited accordingly, you need to upload them to the flash memory of the ESP8266 device. I am on a Mac and use a tool called luatool that just uploads programs into the ESP8266 flash.

Luatool is written in Python so it should run on any OS that supports Python. You can download luatool from its GitHub repository.

Here's what it looks like to run the tool:

This is a sample program I wrote that just blinks the LED; it's not part of the security system. The example shows what it looks like to run luatool, though.

Step 3 - Reset the ESP8266

When you have uploaded both programs, security.lua and init.lua, to flash, reset the ESP8266 with the reset button and your sensor should boot up and be working. You should connect to it with an appropriate serial device (FTDI friend, console cable, or USB cable depending on your ESP8266 board) and a terminal program like CoolTerm, then press the reset button. You should see the NodeMCU banner, followed by the WiFi connection, followed by some switch state information.

Change the sensorID parameter in security.lua for each sensor you flash and upload both Lua programs to each sensor. Once you have done this, you can install the switches on your doors and windows, then plug in the power supply for each sensor.

Next: Configuring MQTT on the Raspberry Pi

Last updated on 2016-08-18 at 08.46.01 AM Published on 2016-06-23 at 10.55.31 PM