178 lines
6.8 KiB
HTML
178 lines
6.8 KiB
HTML
|
define(`TITLE', `First ESP8266 Project: WiFi Power Switch')
|
||
|
define(`DATE', `2015-09-14')
|
||
|
define(`CONTENT', `
|
||
|
<a href="http://a385e-5.de/wp-content/uploads/IMG_3826-e1442696191894.jpg" rel="attachment wp-att-693"><img src="http://a385e-5.de/wp-content/uploads/IMG_3826-e1442696191894-768x1024.jpg" alt="IMG_3826" width="474" height="632" class="alignnone size-large wp-image-693" /></a>
|
||
|
|
||
|
The ESP8266 boards (ESP-01, cmp. <a href="http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family#esp-01"> http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family#esp-01 </a>
|
||
|
) 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 (<a href="http://www.nodemcu.com/index_en.html"> http://www.nodemcu.com/index_en.html</a>) and it is exactly what I was looking for since a while: <a href="http://www.lua.org/">Lua</a> 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.
|
||
|
|
||
|
<!--more-->
|
||
|
|
||
|
|
||
|
|
||
|
No need to install an SDK on the PC, just an upload tool like <a href="https://github.com/kmpm/nodemcu-uploader">nodemcu-uploader.py</a> or <a href="http://esp8266.ru/esplorer/">esplorer</a> is required. First the nodemcu firmware needs to be installed on the ESP8266 board. I found a very good intro <a href="http://www.whatimade.today/loading-the-nodemcu-firmware-on-the-esp8266-windows-guide/">here</a>.
|
||
|
|
||
|
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 <a href="https://primalcortex.wordpress.com/2014/12/30/esp8266-nodemcu-and-lua-language-and-some-arduino-issues/">here</a> and <a href="https://primalcortex.wordpress.com/2015/02/06/nodemcu-and-mqtt-how-to-start/">here</a>.
|
||
|
|
||
|
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:
|
||
|
|
||
|
<pre>
|
||
|
-- 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
|
||
|
</pre>
|
||
|
|
||
|
|
||
|
<pre>
|
||
|
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
|
||
|
</pre>
|
||
|
|
||
|
<img src="http://a385e-5.de/wp-content/uploads/IMG_3814-1024x768.jpg" alt="IMG_3814" width="474" height="356" class="alignnone size-large wp-image-688" />
|
||
|
|
||
|
<a href="http://a385e5.files.wordpress.com/2015/09/scan_006894_1.pdf"><img src="http://a385e5.files.wordpress.com/2015/09/scan_006894.png" alt="scan_006894" width="924" height="603" class="alignnone size-full wp-image-675" /></a>
|
||
|
|
||
|
|
||
|
Important note: this thing runs on mains power. Don't play with it unless you know exactly what you are doing!
|
||
|
')
|