|
@@ -0,0 +1,153 @@
|
|
|
+-- garage_door_open.lua
|
|
|
+-- Part of nodemcu-httpserver, example.
|
|
|
+-- Author: Marcos Kirsch
|
|
|
+
|
|
|
+--[[
|
|
|
+ This example assumed you have a Wemos D1 Pro to control a two-door garage.
|
|
|
+ For each garage door, a Wemos relay shield is used to simulate a button (connect relay in
|
|
|
+ parallel with the actual physical button) and a reed switch is used in order to know
|
|
|
+ whether a door is currently open or closed (install switch so that it is in the closed
|
|
|
+ position when your garage door is closed).
|
|
|
+
|
|
|
+ You can configure which GPIO pins you use for each function by modifying variable
|
|
|
+ pinConfig below.
|
|
|
+]]--
|
|
|
+
|
|
|
+local function pushTheButton(connection, pinConfig)
|
|
|
+ -- push the button!
|
|
|
+ gpio.write(pinConfig["controlPin"], gpio.HIGH)
|
|
|
+ gpio.mode(pinConfig["controlPin"], gpio.OUTPUT, gpio.FLOAT)
|
|
|
+ tmr.delay(300000) -- in microseconds
|
|
|
+ gpio.mode(pinConfig["controlPin"], gpio.INPUT, gpio.FLOAT)
|
|
|
+ gpio.write(pinConfig["controlPin"], gpio.LOW)
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+local function readDoorStatus(pinConfig)
|
|
|
+ -- When the garage door is closed, the reed relay closes, grounding the pin and causing us to read low (0).
|
|
|
+ -- When the garage door is open, the reed relay is open, so due to pullup we read high (1).
|
|
|
+ gpio.write(pinConfig["statusPin"], gpio.HIGH)
|
|
|
+ gpio.mode(pinConfig["statusPin"], gpio.INPUT, gpio.PULLUP)
|
|
|
+ if gpio.read(pinConfig["statusPin"]) == 1 then return 'open' else return 'closed' end
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+local function sendResponse(connection, httpCode, errorCode, action, pinConfig, message)
|
|
|
+
|
|
|
+ -- Handle nil inputs
|
|
|
+ if action == nil then action = '' end
|
|
|
+ if pinConfig == nil then
|
|
|
+ pinConfig = {}
|
|
|
+ pinConfig["door"] = 0
|
|
|
+ pinConfig["controlPin"] = 0
|
|
|
+ pinConfig["statusPin"] = 0
|
|
|
+ end
|
|
|
+ if message == nil then message = '' end
|
|
|
+
|
|
|
+ connection:send("HTTP/1.0 "..httpCode.." OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
|
|
|
+ connection:send('{"error":'..errorCode..', "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"'..action..'", "message":"'..message..'"}')
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+local function sendStatus(connection, pinConfig)
|
|
|
+ connection:send("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
|
|
|
+ connection:send('{"error":0, "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"status"'..', "status":"'..readDoorStatus(pinConfig)..'"}')
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+local function openDoor(connection, pinConfig)
|
|
|
+ -- errors if door is already open.
|
|
|
+ local doorStatus = readDoorStatus(pinConfig)
|
|
|
+ if doorStatus == 'open' then
|
|
|
+ return false
|
|
|
+ else
|
|
|
+ pushTheButton(connection, pinConfig)
|
|
|
+ return true
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+local function closeDoor(connection, pinConfig)
|
|
|
+ -- errors if door is already closed.
|
|
|
+ local doorStatus = readDoorStatus(pinConfig)
|
|
|
+ if doorStatus == 'closed' then
|
|
|
+ return false
|
|
|
+ else
|
|
|
+ pushTheButton(connection, pinConfig)
|
|
|
+ return true
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+return function (connection, req, args)
|
|
|
+
|
|
|
+ -- The values for pinConfig depend on how your Wemo D1 mini Pro is wired.
|
|
|
+ -- Adjust as needed.
|
|
|
+ pinConfig = {}
|
|
|
+ pinConfig["1"] = {}
|
|
|
+ pinConfig["1"]["door"] = 1
|
|
|
+ pinConfig["1"]["controlPin"] = 2
|
|
|
+ pinConfig["1"]["statusPin"] = 5
|
|
|
+ pinConfig["2"] = {}
|
|
|
+ pinConfig["2"]["door"] = 2
|
|
|
+ pinConfig["2"]["controlPin"] = 1
|
|
|
+ pinConfig["2"]["statusPin"] = 6
|
|
|
+
|
|
|
+ -- Make this work with both GET and POST methods.
|
|
|
+ -- In the POST case, we need to extract the arguments.
|
|
|
+ print("method is " .. req.method)
|
|
|
+ if req.method == "POST" then
|
|
|
+ local rd = req.getRequestData()
|
|
|
+ for name, value in pairs(rd) do
|
|
|
+ args[name] = value
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- validate door input
|
|
|
+
|
|
|
+ if args.door == nil then
|
|
|
+ sendResponse(connection, 400, -1, args.action, pinConfig[args.door], "No door specified")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if pinConfig[args.door] == nil then
|
|
|
+ sendResponse(connection, 400, -2, args.action, pinConfig[args.door], "Bad door specified")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ -- perform action
|
|
|
+
|
|
|
+ if args.action == "open" then
|
|
|
+ if(openDoor(connection, pinConfig[args.door])) then
|
|
|
+ sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door opened")
|
|
|
+ else
|
|
|
+ sendResponse(connection, 400, -3, args.action, pinConfig[args.door], "Door is already open")
|
|
|
+ end
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if args.action == "close" then
|
|
|
+ if(closeDoor(connection, pinConfig[args.door])) then
|
|
|
+ sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door closed")
|
|
|
+ else
|
|
|
+ sendResponse(connection, 400, -4, args.action, pinConfig[args.door], "Door is already closed")
|
|
|
+ end
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if args.action == "toggle" then
|
|
|
+ pushTheButton(connection, pinConfig[args.door])
|
|
|
+ sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Pushed the button")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if args.action == "status" then
|
|
|
+ sendStatus(connection, pinConfig[args.door])
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ -- everything else is error
|
|
|
+
|
|
|
+ sendResponse(connection, 400, -5, args.action, pinConfig[args.door], "Bad action")
|
|
|
+
|
|
|
+end
|