How to control a led stripe via a PIR sensor
Seems an easy task, but there where some pitfalls.
For this project we needed the following parts:
Wemos D1 Mini NodeMcu
MOSFET IRF520 driver module
Aluminum Heat Sink for TO-220 MOSFET
DC-DC Buck Converter Step Down Module
HCSR501 human infrared sensor module
DHT11 Digital Temperature and Humidity Sensor
10cm 2.54mm 1pin Female to Female jumper wire Dupont cable
Put the things together
First we flash the Wemos D1 Mini NodeMcu. Then we connect the mosfet driver module via 3 dupont cables to the NodeMcu (SIG-D5, GND-G and VCC-5V).
The lua code files
init.lua
|
-- need PULLUP resistor for gpio4 otherwise watchdog reset gpio.mode(2, gpio.INPUT, gpio.PULLUP) local timer = tmr.create() timer:alarm(300, tmr.ALARM_SINGLE, function() pcall(function() require("program") end) end ) |
program.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
pcall(function() require("config") end) pcall(function() require("log") end) pcall(function() require("message") end) if config["lightPin"] > 0 then if pcall(function() require("light") end) then light.duty = 1023 light.startDimming() local timer = tmr.create() timer:alarm(5000, tmr.ALARM_SINGLE, function() light.duty = 0 light.startDimming() end ) end end if config["dht11Pin"] > 0 then pcall(function() require("th") end) end if config["pirPin"] > 0 or config["radarPin"] > 0 then if pcall(function() require("motion") end) then if config["pirPin"] > 0 then pir = motion:new() pir:init(config["pirPin"], config["mqttRoot"].."/pir") pir:start() end if config["radarPin"] > 0 then radar = motion:new() radar:init(config["radarPin"], config["mqttRoot"].."/radar") radar:start() end motion.init=nil motion.start=nil motion.new=nil end end pcall(function() require("web") end) -- check running services local timer = tmr.create() timer:alarm(180000, tmr.ALARM_AUTO, function() if message then if web.connected then if not message.connected then log.print("mqtt reconnect") message.m:close() message.m:connect(config["mqttBroker"], config["mqttPort"], 0, 1) end end end end ) |
config.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
-- pin config -- 0..GPIO16..no interrrupt..used for wakeup from deepsleep if connected to reset -- 1..GPIO5...DHT11 -- 2..GPIO4...pir -- 3..GPIO0...flash -- 4..GPIO2...LED -- 5..GPIO14..MOS_FET -- 6..GPIO12.. -- 7..GPIO13.. -- 8..GPIO15..connected to GND -- 9..GPIO3...TXD0 --10..GPIO1...RXT0 --11..GPIO9... --12..GPIO10.. config = {} config["ssid"] = "OpenWrt" config["pass"] = "pass0000" config["ledon"] = true config["webserver"] = true config["hostname"] = "home-buero-light" config["apPass"] = "apPass00" config["mqttUser"] = "mqttUser" config["mqttPass"] = "mqttPass" config["mqttBroker"] = "home-mqttbroker" config["mqttPort"] = "1883" config["pirPin"] = 0 config["radarPin"] = 0 config["lightPin"] = 0 config["lightSec"] = 10 config["dht11Pin"] = 0 if file.open("config.cfg", "r") then if cjson then config = cjson.decode(file.read()) end else if cjson then local ok, json ok, json = pcall(cjson.encode, config) print(ok) print(json) if ok then file.open("config.cfg", "w") file.write(json) file.close() end end end config["mqttRoot"] = string.gsub(config["hostname"], "-", "/") |
log.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
local P = {} log = P function P.print(message) local tm = rtctime.epoch2cal(rtctime.get()) file.open(P.file, "a+") local date = tm["year"]..tm["mon"]..tm["day"].." "..tm["hour"]..":"..tm["min"]..":"..tm["sec"] file.writeline(date.." "..message) print(date.." "..message) file.close() local files = file.list() if files[P.file] and files[P.file] > 100000 then file.remove(P.file..".1") file.rename(P.file, P.file..".1") end end P.file = "messages.log" local _, r = node.bootreason() P.print("boot reason "..r) return P |
light.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
local P = {} light = P P.pin = config["lightPin"] P.duty = 0 P.secondson = config["lightSec"] P.increment = 200 P.decrement = 30 P.mqttRoot = config["mqttRoot"] pwm.setup(P.pin, 500, 0) function P.startDimming() if message ~= nil and message.connected then message.m:publish(P.mqttRoot.."/duty", P.duty, 0, 0) end local timer = tmr.create() timer:alarm(50, tmr.ALARM_AUTO, function() local lduty = pwm.getduty(P.pin) if lduty > P.duty then lduty = lduty - P.decrement if lduty < P.duty then lduty = P.duty end end if lduty < P.duty then lduty = lduty + P.increment if lduty > P.duty then lduty = P.duty end end pwm.setduty(P.pin, lduty) if lduty == P.duty then timer:unregister() end end ) end if (message ~= nil) then message.addSub(P.mqttRoot.."/+", 0, function(subTopic, data) if subTopic == "/setduty" then P.duty = 0 + data if P.duty > 1023 then P.duty = 1023 elseif P.duty < 0 then P.duty = 0 end P.startDimming() elseif subTopic == "/secondson" then P.secondson = 0 + data if P.secondson > 3600 then P.secondson = 3600 elseif P.secondson < 0 then P.secondson = 0 end elseif subTopic == "/increment" then P.increment = 0 + data if P.increment > 1023 then P.increment = 1023 elseif P.increment < 1 then P.increment = 1 end elseif subTopic == "/decrement" then P.decrement = 0 + data if P.decrement > 1023 then P.decrement = 1023 elseif P.decrement < 1 then P.decrement = 1 end end end ) end return P |
th.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
local P = {} th = P P.pin = config["dht11Pin"] P.t = -99.9 P.h = -99.9 P.t2 = -99.9 P.h2 = -99.9 P.ts = -99.9 P.hs = -99.9 local status, t, h, t_d, h_d = dht.read11(P.pin) if status == dht.OK then P.t = t P.h = h end local timer = tmr.create() timer:alarm(1000, tmr.ALARM_AUTO, function() local status, t, h, t_d, h_d = dht.read11(P.pin) if status == dht.OK then if P.t == t then P.t2 = t elseif P.t2 == t then P.t = t else P.t2 = t end if P.h == h then P.h2 = h elseif P.h2 == h then P.h = h else P.h2 = h end end if message ~= nil and message.connected then if P.ts ~= P.t then if P.t == P.t2 then message.m:publish(config["mqttRoot"].."/temperature", P.t, 0, 0) P.ts = P.t end end if P.hs ~= P.h then if P.h == P.h2 then message.m:publish(config["mqttRoot"].."/humidity", P.h, 0, 0) P.hs = P.h end end end end ) return P |
motion.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
local P = {} motion = P function P:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function P:init(pin, root) self.pin = pin self.mqttRoot = root self.timer = tmr.create() gpio.mode(self.pin, gpio.INT, gpio.PULLUP) end function P:start() local function trigger(level) if message ~= nil and message.connected then message.m:publish(self.mqttRoot.."/level", level, 0, 0) end if light ~= nil then if level == gpio.HIGH then light.duty = 1023 light.startDimming() self.timer:stop() else self.timer:alarm(light.secondson*1000, tmr.ALARM_SINGLE, function() light.duty = 0 light.startDimming() end ) end end gpio.trig(self.pin, level == gpio.HIGH and "down" or "up") end gpio.trig(self.pin, "up", trigger) end return P |
web.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
local P = {} web = P if (config == nil) then log.print("No configuration found, WEB aborted.") web = nil return P end P.telnetMode = false P.connected = false P.ledon = config["ledon"] gpio.mode(4, gpio.OUTPUT) gpio.write(4, gpio.HIGH) cfg={} cfg.ssid = config["hostname"] cfg.pwd = config["apPass"] wifi.setmode(wifi.STATIONAP) wifi.ap.config(cfg) wifi.setmode(wifi.STATION) wifi.sta.sethostname(config["hostname"]) log.print('set mode=STATION (mode='..wifi.getmode()..')') log.print('MAC Address: '..wifi.sta.getmac()) log.print('Chip ID: '..node.chipid()) log.print('Heap Size: '..node.heap()) wifi.sta.config(config["ssid"], config["pass"]) log.print("Connecting to AP...") local connectionCount = 0; local timer = tmr.create() timer:alarm(1000, tmr.ALARM_AUTO, function() if wifi.sta.getip() == nil then if gpio.read(4) == gpio.LOW then gpio.write(4, gpio.HIGH) else gpio.write(4, gpio.LOW) end connectionCount = connectionCount + 1 if connectionCount > 30 then connectionCount = 0 if wifi.getmode() == wifi.STATION then wifi.setmode(wifi.STATIONAP) log.print("Connecting timeout...switch to wifi.STATIONAP\n") log.print("Start webserver") pcall(function() require("webserver") end) end end else local ip, nm, gw=wifi.sta.getip() log.print("IP Address: "..ip) log.print("Netmask: "..nm) log.print("Gateway Addr: "..gw) timer:unregister() if P.ledon then gpio.write(4, gpio.LOW) else gpio.mode(4, gpio.INPUT) end if wifi.getmode() == wifi.STATIONAP then wifi.setmode(wifi.STATION) P.telnetMode = false connectionCount = 0 log.print("Reconnection...switch to wifi.STATION\n") if not config["webserver"] then webserver = nil end end P.connected = true if config["webserver"] then log.print("Start webserver") pcall(function() require("webserver") end) end if log then sntp.sync("pool.ntp.org", function() log.print("INFO: Time Synchronized (via sntp). Current timestamp: "..rtctime.get()) end ) end end end ) return P |
webserver.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
local P = {} webserver = P P.telnetMode = false P.srv=net.createServer(net.TCP, 180) P.srv:listen(80, function(socket) local fifo = {} local fifo_drained = true local function sender(sck) if #fifo > 0 then sck:send(table.remove(fifo, 1)) else fifo_drained = true if not P.telnetMode then sck:close() webpage = nil end end end local function s_output(str) table.insert(fifo, str) if socket ~= nil and fifo_drained then fifo_drained = false sender(socket) end end if P.telnetMode then node.output(s_output, 0) end socket:on("receive", function(sck, payload) if P.telnetMode then node.input(payload) elseif pcall(function() dofile("webpage.lua") end) then webpage.serve(sck, payload) end end) socket:on("disconnection", function(sck) if P.telnetMode then node.output(nil) P.telnetMode = false end end) socket:on("sent", sender) if P.telnetMode then print("Welcome to NodeMCU.") end end) return P |
webpage.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
local P = {} webpage = P function P.serve(sck, payload) log.print(string.sub(payload, 0, 20)) if (payload:sub(1,10) == "GET / HTTP" or payload:sub(1,17) == "GET /light=TOGGLE") and light then if file.open("webpage.html") then local buf = file.read() file.close() buf = buf:format(light.duty, light.secondson, light.increment, light.decrement) sck:send(buf) end if string.find(payload, "light=TOGGLE") then if light.duty > 0 then light.duty = 0 else light.duty = 1023 end light.startDimming() end elseif payload:sub(1,11) == "GET /config" or payload:sub(1,14) == "GET /setconfig" or payload:sub(1,18) == "GET /telnetMode=ON" then if file.open("config.html") then local buf = file.read() file.close() local c = config buf = buf:format(c["ssid"], c["pass"], tostring(c["ledon"]), tostring(c["webserver"]), c["hostname"], c["apPass"], c["mqttUser"], c["mqttPass"], c["mqttBroker"], c["mqttPort"], c["pirPin"], c["radarPin"], c["lightPin"], c["lightSec"], c["dht11Pin"]) sck:send(buf) end if string.find(payload, "telnetMode=ON") then webserver.telnetMode = true end if payload:sub(1,14) == "GET /setconfig" then local _, i = string.find(payload, "?") local b = i+1, j while i ~= nil do _, i = string.find(payload, "=", i+1) if i == nil then break end _, j = string.find(payload, "&", i+1) if j == nil then _, j = string.find(payload, " ", i+1) if j == nil then break end end print(string.sub(payload, b, i-1).." "..string.sub(payload, i+1, j-1)) config[string.sub(payload, b, i-1)]=string.sub(payload, i+1, j-1) b = j+1 i = j end local ok, json ok, json = pcall(cjson.encode, config) if ok then file.open("config.cfg", "w") --file.write(json) file.close() end end else buf = 'HTTP/1.1 404 Not Found\n' buf = buf..'Content-Type: text/html\n' buf = buf..'\n' buf = buf..'<!DOCTYPE HTML>\n' buf = buf..'<html>\n' buf = buf..'Page not found!\n' buf = buf..'</html>\n' buf = buf..'\n' sck:send(buf) end end return P |
webpage.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
HTTP/1.1 200 OK Content-Type: text/html <!DOCTYPE HTML> <html> Duty %d: <br> Seconds On %d <br> Increment %d <br> Decrement %d <br> <br> <a href="/light=TOGGLE"><button>Toggle light</button></a> <br> <br> <a href="/telnetMode=ON"><button>Turn telnet mode on</button></a> </html> |
config.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
HTTP/1.1 200 OK Content-Type: text/html <!DOCTYPE HTML> <html> <head> <style> form { font-family: courier; } </style> </head> <body> <form action="setconfig"> ssid________<input type="text" name="ssid" value="%s"><br> pass________<input type="text" name="pass" value="%s"><br> ledon_______<input type="text" name="ledon" value="%s"><br> webserver___<input type="text" name="webserver" value="%s"><br> hostname____<input type="text" name="hostname" value="%s"><br> apPass______<input type="text" name="apPass" value="%s"><br> mqttUser____<input type="text" name="mqttUser" value="%s"><br> mqttPass____<input type="text" name="mqttPass" value="%s"><br> mqttBroker__<input type="text" name="mqttBroker" value="%s"><br> mqttPort____<input type="text" name="mqttPort" value="%s"><br> <br> <input type="submit" value="Submit"> </form> <br><br> <a href="/telnetMode=ON"><button>Turn telnet mode on</button></a> </body> </html> |