NodeMCU で web LED signal

infinite loop さんが [Crystal Signal Pi]([http://crystal-signal.com/]) と言う製品を発売されました。Raspberry Pi を使った RGB color LED 信号灯です。これって NodeMCU で間に合うよね、と貧乏性なワタシです。当初は MicroPython で考えてみたものの、socket の非同期動作ができない(?)と言うことで不慣れな Lua で書いています。結局 socket の非同期処理とは関係なく、コマンドのパラメータをグローバルに置いてタイマで LED の点滅制御をしています。排他、それ何？みたいな雑なコードですみません。無駄に 100ms 毎にタイマ割込みを入れているのは PWM による LED の明るさ制御も自前でやってみようとした名残りです。

http://example.com//ctrl/?red=0&green=64&blue=255&period=500&rpt=300 のようににして使います。このパラメータ形式は当初の Crystal Sigal Pi の仕様にならったつもりなのですが、現行の仕様とは異なります。[YouTube](https://www.youtube.com/watch?v=48F32NLfHhA)にバラックで動作中の様子を置きました。

* [MicroPython で web LED signal](http://qiita.com/n24bass/items/86e7669f9ac5df6cca86) - MicroPython でも試してみました。(2017.01.27 追記)

```lua:websignal.lua

params = {red=0, green=0, blue=0, period=500, rpt=0}
ptimer = 0
power = false

pwm.setup(1, 500, 0) -- LED RED
pwm.setup(2, 500, 0) -- LED GREEN
pwm.setup(3, 500, 0) -- LED BLUE
pwm.start(1)
pwm.start(2)
pwm.start(3)

function split(str, pattern, n)
   if not string.find(str, pattern) then
	  return { str }
   end
   local result = {}
   local fpat = "(.-)" .. pattern
   local insertCnt = 1
   local lastEnd = 1
   local s, e, cap = string.find(str, fpat, 1)
   while s do
	  if insertCnt > n and n > 0 then
		 break
	  end
	  if s ~= 1 or cap ~= "" then
		 table.insert(result, cap)
	  end
	  insertCnt = insertCnt + 1
	  lastEnd = e + 1
	  s, e, cap = string.find(str, fpat, lastEnd)
   end
   if lastEnd <= #str then
	  cap = string.sub(str, lastEnd)
	  table.insert(result, cap)
   end
   return result
end

function pp()
   local k, v
   local s = ""
   for k,v in pairs(params) do s = s.."<br>\n"..k..":"..v end
   return s
end

function getparams(s)
   local t = split(s, "\n", 2) -- get first line
   t = split(t[1], " ", 4) -- get 'GET' argument
   -- t[1]: s = "/ctrl/?red=0&green=64&blue=255&period=500&rpt=300"
   t = split(t[2], "/", 2)
   if not (#t > 1 and t[1] == 'ctrl' and string.sub(t[2], 1, 1) == '?') then
	  return false
   end
   tt = split(string.sub(t[2], 2), "&", 10)
   local k, v
   for k, v in pairs(tt) do
	  local a = split(v, "=", 2)
	  local k = a[1]
	  local v = a[2]
	  if params[k] ~= nil then
		 v = tonumber(v)
		 if v < 0 then v = 0 end
		 if k ~= 'period' and k ~= 'rpt' and v > 1023 then v = 1023 end
		 params[k] = v
	  end
   end
   print(pp())
   tmfunc()

   return true
end

function led(r, g, b)
   print("R:"..r..",G:"..g..",B:"..b)
   pwm.setduty(1, r)
   pwm.setduty(2, g)
   pwm.setduty(3, b)
end

function tmfunc()
   local npower = power
   if params['rpt'] > 0 then
	  if params['period'] == 0 then
		 npower = true
	  else
		 if ptimer > 0 then
			ptimer = ptimer - 1
		 else
			npower = not power
			if not npower then params['rpt'] = params['rpt'] - 1 end
			ptimer = params['period'] / 200
		 end
	  end
   else
	  npower = off
   end

   -- LED on or off by params['power']
   if power ~= npower then
	  power = npower
	  if power then led(params['red'], params['green'], params['blue']) else led(0, 0, 0) end
   end
end

--

wifi.setmode(wifi.STATION)
wifi.sta.config("SSID", "PASSWORD")
wifi.sta.autoconnect(1)

tmr.register(0, 100, tmr.ALARM_AUTO, function() tmfunc() end)
tmr.start(0)

srv=net.createServer(net.TCP)
srv:listen(80,
		   function(conn) 
			  conn:on("receive",
					  function(conn,payload) 
						 print(payload)
						 local r = getparams(payload)
						 if r then
							conn:send("<h1>ESP8266 - Signal NodeMCU working!</h1><p>"..pp().."</p>")
						 else
							conn:send("<h1>ESP8266 - Server is working!</h1><p>"..pp().."</p>")
						 end
						 conn:close()
			  end)
end)
```