From d09fe3983d3db9347b7e7860c19fafe8c6460947 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Fri, 16 Dec 2016 02:13:27 -0500 Subject: [PATCH] lamp: linux wrappers for xpm and morse renderers This uses lua-cqueues to provide an implementation of the lamp renderer as a kind of shell pipeline tool. I am sure things could be improved, but this is a good start and lets me carry a virtual lamp on my desktop as well as a slightly different piece of kit on my physical desk. --- linux-draw-morse.lua | 34 ++++++++++++++++++ linux-draw-xpm.lua | 81 +++++++++++++++++++++++++++++++++++++++++++ linux-draw.lua | 82 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 linux-draw-morse.lua create mode 100644 linux-draw-xpm.lua create mode 100644 linux-draw.lua diff --git a/linux-draw-morse.lua b/linux-draw-morse.lua new file mode 100644 index 0000000..87e24b4 --- /dev/null +++ b/linux-draw-morse.lua @@ -0,0 +1,34 @@ +package.loaded["morse"] = dofile("morse/morse.lua") + +local dit = 500 -- milliseconds + +function loaddrawfn(name) + local m = (require"morse")(name) + printerr(name) + return function (t,_,g,r,b) + local function onf() + print(string.format("%x %x %x",r,g,b)) + end + local function offf() print("0 0 0") end + + local function morsecb(dur,on) + if on then onf() else offf() end + t:register(dur*250,tmr.ALARM_SINGLE,function() + local did = m(morsecb) + if not did then + offf() -- off at end + -- t:register(1000,tmr.ALARM_SINGLE,function() (require"morse")(name)(morsecb) end) -- cyclic behavior + t:register(dit,tmr.ALARM_SINGLE,function() onf() end) -- solid at end + end + end) + end + -- begin by being solid on for 10 dit times, then off for 4, then run + -- morse sequence + onf() + t:register(10*dit,tmr.ALARM_SINGLE, function() + offf() ; t:register(4*dit,tmr.ALARM_SINGLE,function() m(morsecb) end) + end) + end +end + +function doremotedraw() end diff --git a/linux-draw-xpm.lua b/linux-draw-xpm.lua new file mode 100644 index 0000000..61e7808 --- /dev/null +++ b/linux-draw-xpm.lua @@ -0,0 +1,81 @@ +-- emulate enough framebuffer functionality +remotefb.length = 32 +function remotefb.set(self,ix,a1,a2,a3) + -- printerr(ix,a1,a2,a3) + if ix > self.length then return end + if type(a1) == 'string' and a2 == nil and a3 == nil then + self[ix] = a1 + else + self[ix] = string.char(math.floor(a1),math.floor(a2),math.floor(a3)) + end +end +function remotefb.size(self) return self.length end +function remotefb.fade(self,factor,out) + local ix, f + if out + then f = function(c) return string.char(string.byte(c,1) / factor) end + else f = function(c) return string.char(string.byte(c,1) * factor) end + end + for ix = 1,self.length do self[ix] = self[ix]:gsub(".", f) end +end +function remotefb.fill(self,...) + local i + for i = 1,self.length do self:set(i,...) end +end + +local function drawfailsafe(t,fb,g,r,b) end +function loaddrawfn(name) + local f = loadfile (string.format("examples/lamp/draw-%s.lua",name)) + local fn = f and f() + if fn + then return function(t,fb,g,r,b) fn(t,fb,g,r,b) end + else return drawfailsafe + end +end + +local outfn = "/run/user/1000/lamp-purple.xpm" +local drawstr = "abcdefghijklmnopqrstuvwxzy123456" +local drawstr2 = "" +local function computedrawstr2() + local ix = 0, r, c + for r = 1,4 do + local line = "" + for c = 1,8 do + ix = ix + 1 + local h = drawstr:sub(ix,ix) + if c ~= 1 then line = line .. "0" end + line = line .. h .. h + end + line = line .. "\n" + if r ~= 1 then drawstr2 = drawstr2 .. "00000000000000000000000\n" end + drawstr2 = drawstr2 .. line + drawstr2 = drawstr2 .. line + end +end +computedrawstr2() +local function pixelval(byteval) + +end +function dodraw() + local f = io.open(outfn,"w+") + f:write("! XPM2\n23 11 33 1\n") -- header + f:write("0 c #000000\n") + local ix = 0, r, c + for r = 1,4 do + for c = 1,8 do + ix = ix + 1 + f:write(string.format("%s c #%02x%02x%02x\n", + drawstr:sub(ix,ix), + string.byte(remotefb[ix],2)*16, -- r + string.byte(remotefb[ix],1)*16, -- g + string.byte(remotefb[ix],3)*16 -- b + )) + end + end + f:write(drawstr2) + f:close() + -- io.stderr:write("draw\n") + io.write("\n\n") -- XXX? WTF? + io.flush() +end +doremotedraw = dodraw diff --git a/linux-draw.lua b/linux-draw.lua new file mode 100644 index 0000000..24fcabe --- /dev/null +++ b/linux-draw.lua @@ -0,0 +1,82 @@ +-- Expects to be fed a series of draw commands on stdin and a worker module +-- lua file (e.g. ...-xpm.lua or ...-morse.lua) as argv[1]. +-- +-- This wraps around lamp-remote.lua for all the parsing logic (ain't code +-- reuse great?) and expects the worker modules to provide "enough" of the +-- nodemcu environment for whatever else they load. e.g. -xpm provides a +-- full frame buffer emulation and loads the various draw-*.lua modules just +-- like the hardware, while -morse does nothing of the sort. + +cq = require "cqueues" +local cqs = require "cqueues.signal" +cqc = cq.new() +package.loaded["fifo"] = dofile("fifo/fifo.lua") -- ick, but hey, it works! + +function printerr(...) + local s = "", i, v + for i,v in ipairs{...} do s = s .. tostring(v) .. "\t" end + s = s .. "\n" + io.stderr:write(s) +end + +-- Emulate tq with facilities available thanks to cqueues +tq = dofile("tq/tq.lua")(nil) +tq.now = function() return cq.monotime() * 1000000 end +tq.arm = function(self,fn,t) cqc:wrap(function() cq.poll(t/1000) ; fn() end) end + +-- how backwards is this!? We are using tq as faked above for tmr support +-- since it's not obvious to me how to remove pending events in cqueues. +tmr = {} +tmr.ALARM_AUTO = 0 +function tmr.unregister(self) + if self.tqe then tq:dequeue(self.tqe); self.tqe = nil end +end +function tmr.register(self,period,mode,fn) + tmr.unregister(self) + + local fnwrap + if mode == tmr.ALARM_AUTO then + -- persist by re-registering after fire + fnwrap = function() tmr.register(self,period,mode,fn); fn() end + else + fnwrap = fn + end + + self.tqe = tq:queue(period,fnwrap) +end + +remotetmr = {} +remotetmr.tqe = nil +remotetmr.register = tmr.register +remotetmr.unregister = tmr.unregister + +remotefb = {} + +if arg[1] then dofile(arg[1]) else print("You probably meant to give a filename"); os.exit(1) end + +cqc:wrap(function() + while true do + cq.poll({ pollfd = 0, events = 'r' }) + local line = io.read() -- XXX :( + if line == nil or line == "" then return end + dofile("examples/lamp/lamp-remote.lua")(line) + end +end) +io.stdout:setvbuf("no") + +cqc:wrap(function() + cq.poll(cqs.listen(cqs.SIGINT, cqs.SIGTERM, cqs.SIGQUIT)) + print("exit") + os.exit() +end) +cqs.block(cqs.SIGINT) +cqs.block(cqs.SIGTERM) +cqs.block(cqs.SIGQUIT) + +cqc:wrap(function() + cq.poll(cqs.listen(cqs.SIGHUP)) + print("hup") +end) +cqs.block(cqs.SIGHUP) + +assert(cqc:loop()) -- 2.50.1