diff --git a/mote/v2/openwrt/package/flukso/Makefile b/mote/v2/openwrt/package/flukso/Makefile index 2b9b99e..f800d2a 100644 --- a/mote/v2/openwrt/package/flukso/Makefile +++ b/mote/v2/openwrt/package/flukso/Makefile @@ -3,7 +3,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=flukso -PKG_VERSION:=2.0 +PKG_VERSION:=2.0-alpha PKG_RELEASE:=1 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) @@ -11,10 +11,12 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) include $(INCLUDE_DIR)/package.mk define Package/flukso - SECTION:=utils - CATEGORY:=Utilities - DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1 - TITLE:=Flukso - community metering + SECTION:=utils + CATEGORY:=Utilities + DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1 + TITLE:=Flukso - community metering + URL:=http://www.flukso.net + MAINTAINER:=Bart Van Der Meerssche endef define Package/flukso/description @@ -32,10 +34,12 @@ define Package/flukso/install $(INSTALL_DATA) $(PKG_BUILD_DIR)/dbg.lua $(1)/usr/lib/lua/ $(INSTALL_DIR) $(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_BIN) $(PKG_BUILD_DIR)/luad $(1)/usr/sbin/ - $(LN) /usr/sbin/luad $(1)/usr/sbin/spid - $(INSTALL_BIN) $(PKG_BUILD_DIR)/spi/spid.lua $(1)/usr/sbin/ + $(LN) /usr/sbin/luad $(1)/usr/sbin/fluksod + $(INSTALL_BIN) $(PKG_BUILD_DIR)/fluksod.lua $(1)/usr/sbin/ endef $(eval $(call BuildPackage,flukso)) diff --git a/mote/v2/openwrt/package/flukso/luasrc/flukso/BinDecHex.lua b/mote/v2/openwrt/package/flukso/luasrc/flukso/BinDecHex.lua new file mode 100644 index 0000000..3483bba --- /dev/null +++ b/mote/v2/openwrt/package/flukso/luasrc/flukso/BinDecHex.lua @@ -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 diff --git a/mote/v2/openwrt/package/flukso/luasrc/flukso/spi.lua b/mote/v2/openwrt/package/flukso/luasrc/flukso/spi.lua new file mode 100644 index 0000000..15da940 --- /dev/null +++ b/mote/v2/openwrt/package/flukso/luasrc/flukso/spi.lua @@ -0,0 +1,210 @@ +--[[ + + spi.lua - Lua 5.1 flukso module for spidev message processing + + Copyright (C) 2011 Bart Van Der Meerssche + + 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 . + +]]-- + +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 = +-- parsed = { , , , ... } +-- encoded = +-- received = { raw = , l = , crc = , u = } +-- decoded = { l = ..., u = ... } +-- reply = +-- } +-- +-- @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 diff --git a/mote/v2/openwrt/package/flukso/luasrc/fluksod.lua b/mote/v2/openwrt/package/flukso/luasrc/fluksod.lua new file mode 100755 index 0000000..c81fb46 --- /dev/null +++ b/mote/v2/openwrt/package/flukso/luasrc/fluksod.lua @@ -0,0 +1,78 @@ +#! /usr/bin/env lua + +--[[ + + fluksod.lua - Lua part of fluksod + + Copyright (C) 2011 Bart Van Der Meerssche + + 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 . + +]]-- + +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 diff --git a/mote/v2/openwrt/package/flukso/luasrc/test b/mote/v2/openwrt/package/flukso/luasrc/rrdtest.lua similarity index 100% rename from mote/v2/openwrt/package/flukso/luasrc/test rename to mote/v2/openwrt/package/flukso/luasrc/rrdtest.lua diff --git a/mote/v2/openwrt/package/flukso/luasrc/spi/spid.lua b/mote/v2/openwrt/package/flukso/luasrc/spi/spid.lua deleted file mode 100755 index c8cd696..0000000 --- a/mote/v2/openwrt/package/flukso/luasrc/spi/spid.lua +++ /dev/null @@ -1,60 +0,0 @@ -#! /usr/bin/env lua - ---[[ - - spid.lua - Lua part of the spi daemon - - Copyright (C) 2011 Bart Van Der Meerssche - - 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 . - -]]-- - - -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')