garage_door.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. -- garage_door_open.lua
  2. -- Part of nodemcu-httpserver, example.
  3. -- Author: Marcos Kirsch
  4. --[[
  5. This example assumed you have a Wemos D1 Pro to control a two-door garage.
  6. For each garage door, a Wemos relay shield is used to simulate a button (connect relay in
  7. parallel with the actual physical button) and a reed switch is used in order to know
  8. whether a door is currently open or closed (install switch so that it is in the closed
  9. position when your garage door is closed).
  10. You can configure which GPIO pins you use for each function by modifying variable
  11. pinConfig below.
  12. ]]--
  13. local function pushTheButton(connection, pinConfig)
  14. -- push the button!
  15. gpio.write(pinConfig["controlPin"], gpio.HIGH)
  16. gpio.mode(pinConfig["controlPin"], gpio.OUTPUT, gpio.FLOAT)
  17. tmr.delay(300000) -- in microseconds
  18. gpio.mode(pinConfig["controlPin"], gpio.INPUT, gpio.FLOAT)
  19. gpio.write(pinConfig["controlPin"], gpio.LOW)
  20. end
  21. local function readDoorStatus(pinConfig)
  22. -- When the garage door is closed, the reed relay closes, grounding the pin and causing us to read low (0).
  23. -- When the garage door is open, the reed relay is open, so due to pullup we read high (1).
  24. gpio.write(pinConfig["statusPin"], gpio.HIGH)
  25. gpio.mode(pinConfig["statusPin"], gpio.INPUT, gpio.PULLUP)
  26. if gpio.read(pinConfig["statusPin"]) == 1 then return 'open' else return 'closed' end
  27. end
  28. local function sendResponse(connection, httpCode, errorCode, action, pinConfig, message)
  29. -- Handle nil inputs
  30. if action == nil then action = '' end
  31. if pinConfig == nil then
  32. pinConfig = {}
  33. pinConfig["door"] = 0
  34. pinConfig["controlPin"] = 0
  35. pinConfig["statusPin"] = 0
  36. end
  37. if message == nil then message = '' end
  38. connection:send("HTTP/1.0 "..httpCode.." OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
  39. connection:send('{"error":'..errorCode..', "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"'..action..'", "message":"'..message..'"}')
  40. end
  41. local function sendStatus(connection, pinConfig)
  42. connection:send("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
  43. connection:send('{"error":0, "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"status"'..', "status":"'..readDoorStatus(pinConfig)..'"}')
  44. end
  45. local function openDoor(connection, pinConfig)
  46. -- errors if door is already open.
  47. local doorStatus = readDoorStatus(pinConfig)
  48. if doorStatus == 'open' then
  49. return false
  50. else
  51. pushTheButton(connection, pinConfig)
  52. return true
  53. end
  54. end
  55. local function closeDoor(connection, pinConfig)
  56. -- errors if door is already closed.
  57. local doorStatus = readDoorStatus(pinConfig)
  58. if doorStatus == 'closed' then
  59. return false
  60. else
  61. pushTheButton(connection, pinConfig)
  62. return true
  63. end
  64. end
  65. return function (connection, req, args)
  66. -- The values for pinConfig depend on how your Wemo D1 mini Pro is wired.
  67. -- Adjust as needed.
  68. pinConfig = {}
  69. pinConfig["1"] = {}
  70. pinConfig["1"]["door"] = 1
  71. pinConfig["1"]["controlPin"] = 2
  72. pinConfig["1"]["statusPin"] = 5
  73. pinConfig["2"] = {}
  74. pinConfig["2"]["door"] = 2
  75. pinConfig["2"]["controlPin"] = 1
  76. pinConfig["2"]["statusPin"] = 6
  77. -- Make this work with both GET and POST methods.
  78. -- In the POST case, we need to extract the arguments.
  79. print("method is " .. req.method)
  80. if req.method == "POST" then
  81. local rd = req.getRequestData()
  82. for name, value in pairs(rd) do
  83. args[name] = value
  84. end
  85. end
  86. -- validate door input
  87. if args.door == nil then
  88. sendResponse(connection, 400, -1, args.action, pinConfig[args.door], "No door specified")
  89. return
  90. end
  91. if pinConfig[args.door] == nil then
  92. sendResponse(connection, 400, -2, args.action, pinConfig[args.door], "Bad door specified")
  93. return
  94. end
  95. -- perform action
  96. if args.action == "open" then
  97. if(openDoor(connection, pinConfig[args.door])) then
  98. sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door opened")
  99. else
  100. sendResponse(connection, 400, -3, args.action, pinConfig[args.door], "Door is already open")
  101. end
  102. return
  103. end
  104. if args.action == "close" then
  105. if(closeDoor(connection, pinConfig[args.door])) then
  106. sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door closed")
  107. else
  108. sendResponse(connection, 400, -4, args.action, pinConfig[args.door], "Door is already closed")
  109. end
  110. return
  111. end
  112. if args.action == "toggle" then
  113. pushTheButton(connection, pinConfig[args.door])
  114. sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Pushed the button")
  115. return
  116. end
  117. if args.action == "status" then
  118. sendStatus(connection, pinConfig[args.door])
  119. return
  120. end
  121. -- everything else is error
  122. sendResponse(connection, 400, -5, args.action, pinConfig[args.door], "Bad action")
  123. end