Start up an AP if unable to reconnect to last network
Posted: Wed Feb 25, 2015 1:36 am
I wanted to be able to configure a device purely through wifi, in case I didn't have a serial port available. I couldn't find anything on here to do that, so I decided to write my own. Maybe it will be useful for someone.
On startup, it attempts to reconnect to its last SSID. If that's successful, it launches 'task.lua'. If it's not able to reconnect after 30 seconds, it starts up an Access Point named 'ConfigureMe-XX-YY' with no security. If you connect to it and go to http://192.168.4.1/ in your browser, you get a little configuration screen that will let you tell the ESP8266 what SSID and password you'd like it to connect to. Then you hit the reboot button, and (hopefully) your new settings take effect.
This is bigger than I'd like, but it seems to work for me. Maybe others have some ideas for optimizing it. It's split into three files to avoid heap problems.
init.lua:
configServer.lua:
configServerInit.lua:
On startup, it attempts to reconnect to its last SSID. If that's successful, it launches 'task.lua'. If it's not able to reconnect after 30 seconds, it starts up an Access Point named 'ConfigureMe-XX-YY' with no security. If you connect to it and go to http://192.168.4.1/ in your browser, you get a little configuration screen that will let you tell the ESP8266 what SSID and password you'd like it to connect to. Then you hit the reboot button, and (hopefully) your new settings take effect.
This is bigger than I'd like, but it seems to work for me. Maybe others have some ideas for optimizing it. It's split into three files to avoid heap problems.
init.lua:
Code: Select all
timeout = 30 -- seconds to wait for connect before going to AP mode
statuses = {[0]="Idle",
[1]="Connecting",
[2]="Wrong password",
[3]="No AP found",
[4]="Connect fail",
[5]="Got IP",
[255]="Not in STATION mode"}
checkCount = 0
function checkStatus()
checkCount = checkCount + 1
local s=wifi.sta.status()
print("Status = " .. s .. " (" .. statuses[s] .. ")")
if(s==5) then -- successful connect
launchApp()
return
elseif(s==2 or s==3 or s==4) then -- failed
startServer()
return
end
if(checkCount >= timeout) then
startServer()
return
end
end
function launchApp()
cleanup()
print("I'm connected to my last network. Launching my real task.")
local task = 'task.lua'
local f=file.open(task, 'r')
if(f == nil) then
print('Error opening file ' .. task)
return
end
f.close()
dofile(task)
end
function startServer()
lastStatus = statuses[wifi.sta.status()]
cleanup()
print("network not found, switching to AP mode")
dofile('configServer.lua')
end
function cleanup()
-- stop our alarm
tmr.stop(0)
-- nil out all global vars we used
timeout = nil
statuses = nil
checkCount = nil
-- nil out any functions we defined
checkStatus = nil
launchApp = nil
startServer = nil
cleanup = nil
-- take out the trash
collectgarbage()
-- pause a few seconds to allow garbage to collect and free up heap
tmr.delay(5000)
end
-- make sure we are trying to connect as clients
wifi.setmode(wifi.STATION)
wifi.sta.autoconnect(1)
-- every second, check our status
tmr.alarm(0, 1000, 1, checkStatus)
configServer.lua:
Code: Select all
dofile("configServerInit.lua")
apRefresh = 15 -- how many seconds between when we scan for updated AP info for the user
currentAPs = {}
newssid = ""
function listAPs_callback(t)
if(t==nil) then
return
end
currentAPs = t
end
function listAPs()
wifi.sta.getap(listAPs_callback)
end
function sendPage(conn)
conn:send('HTTP/1.1 200 OK\n\n')
conn:send('<!DOCTYPE HTML>\n<html>\n<head><meta content="text/html; charset=utf-8">\n<title>Device Configuration</title></head>\n<body>\n<form action="/" method="POST">\n')
if(lastStatus ~= nil) then
conn:send('<br/>Previous connection status: ' .. lastStatus ..'\n')
end
if(newssid ~= "") then
conn:send('<br/>Upon reboot, unit will attempt to connect to SSID "' .. newssid ..'".\n')
end
conn:send('<br/><br/>\n\n<table>\n<tr><th>Choose SSID to connect to:</th></tr>\n')
for ap,v in pairs(currentAPs) do
conn:send('<tr><td><input type="button" onClick=\'document.getElementById("ssid").value = "' .. ap .. '"\' value="' .. ap .. '"/></td></tr>\n')
end
conn:send('</table>\n\nSSID: <input type="text" id="ssid" name="ssid" value=""><br/>\nPassword: <input type="text" name="passwd" value=""><br/>\n\n')
conn:send('<input type="submit" value="Submit"/>\n<input type="button" onClick="window.location.reload()" value="Refresh"/>\n<br/>If you\'re happy with this...\n<input type="submit" name="reboot" value="Reboot!"/>\n')
conn:send('</form>\n</body></html>')
end
function url_decode(str)
local s = string.gsub (str, "+", " ")
s = string.gsub (s, "%%(%x%x)",
function(h) return string.char(tonumber(h,16)) end)
s = string.gsub (s, "\r\n", "\n")
return s
end
function incoming_connection(conn, payload)
if (string.find(payload, "GET /favicon.ico HTTP/1.1") ~= nil) then
print("GET favicon request")
elseif (string.find(payload, "GET / HTTP/1.1") ~= nil) then
print("GET received")
sendPage(conn)
else
print("POST received")
local blank, plStart = string.find(payload, "\r\n\r\n");
if(plStart == nil) then
return
end
payload = string.sub(payload, plStart+1)
args={}
args.passwd=""
-- parse all POST args into the 'args' table
for k,v in string.gmatch(payload, "([^=&]*)=([^&]*)") do
args[k]=url_decode(v)
end
if(args.ssid ~= nil and args.ssid ~= "") then
print("New SSID: " .. args.ssid)
print("Password: " .. args.passwd)
newssid = args.ssid
wifi.sta.config(args.ssid, args.passwd)
end
if(args.reboot ~= nil) then
print("Rebooting")
conn:close()
node.restart()
end
conn:send('HTTP/1.1 303 See Other\n')
conn:send('Location: /\n')
end
end
-- start a periodic scan for other nearby APs
tmr.alarm(0, apRefresh*1000, 1, listAPs)
listAPs() -- and do it once to start with
-- Now we set up the Web Server
srv=net.createServer(net.TCP)
srv:listen(80,function(sock)
sock:on("receive", incoming_connection)
sock:on("sent", function(sock)
sock:close()
end)
end)
configServerInit.lua:
Code: Select all
apNamePrefix = "ConfigureMe" -- your AP will be named this plus "-XX-YY", where XX and YY are the last two bytes of this unit's MAC address
apNetConfig = {ip = "192.168.4.1", -- NodeMCU seems to be hard-coded to hand out IPs in the 192.168.4.x range, so let's make sure we're there, too
netmask = "255.255.255.0",
gateway = "192.168.4.1"}
-- Set up our Access Point with the proper name and settings
local apName = apNamePrefix .. "-" .. string.sub(wifi.ap.getmac(),13)
print("Starting up AP with SSID: " .. apName);
wifi.setmode(wifi.STATIONAP)
local apSsidConfig = {}
apSsidConfig.ssid = apName
wifi.ap.config(apSsidConfig)
wifi.ap.setip(apNetConfig)