]> hydra-www.ietfng.org Git - acmetensortoys-esp-lua_lamp/commitdiff
lamp: linux wrappers for xpm and morse renderers
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 16 Dec 2016 07:13:27 +0000 (02:13 -0500)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 16 Dec 2016 16:44:01 +0000 (11:44 -0500)
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 [new file with mode: 0644]
linux-draw-xpm.lua [new file with mode: 0644]
linux-draw.lua [new file with mode: 0644]

diff --git a/linux-draw-morse.lua b/linux-draw-morse.lua
new file mode 100644 (file)
index 0000000..87e24b4
--- /dev/null
@@ -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 (file)
index 0000000..61e7808
--- /dev/null
@@ -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 (file)
index 0000000..24fcabe
--- /dev/null
@@ -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())