Code for a smart light built using an esp8266
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

263 lines
7.2 KiB

-- Never change these unless the board changes
echo_pin = 2
trig_pin = 3
light_pin = 5
light_on = false
if adc.force_init_mode(adc.INIT_ADC)
then
node.restart()
return -- don't bother continuing, the restart is scheduled
end
print("System voltage (mV):", adc.readvdd33(0))
gpio.mode(light_pin, gpio.OUTPUT)
gpio.mode(echo_pin, gpio.INT) -- interrupt mode
gpio.mode(trig_pin, gpio.OUTPUT)
gpio.write(light_pin, gpio.HIGH)
function toggle_light()
if light_on then
gpio.write(light_pin, gpio.HIGH)
else
gpio.write(light_pin, gpio.LOW)
end
light_on = not light_on
end
function turn_light_on()
gpio.write(light_pin, gpio.LOW)
end
function turn_light_off()
gpio.write(light_pin, gpio.HIGH)
end
function tablelen(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
samples = {}
distance = {start_v=0.0, end_v=0.0}
sample_rate = 4 -- number of samples it requires before triggering, recommended <= 5, >=3
max_stderr = 0.5 -- margin of error allowed before triggering the light, recommended <= 1
distance_max = 30 -- threshold for how far an object must be to trigger it in cm
distance_min = 10 -- minimum distance (below this amount don't trigger it), recommended >= 5, <= 20
-- Get the mean value of a table
function mean(t)
-- http://lua-users.org/wiki/SimpleStats
local sum = 0
local count = 0
for k,v in pairs(t) do
if type(v) == 'number' then
sum = sum + v
count = count + 1
end
end
return (sum / count)
end
function stdev(t)
-- http://lua-users.org/wiki/SimpleStats
local m
local vm
local sum = 0
local count = 0
local result
m = mean(t)
for k, v in pairs(t) do
if type(v) == 'number' then
vm = v - m
sum = sum + (vm * vm)
count = count + 1
end
end
result = math.sqrt(sum / (count-1))
return result
end
function stderr(t)
-- based on the definition of standard error
return stdev(t) / math.sqrt(tablelen(t))
end
function measure(level, ts, evcount)
if level == 1 then
distance["start_v"] = ts
else
-- check if there's a low voltage but no starting high voltage
if distance["start_v"] > 0 then
distance["end_v"] = ts
if distance["start_v"] >= distance["end_v"] then
distance = {start_v=0.0, end_v=0.0} -- bad reading
return
end
-- See https://www.adafruit.com/product/165
local temperature = ((adc.read(0)*100)-50)/1000
-- See http://hyperphysics.phy-astr.gsu.edu/hbase/Sound/souspe.html
local speed_of_sound = (331.4 + 0.6*temperature)/10000
-- See https://randomnerdtutorials.com/esp8266-nodemcu-hc-sr04-ultrasonic-arduino/
local d = ((distance["end_v"] - distance["start_v"]) * speed_of_sound) / 2.0
-- Only care about values between 10 and 50
if math.floor(d) > distance_min and d < distance_max then
table.insert(samples, d)
local s = stderr(samples)
if s > max_stderr and tablelen(samples) == (sample_rate - 1) then -- start over if it would cause stderr to be too great
samples = {}
distance = {start_v=0.0, end_v=0.0}
end
end
distance = {start_v=0, end_v=0}
end
end
local s_num = tablelen(samples)
if s_num >= sample_rate then
local distance_mean = mean(samples)
local s = stderr(samples)
if s < max_stderr and distance_mean < distance_max and distance_mean > distance_min then
print("detected something")
print("stder = " .. s)
print("distance = " .. distance_mean)
if not light_on then
toggle_light()
end
end
samples = {}
end
end
function trig()
gpio.write(trig_pin, gpio.HIGH)
tmr.delay(11)
gpio.write(trig_pin, gpio.LOW)
end
-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there
dofile("credentials.lua")
function startup()
if file.open("init.lua") == nil then
print("init.lua deleted or renamed")
else
print("Running")
file.close("init.lua")
print("Starting up")
sntp.sync(nil,
function(sec, usec, server, info)
print("sync'd")
tmr.create():alarm(61, tmr.ALARM_AUTO, trig)
gpio.trig(echo_pin, "both", measure)
end,
nil, 1)
print("Timer created")
require("httpserver").createServer(80, function(req, res)
print("+R", req.method, req.url, node.heap())
req.onheader = function(self, name, value) -- luacheck: ignore
print("+H", name, value)
end
-- setup handler of body, if any
req.ondata = function(self, chunk) -- luacheck: ignore
print("+B", chunk and #chunk, node.heap())
print(req.url)
if not chunk then
-- reply
res:send(nil, 200)
res:send_header("Connection", "close")
if req.url == "/toggle" then
toggle_light()
elseif req.url == "/on" then
turn_light_on()
elseif req.url == "/off" then
turn_light_off()
end
res:send("The light is " .. (light_on and "on\n" or "off\n"))
res:finish()
end
end
end)
end
end
-- Define WiFi station event callbacks
wifi_connect_event = function(T)
print("Connection to AP("..T.SSID..") established!")
print("Waiting for IP address...")
if disconnect_ct ~= nil then disconnect_ct = nil end
end
wifi_got_ip_event = function(T)
-- Note: Having an IP address does not mean there is internet access!
-- Internet connectivity can be determined with net.dns.resolve().
print("Wifi connection is ready! IP address is: "..T.IP)
print("Startup will resume momentarily, you have 3 seconds to abort.")
print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end
wifi_disconnect_event = function(T)
if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then
--the station has disassociated from a previously connected AP
return
end
-- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration.
local total_tries = 75
print("\nWiFi connection to AP("..T.SSID..") has failed!")
--There are many possible disconnect reasons, the following iterates through
--the list and returns the string corresponding to the disconnect reason.
for key,val in pairs(wifi.eventmon.reason) do
if val == T.reason then
print("Disconnect reason: "..val.."("..key..")")
break
end
end
if disconnect_ct == nil then
disconnect_ct = 1
else
disconnect_ct = disconnect_ct + 1
end
if disconnect_ct < total_tries then
print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")")
else
wifi.sta.disconnect()
print("Aborting connection to AP!")
disconnect_ct = nil
end
end
-- Register WiFi Station event callbacks
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event)
print("Connecting to WiFi access point...")
wifi.setmode(wifi.STATION)
wifi.sta.config({ssid=SSID, pwd=PASSWORD})
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default