diff options
-rw-r--r-- | +wakeup.example.lua | 22 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | init.lua | 2 | ||||
-rw-r--r-- | run.lua | 105 | ||||
-rw-r--r-- | wakeup.lua | 86 |
5 files changed, 113 insertions, 104 deletions
diff --git a/+wakeup.example.lua b/+wakeup.example.lua new file mode 100644 index 0000000..e65c8a6 --- /dev/null +++ b/+wakeup.example.lua @@ -0,0 +1,22 @@ +--- Fill this file and save it on the device as "+wakeup.lua". +return { + --- MAC address of the device to wake up. Formatted as twelve hexadecimal characters separated or not, for example: + --- + --- "1a:2b:3c:4d:5e:6f" + --- "1a 2b 3c 4d 5e 6f" + --- "1a2b3c4d5e6f" + --- + --- These three are equivalent. + mac=nil, + --- Broadcast address to send the magic packet to. + addr="255.255.255.255", + --- Wake-on-LAN port. + port=9, + --- Not yet implemented time description of when to wake up the device. + when=nil, + --- Wi-Fi configuration same as passed to wifi.config(). It won't be used if left as nil. In this case user is + --- expected to manually configure and persist AP in the flash. + wifi=nil, + --- SNTP server address or table of addresses. See sntp.sync(). + sntp="time.google.com", +} @@ -1,2 +1,2 @@ -*.conf ++wakeup.lua .venv/ @@ -4,7 +4,7 @@ function init () wifi.setmode(wifi.NULLMODE, false) local l, r = pcall(require, "run") if not l then - print("run.lua not found") + print("Could not load run.lua:", r) return end RUN = r @@ -1,105 +1,6 @@ -local wakeup = { - port = 9, -} - - ---- Reads configuration file at [[path]] and parses a pair of MAC and broadcast addresses from it. Builds a Wake-on-LAN ---- magic packet and saves it for later use with :worker(). Returns true if no errors were encountered, otherwise a nil. -function wakeup:configure (path) - local fd = file.open(path) - if not fd then - print("Config file not found", path) - return - end - local line = fd:readline() - fd:close() - fd = nil - self.mac, self.addr = line:match("^(%S+)%s+(%S+)") - self.data = "" - for octet in self.mac:gmatch("%x%x") do - self.data = self.data .. string.char(tonumber(octet, 16)) - end - if #self.data ~= 6 then - print(string.format("Invalid MAC\t%q", self.mac)) - return - end - self.data = string.char(0xff):rep(6) .. self.data:rep(16) - return true -end - - -function wakeup:open () - self.udp = net.createUDPSocket() -end - - -function wakeup:close () - if self.udp then - self.udp:close() - end -end - - ---- Creates a new counter that has :up() method that returns true until it gets called N [[times]]. Then it returns only ---- false. -local -function new_counter (times) - return { - count = 0, - times = times, - up = function (self) - self.count = self.count + 1 - return self.count <= self.times - end, - } -end - - ---- Begins to send Wake-on-LAN magic packets to the broadcast [[.addr]] for selected [[.mac]]. Packets are sent several ---- [[times]] each separated by [[interval]]. Callback is called [[after]] all packets are sent. -function wakeup:sendout (interval, times, after) - self:open() - local count = new_counter(times) - return tmr.create():alarm(interval, tmr.ALARM_SEMI, function (timer) - if count:up() then - print("Waking up", self.mac, self.addr) - self.udp:send(self.port, self.addr, self.data) - timer:start() - else - timer:unregister() - self:close() - after() - end - end) -end - - ---- Sets up Wake Up to run when wifi is connected and configured in a network. Runs callbacks [[before]] send-outs, and ---- [[after]] they are done. -function wakeup:setup (before, after) - if not self:configure("wakeup.conf") then - error("Could not configure wakeup") - end - wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function () - before() - self:sendout(500, 5, after) - end) -end - +local wakeup = require("wakeup") return function () - gpio.write(4, gpio.HIGH) - gpio.mode(4, gpio.OUTPUT) - - tmr.create():alarm(1000, tmr.ALARM_SINGLE, function () - wakeup:setup( - function () gpio.write(4, gpio.LOW) end, - function () - wifi.setmode(wifi.NULLMODE, false) - gpio.write(4, gpio.HIGH) - end - ) - wifi.setmode(wifi.STATION, false) - wifi.sta.connect() - end) + wakeup:init() + tmr.create():alarm(1000, tmr.ALARM_SINGLE, function () wakeup:run() end) end diff --git a/wakeup.lua b/wakeup.lua new file mode 100644 index 0000000..9239fe1 --- /dev/null +++ b/wakeup.lua @@ -0,0 +1,86 @@ +local wakeup = require("+wakeup") + + +--- Builds and returns a Wake-on-LAN magic packet for [[mac]] address. +local +function magic_packet (mac) + local data = "" + for octet in mac:gmatch("%x%x") do + data = data .. string.char(tonumber(octet, 16)) + end + if #data ~= 6 then + error(string.format("Invalid MAC\t%q", mac), 2) + end + return string.char(0xff):rep(6) .. data:rep(16) +end + + +function wakeup:open () + self.udp = net.createUDPSocket() +end + + +function wakeup:close () + if self.udp then + self.udp:close() + end +end + + +--- Creates a new counter that has :up() method that returns true until it gets called N [[times]]. Then it returns only +--- false. +local +function new_counter (times) + return { + count = 0, + times = times, + up = function (self) + self.count = self.count + 1 + return self.count <= self.times + end, + } +end + + +--- Begins to send Wake-on-LAN magic packets to the broadcast [[.addr]] for selected [[.mac]]. Packets are sent several +--- [[times]] each separated by [[interval]]. Callback is called [[after]] all packets are sent. +function wakeup:sendout (interval, times, after) + self:open() + local count = new_counter(times) + return tmr.create():alarm(interval, tmr.ALARM_SEMI, function (timer) + if count:up() then + print("Waking up", self.mac, self.addr) + self.udp:send(self.port, self.addr, self.data) + timer:start() + else + timer:unregister() + self:close() + after() + end + end) +end + + +function wakeup:init () + gpio.write(4, gpio.HIGH) + gpio.mode(4, gpio.OUTPUT) + self.data = magic_packet(self.mac) + + wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function () + gpio.write(4, gpio.LOW) + + self:sendout(500, 5, function () + wifi.setmode(wifi.NULLMODE, false) + gpio.write(4, gpio.HIGH) + end) + end) +end + + +function wakeup:run () + wifi.setmode(wifi.STATION, false) + wifi.sta.connect() +end + + +return wakeup |