define(`TITLE', `First ESP8266 Project: WiFi Power Switch')
define(`DATE', `2015-09-14')
define(`CONTENT', `
The ESP8266 boards (ESP-01, cmp. http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family#esp-01
) were lying on my desk for already some time. I never took the time to install the SDK on my Mac. Now I came around nodemcu ( http://www.nodemcu.com/index_en.html) and it is exactly what I was looking for since a while: Lua on a microcontroller. The idea behind is simple: When you are coding on a PC (or a Mac) and you have a rather small problem, a rather simple task to perform, you (or at least: I) would tend to use a scripting language. On a PC my first choice would be Python, other people prefer Perl or even Lua. On a microcontroller I also was faced those simple tasks (like toggling one bit based on an MQTT message) but never had a scripting language available. But here it is.
No need to install an SDK on the PC, just an upload tool like nodemcu-uploader.py or esplorer is required. First the nodemcu firmware needs to be installed on the ESP8266 board. I found a very good intro here.
My Lua script for this project running on the microcontroller consists of two files: ''init.lua'' and ''mqtt.lua''.
Coding Lua for nodemcu isn't really intuitive, at least not when you come from Arduinos or so. You can't simple write a ''setup'' method and do all the initialization within and a ''loop'' method to be continuously run. Doing it this way would either drive you into the chip's watchdog and would consume any available CPU any and leave nothing for the WiFi and TCP/IP stack. You've to work with timers and callback functions. I found two really good articles here and here.
What I also had to learn the hard way is that the nodemcu does not leave much space for the script. With a handful of debug messages containing some meaningful text (as it is no problem on the Arduino or Teensy) I over and over run into out of memory messages or even worse in misbehaving code.
I finally came to this code:
-- init.lua -- Constants SSID = "MessWLAN" APPWD = "geheim" CMDFILE = "mqtt.lua" -- Some control variables wifiTrys = 0 NUMWIFITRYS = 200 function launch() print("A") print("B: " .. wifi.sta.getip()) tmr.alarm(0, 5000, 0, function() dofile(CMDFILE) end ) end function checkWIFI() if ( wifiTrys > NUMWIFITRYS ) then print("C") else ipAddr = wifi.sta.getip() if ( ( ipAddr ~= nil ) and ( ipAddr ~= "0.0.0.0" ) )then tmr.alarm( 1 , 500 , 0 , launch ) else tmr.alarm( 0 , 2500 , 0 , checkWIFI) print("D: " .. wifiTrys) wifiTrys = wifiTrys + 1 end end end print("E") ipAddr = wifi.sta.getip() if ( ( ipAddr == nil ) or ( ipAddr == "0.0.0.0" ) ) then print("F") wifi.setmode( wifi.STATION ) wifi.sta.config( SSID , APPWD) print("G") tmr.alarm( 0 , 2500 , 0 , checkWIFI ) else launch() end
BROKER = "172.16.2.15" BRPORT = 1883 BRUSER = "" BRPWD = "" CLIENTID = "ESP8266-" .. node.chipid() SWITCH_PIN = 3 SWITCH_ID = 1 topics = {"IoT/Watchdog", "IoT/Switch" .. SWITCH_ID} TOPIC_WATCHDOG = 1 TOPIC_SWITCH = 2 pub_sem = 0 current_topic = 1 topicsub_delay = 50 id2 = 0 switch_state = false old_switch_state = true function alarm() node.restart() end gpio.mode(SWITCH_PIN, gpio.OUTPUT) gpio.write(SWITCH_PIN, gpio.LOW) m = mqtt.Client( CLIENTID, 120, BRUSER, BRPWD) tmr.alarm(4, 60000, 1, alarm) m:connect( BROKER , BRPORT, 0, function(conn) print("4") mqtt_sub() end) function mqtt_sub() if table.getn(topics) < current_topic then run_main_prog() else m:subscribe(topics[current_topic] , 0, function(conn) end) current_topic = current_topic + 1 tmr.alarm(5, topicsub_delay, 0, mqtt_sub ) end end function publish_status() if pub_sem == 0 then pub_sem = 1 local uptime = tmr.time() local switch_state_pres = 0 if (switch_state) then switch_state_pres = 1 end local msg = "{"metadata":{"device":"WiFiSwitch" .. SWITCH_ID .. ""}, "data":{"uptime":" .. uptime .. ", "state":" .. switch_state_pres .. "}}" m:publish("IoT/Status/WiFiSwitch" .. SWITCH_ID, msg ,0,0, function(conn) print("8: " .. id2 .. ", " .. switch_state_pres) pub_sem = 0 id2 = id2 +1 end) end end function run_main_prog() tmr.alarm(3, 1000, 1, publish_status ) m:on("message", function(conn, topic, data) print(topic .. ":" ) if (data ~= nil ) then print ( data ) if (topic == topics[TOPIC_SWITCH] and data == "switch on") then switch_state = true elseif (topic == topics[TOPIC_SWITCH] and data == "switch off") then switch_state = false elseif (topic == topics[TOPIC_SWITCH] and data == "switch toggle") then switch_state = not switch_state elseif (topic == topics[TOPIC_WATCHDOG] and data == "WauWau!") then print("9") tmr.alarm(4, 60000, 1, alarm) end if (switch_state ~= old_switch_state) then old_switch_state = switch_state if (switch_state) then gpio.write(SWITCH_PIN, gpio.LOW) else gpio.write(SWITCH_PIN, gpio.HIGH) end end end end ) end