[fluksod] skeleton implementation of message processing on spidev bus

This commit is contained in:
Bart Van Der Meerssche 2011-01-09 12:00:50 +01:00
parent 221636f5cf
commit f3516415bd
6 changed files with 816 additions and 67 deletions

View File

@ -3,7 +3,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=flukso PKG_NAME:=flukso
PKG_VERSION:=2.0 PKG_VERSION:=2.0-alpha
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
@ -15,6 +15,8 @@ define Package/flukso
CATEGORY:=Utilities CATEGORY:=Utilities
DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1 DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1
TITLE:=Flukso - community metering TITLE:=Flukso - community metering
URL:=http://www.flukso.net
MAINTAINER:=Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
endef endef
define Package/flukso/description define Package/flukso/description
@ -32,10 +34,12 @@ define Package/flukso/install
$(INSTALL_DATA) $(PKG_BUILD_DIR)/dbg.lua $(1)/usr/lib/lua/ $(INSTALL_DATA) $(PKG_BUILD_DIR)/dbg.lua $(1)/usr/lib/lua/
$(INSTALL_DIR) $(1)/usr/lib/lua/rrd $(INSTALL_DIR) $(1)/usr/lib/lua/rrd
$(INSTALL_DATA) $(PKG_BUILD_DIR)/rrd/*.lua $(1)/usr/lib/lua/rrd/ $(INSTALL_DATA) $(PKG_BUILD_DIR)/rrd/*.lua $(1)/usr/lib/lua/rrd/
$(INSTALL_DIR) $(1)/usr/lib/lua/flukso
$(INSTALL_DATA) $(PKG_BUILD_DIR)/flukso/*.lua $(1)/usr/lib/lua/flukso/
$(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/luad $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/luad $(1)/usr/sbin/
$(LN) /usr/sbin/luad $(1)/usr/sbin/spid $(LN) /usr/sbin/luad $(1)/usr/sbin/fluksod
$(INSTALL_BIN) $(PKG_BUILD_DIR)/spi/spid.lua $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/fluksod.lua $(1)/usr/sbin/
endef endef
$(eval $(call BuildPackage,flukso)) $(eval $(call BuildPackage,flukso))

View File

@ -0,0 +1,517 @@
--[[
/*
* Copyright (c) 2007 Tim Kelly/Dialectronics
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
--]]
module(..., package.seeall);
local hex2bin = {
["0"] = "0000",
["1"] = "0001",
["2"] = "0010",
["3"] = "0011",
["4"] = "0100",
["5"] = "0101",
["6"] = "0110",
["7"] = "0111",
["8"] = "1000",
["9"] = "1001",
["a"] = "1010",
["b"] = "1011",
["c"] = "1100",
["d"] = "1101",
["e"] = "1110",
["f"] = "1111"
}
local bin2hex = {
["0000"] = "0",
["0001"] = "1",
["0010"] = "2",
["0011"] = "3",
["0100"] = "4",
["0101"] = "5",
["0110"] = "6",
["0111"] = "7",
["1000"] = "8",
["1001"] = "9",
["1010"] = "A",
["1011"] = "B",
["1100"] = "C",
["1101"] = "D",
["1110"] = "E",
["1111"] = "F"
}
--[[
local dec2hex = {
["0"] = "0",
["1"] = "1",
["2"] = "2",
["3"] = "3",
["4"] = "4",
["5"] = "5",
["6"] = "6",
["7"] = "7",
["8"] = "8",
["9"] = "9",
["10"] = "A",
["11"] = "B",
["12"] = "C",
["13"] = "D",
["14"] = "E",
["15"] = "F"
}
--]]
-- These functions are big-endian and take up to 32 bits
-- Hex2Bin
-- Bin2Hex
-- Hex2Dec
-- Dec2Hex
-- Bin2Dec
-- Dec2Bin
function Hex2Bin(s)
-- s -> hexadecimal string
local ret = ""
local i = 0
for i in string.gfind(s, ".") do
i = string.lower(i)
ret = ret..hex2bin[i]
end
return ret
end
function Bin2Hex(s)
-- s -> binary string
local l = 0
local h = ""
local b = ""
local rem
l = string.len(s)
rem = l % 4
l = l-1
h = ""
-- need to prepend zeros to eliminate mod 4
if (rem > 0) then
s = string.rep("0", 4 - rem)..s
end
for i = 1, l, 4 do
b = string.sub(s, i, i+3)
h = h..bin2hex[b]
end
return h
end
function Bin2Dec(s)
-- s -> binary string
local num = 0
local ex = string.len(s) - 1
local l = 0
l = ex + 1
for i = 1, l do
b = string.sub(s, i, i)
if b == "1" then
num = num + 2^ex
end
ex = ex - 1
end
return string.format("%u", num)
end
function Dec2Bin(s, num)
-- s -> Base10 string
-- num -> string length to extend to
local n
if (num == nil) then
n = 0
else
n = num
end
s = string.format("%x", s)
s = Hex2Bin(s)
while string.len(s) < n do
s = "0"..s
end
return s
end
function Hex2Dec(s)
-- s -> hexadecimal string
local s = Hex2Bin(s)
return Bin2Dec(s)
end
function Dec2Hex(s)
-- s -> Base10 string
s = string.format("%x", s)
return s
end
-- These functions are big-endian and will extend to 32 bits
-- BMAnd
-- BMNAnd
-- BMOr
-- BMXOr
-- BMNot
function BMAnd(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == cm then
if cv == "1" then
s = s.."1"
else
s = s.."0"
end
else
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMNAnd(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == cm then
if cv == "1" then
s = s.."0"
else
s = s.."1"
end
else
s = s.."1"
end
end
return Bin2Hex(s)
end
function BMOr(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == "1" then
s = s.."1"
elseif cm == "1" then
s = s.."1"
else
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMXOr(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == "1" then
if cm == "0" then
s = s.."1"
else
s = s.."0"
end
elseif cm == "1" then
if cv == "0" then
s = s.."1"
else
s = s.."0"
end
else
-- cv and cm == "0"
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMNot(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cm == "1" then
if cv == "1" then
-- turn off
s = s.."0"
else
-- turn on
s = s.."1"
end
else
-- leave untouched
s = s..cv
end
end
return Bin2Hex(s)
end
-- these functions shift right and left, adding zeros to lost or gained bits
-- returned values are 32 bits long
-- BShRight(v, nb)
-- BShLeft(v, nb)
function BShRight(v, nb)
-- v -> hexstring value to be shifted
-- nb -> number of bits to shift to the right
-- s -> binary string of v
local s = Hex2Bin(v)
while (string.len(s) < 32) do
s = "0000"..s
end
s = string.sub(s, 1, 32 - nb)
while (string.len(s) < 32) do
s = "0"..s
end
return Bin2Hex(s)
end
function BShLeft(v, nb)
-- v -> hexstring value to be shifted
-- nb -> number of bits to shift to the right
-- s -> binary string of v
local s = Hex2Bin(v)
while (string.len(s) < 32) do
s = "0000"..s
end
s = string.sub(s, nb + 1, 32)
while (string.len(s) < 32) do
s = s.."0"
end
return Bin2Hex(s)
end

View File

@ -0,0 +1,210 @@
--[[
spi.lua - Lua 5.1 flukso module for spidev message processing
Copyright (C) 2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]--
local nixio = require 'nixio'
local BinDecHex = require 'flukso.BinDecHex'
local os, table, string =
os, table, string
local getfenv, setmetatable, tonumber =
getfenv, setmetatable, tonumber
module (...)
local modenv = getfenv()
-- private
local function dow_crc(sequence, crc)
crc = crc or 0x00
local r1 = { 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83,
0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41 }
local r2 = { 0x00, 0x9d, 0x23, 0xbe, 0x46, 0xdb, 0x65, 0xf8,
0x8c, 0x11, 0xaf, 0x32, 0xca, 0x57, 0xe9, 0x74 }
local first_char = sequence:sub(1, 1)
if first_char ~= '' then
local i = nixio.bit.band(nixio.bit.bxor(first_char:byte(), crc), 0xff)
crc = nixio.bit.bxor(r1[nixio.bit.band(i, 0xf) + 1], r2[nixio.bit.rshift(i, 4) + 1])
return dow_crc(sequence:sub(2, -1), crc)
else
return nixio.bin.hexlify(string.char(crc))
end
end
--- Create a new spi message object.
--
-- Attributes:
-- { to = ctrl | delta | uart
-- body = <string>
-- parsed = { <command>, <arg1>, <arg2>, ... }
-- encoded = <string>
-- received = { raw = <string>, l = <string>, crc = <string>, u = <string> }
-- decoded = { l = ..., u = ... }
-- reply = <string>
-- }
--
-- @param to The message destination: ctrl | delta | uart.
-- @param body The message body.
-- @return An spi message object.
function new_msg(to, body)
return setmetatable({ to = to, body = body }, { __index = modenv })
end
function parse(msg)
msg.parsed = {}
msg.parsed[1] = msg.body:match('^%l%l')
for arg in msg.body:gmatch('%d+') do
msg.parsed[#msg.parsed + 1] = arg
end
--> TODO: more detailed command syntax checking
end
function encode(msg)
if msg.to == 'uart' then
msg.encoded = nixio.bin.hexlify(msg.body)
return
end
if msg.parsed[1] == 'gd' then
msg.encoded = msg.parsed[1]
elseif msg.parsed[1] == 'gv' then
elseif msg.parsed[1] == 'gp' then
elseif msg.parsed[1] == 'gc' then
elseif msg.parsed[1] == 'gm' then
elseif msg.parsed[1] == 'gw' then
elseif msg.parsed[1] == 'gb' then
elseif msg.parsed[1] == 'sv' then
elseif msg.parsed[1] == 'sp' then
elseif msg.parsed[1] == 'sc' then
elseif msg.parsed[1] == 'sm' then
elseif msg.parsed[1] == 'sw' then
elseif msg.parsed[1] == 'sb' then
elseif msg.parsed[1] == 'ct' then
else
end
--> TODO msg.encoded = msg.encoded .. dow_crc(msg.encoded)
end
function tx(msg, cdev)
if msg.to == 'ctrl' or msg.to == 'delta' then
cdev:write('l' .. msg.encoded .. '.\0\0')
elseif msg.to == 'uart' then
cdev:write('u' .. msg.encoded .. '\0\0')
end
end
function rx(msg, cdev)
local sequence = {}
for char in function() return cdev:read(1) end do
if char ~= '\0' then
table.insert(sequence, char)
else
-- one more read to let the AVR send a second 0x00
-- after which the AVR's state machine toggles to read mode
cdev:read(1)
break
end
end
msg.received = {}
msg.received.raw = table.concat(sequence)
msg.received.l, msg.received.u = msg.received.raw:match('^l(%w*)%.?u(%w*)%.?$')
if msg.received.l ~= '' and msg.received.l:sub(1, 2) == msg.parsed[1] then
msg.received.crc = msg.received.l:sub(-2, -1)
msg.received.l = msg.received.l:sub(1, -3)
if dow_crc(msg.received.l) ~= msg.received.crc then
--> TODO implement crc error counter
msg.received.l = ''
end
end
end
function decode(msg)
msg.decoded = {}
if msg.received.u ~= '' then
msg.decoded.u = nixio.bin.unhexlify(msg.received.u)
end
if msg.received.l ~= '' then
msg.decoded.largs = msg.received.l:sub(3, -1)
if msg.parsed[1] == 'gd' then
for i = 1, msg.decoded.largs:len() / 18 do
msg.decoded[(i-1)*3 + 1] =
tonumber(BinDecHex.Hex2Dec(msg.decoded.largs:sub((i-1)*18 + 1, (i-1)*18 + 2)))
msg.decoded[(i-1)*3 + 2] =
tonumber(BinDecHex.Hex2Dec(msg.decoded.largs:sub((i-1)*18 + 3, (i-1)*18 + 10)))
msg.decoded[(i-1)*3 + 3] =
tonumber(BinDecHex.Hex2Dec(msg.decoded.largs:sub((i-1)*18 + 11, (i-1)*18 + 18)))
end
elseif msg.parsed[1] == 'gv' then
elseif msg.parsed[1] == 'gp' then
elseif msg.parsed[1] == 'gc' then
elseif msg.parsed[1] == 'gm' then
elseif msg.parsed[1] == 'gw' then
elseif msg.parsed[1] == 'gb' then
elseif msg.parsed[1] == 'sv' then
elseif msg.parsed[1] == 'sp' then
elseif msg.parsed[1] == 'sc' then
elseif msg.parsed[1] == 'sm' then
elseif msg.parsed[1] == 'sw' then
elseif msg.parsed[1] == 'sb' then
elseif msg.parsed[1] == 'ct' then
end
end
end

View File

@ -0,0 +1,78 @@
#! /usr/bin/env lua
--[[
fluksod.lua - Lua part of fluksod
Copyright (C) 2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]--
require 'dbg'
require 'nixio'
require 'nixio.fs'
local spi = require 'flukso.spi'
DAEMON = os.getenv('DAEMON') or 'fluksod'
DAEMON_PATH = os.getenv('DAEMON_PATH') or '/var/run/' .. DAEMON
O_RDWR_NONBLOCK = nixio.open_flags('rdwr', 'nonblock')
POLLIN = nixio.poll_flags('in')
function mkfifos(input)
local path = string.format('%s/%s/', DAEMON_PATH, input)
nixio.fs.mkdirr(path)
nixio.fs.unlink(path .. 'in') --> clean up mess from previous run
nixio.fs.unlink(path .. 'out') --> idem
nixio.fs.mkfifo(path .. 'in', '644')
nixio.fs.mkfifo(path .. 'out', '644')
local fdin = nixio.open(path .. 'in', O_RDWR_NONBLOCK)
local fdout = nixio.open(path .. 'out', O_RDWR_NONBLOCK)
return { fd = fdin, --> need this entry for nixio.poll
fdin = fdin,
fdout = fdout,
events = POLLIN,
revents = 0,
line = fdin:linesource() }
end
local uart = mkfifos('uart')
local ctrl = mkfifos('ctrl')
local delta = mkfifos('delta')
local fds = { uart, ctrl, delta }
local spidev = nixio.open('/dev/spidev0.0', O_RDWR_NONBLOCK)
while true do
if nixio.poll(fds, -1) then
if delta.revents == POLLIN then
--> TODO flush the delta fd after each POLLIN
local msg = spi.new_msg('delta', delta.line())
msg:parse()
msg:encode()
msg:tx(spidev)
nixio.nanosleep(0, 10e7) --> 10ms
msg:rx(spidev)
msg:decode()
--> dbg.vardump(msg)
delta.fdout:write(msg.parsed[1] .. ' ' .. table.concat(msg.decoded, ' ') .. '\n')
end
end
end

View File

@ -1,60 +0,0 @@
#! /usr/bin/env lua
--[[
spid.lua - Lua part of the spi daemon
Copyright (C) 2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]--
require 'nixio.fs'
ctrl_path = os.getenv('DAEMON_PATH') .. '/ctrl/'
nixio.fs.mkdirr(ctrl_path)
nixio.fs.unlink(ctrl_path .. 'in')
nixio.fs.unlink(ctrl_path .. 'out')
nixio.fs.mkfifo(ctrl_path .. 'in', '644')
nixio.fs.mkfifo(ctrl_path .. 'out', '644')
rdwr_nonblock = nixio.open_flags('rdwr', 'nonblock')
ctrl_fd_in = nixio.open(ctrl_path .. 'in', rdwr_nonblock)
ctrl_fd_out = nixio.open(ctrl_path .. 'out', rdwr_nonblock)
pfin = nixio.poll_flags('in')
local fd = {fd = ctrl_fd_in,
events = pfin,
revents = 0}
local fds = {fd}
ctrl_line = ctrl_fd_in:linesource()
while true do
if nixio.poll(fds, 1000) then
ctrl_fd_out:write((ctrl_line() or 'nil!') .. '\n')
else
ctrl_fd_out:write('timeout\n')
end
end
nixio.fs.unlink(ctrl_path .. 'in')
nixio.fs.unlink(ctrl_path .. 'out')