Thanks for improvement suggestion, Admin. I didn't know that successive concatenations to a single string have poor performance.
I am new to LUA, so i appreciate feedback, how to optimize the code.
Tokatubs.
Working away from home at the moment, and will not be able to test before end of February.
However, i did bring some samples of the data packages sent from the meter with me, and have been playing a little bit with them on my Laptop.
My first plan was to iterate through the whole package, and parse all the obis data and values. But since the packages always have the data i need in the same position every time, i decided that verifying the data package, and read out the data from the known position, would be both easier for me and less demanding for the LM.
Here is the code i have so far, works fine in ZeroBrane Studio:
Some more info how to parse the data:

Tokatubs.
Working away from home at the moment, and will not be able to test before end of February.
However, i did bring some samples of the data packages sent from the meter with me, and have been playing a little bit with them on my Laptop.
My first plan was to iterate through the whole package, and parse all the obis data and values. But since the packages always have the data i need in the same position every time, i decided that verifying the data package, and read out the data from the known position, would be both easier for me and less demanding for the LM.
Here is the code i have so far, works fine in ZeroBrane Studio:
Code:
------------------------------------------------------------------------------------
-- Sample data from 3 phase Kamstrup power meter
-- Type: 6841121BN243101040
-- Tested on ZeroBrane Studio, with Lua Interpreter: Lua (LuaJit)
------------------------------------------------------------------------------------
-- LuaJIT BitOp, same as LM bit lib?
local bit = require("bit")
-- 10 sec sample telegrams
sample_1 = string.char( 0x7e, 0xa0, 0xe2, 0x2b, 0x21, 0x13, 0x23, 0x9a, 0xe6, 0xe7, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x07, 0xe3, 0x01, 0x15, 0x01, 0x17, 0x2b, 0x0a, 0xff, 0x80, 0x00, 0x00, 0x02, 0x19, 0x0a, 0x0e, 0x4b, 0x61, 0x6d, 0x73, 0x74, 0x72, 0x75, 0x70, 0x5f, 0x56, 0x30, 0x30, 0x30, 0x31, 0x09, 0x06, 0x01, 0x01, 0x00, 0x00, 0x05, 0xff, 0x0a, 0x10, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x09, 0x06, 0x01, 0x01, 0x60, 0x01, 0x01, 0xff, 0x0a, 0x12, 0x36, 0x38, 0x34, 0x31, 0x31, 0x32, 0x31, 0x42, 0x4e, 0x32, 0x34, 0x33, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x09, 0x06, 0x01, 0x01, 0x01, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x0b, 0x08, 0x09, 0x06, 0x01, 0x01, 0x02, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x01, 0x01, 0x03, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x01, 0xd1, 0x09, 0x06, 0x01, 0x01, 0x04, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x01, 0x01, 0x1f, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x04, 0x43, 0x09, 0x06, 0x01, 0x01, 0x33, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x02, 0xee, 0x09, 0x06, 0x01, 0x01, 0x47, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x02, 0x2d, 0x09, 0x06, 0x01, 0x01, 0x20, 0x07, 0x00, 0xff, 0x12, 0x00, 0xdc, 0x09, 0x06, 0x01, 0x01, 0x34, 0x07, 0x00, 0xff, 0x12, 0x00, 0xdd, 0x09, 0x06, 0x01, 0x01, 0x48, 0x07, 0x00, 0xff, 0x12, 0x00, 0xdf, 0x6b, 0xaa, 0x7e )
sample_2 = string.char( 0x7e, 0xa0, 0xe2, 0x2b, 0x21, 0x13, 0x23, 0x9a, 0xe6, 0xe7, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x07, 0xe3, 0x01, 0x1d, 0x02, 0x09, 0x39, 0x1e, 0xff, 0x80, 0x00, 0x00, 0x02, 0x19, 0x0a, 0x0e, 0x4b, 0x61, 0x6d, 0x73, 0x74, 0x72, 0x75, 0x70, 0x5f, 0x56, 0x30, 0x30, 0x30, 0x31, 0x09, 0x06, 0x01, 0x01, 0x00, 0x00, 0x05, 0xff, 0x0a, 0x10, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x09, 0x06, 0x01, 0x01, 0x60, 0x01, 0x01, 0xff, 0x0a, 0x12, 0x36, 0x38, 0x34, 0x31, 0x31, 0x32, 0x31, 0x42, 0x4e, 0x32, 0x34, 0x33, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x09, 0x06, 0x01, 0x01, 0x01, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x15, 0x30, 0x09, 0x06, 0x01, 0x01, 0x02, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x01, 0x01, 0x03, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x01, 0x38, 0x09, 0x06, 0x01, 0x01, 0x04, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x01, 0x01, 0x1f, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x08, 0xab, 0x09, 0x06, 0x01, 0x01, 0x33, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x07, 0xb2, 0x09, 0x06, 0x01, 0x01, 0x47, 0x07, 0x00, 0xff, 0x06, 0x00, 0x00, 0x02, 0x37, 0x09, 0x06, 0x01, 0x01, 0x20, 0x07, 0x00, 0xff, 0x12, 0x00, 0xd6, 0x09, 0x06, 0x01, 0x01, 0x34, 0x07, 0x00, 0xff, 0x12, 0x00, 0xda, 0x09, 0x06, 0x01, 0x01, 0x48, 0x07, 0x00, 0xff, 0x12, 0x00, 0xdd, 0x21, 0x57, 0x7e )
-- Table dump / pretty print function found online:
-- http://lua-users.org/wiki/TableSerialization
function print_r (t, indent) -- alt version, abuse to http://richard.warburton.it
local indent=indent or ''
for key,value in pairs(t) do
io.write(indent,'[',tostring(key),']')
if type(value)=="table" then io.write(':\n') print_r(value,indent..'\t')
else io.write(' = ',tostring(value),'\n') end
end
end
-- Return CRC-16/X-25
function crc16(data)
local crc = 0xFFFF
for i = 1, #data do
crc = bit.bxor(crc, data:byte(i))
for j = 1, 8 do
local k = bit.band(crc, 1)
crc = bit.rshift(crc, 1)
if k ~= 0 then
crc = bit.bxor(crc, 0x8408)
end
end
end
return bit.bxor(crc, 0xFFFF)
end
-- Convert 2 bytes to number
function uint16(b)
if( #b ~= 2) then
return
else
return bit.lshift(b:byte(1),8) + b:byte(2)
end
end
-- Convert 4 bytes to number
function uint32(b)
if( #b ~= 4) then
return
else
--Workaround needed... lshift(XX,24) gives som strange results on numbers bigger than 0x7f....
--return bit.lshift(b:byte(1),24) + bit.lshift(b:byte(2),16) + bit.lshift(b:byte(3),8) + b:byte(4)
return b:byte(1) * 2 ^ 24 + bit.lshift(b:byte(2),16) + bit.lshift(b:byte(3),8) + b:byte(4)
end
end
-- Convert 12 byte octet-string to date/time
function date_time(b)
if (#b ~= 12) then
return
end
local weekdays = {"Mon","Tue","Wed","Thu","Fri","Sat","Sun"}
local t = {}
t.year = uint16(b:sub(1,2))
t.month = b:byte(3)
t.day = b:byte(4)
t.wday = weekdays[b:byte(5)]
t.hour = b:byte(6)
t.min = b:byte(7)
t.sec = b:byte(8)
return t
end
function parse(b)
-- Check package length
if (#b ~= 228) then
print("Error: Wrong package length, 10 sec package should be 228 bytes")
return
end
-- Check start and stop flag
if ( b:byte(1) ~= 0x7E or b:byte(-1) ~= 0x7E) then
print("Error: Wrong start and/or stop flag, both must be 0x7E on valid package")
return
end
-- Parse checksum bytes -> 0xAA, 0xBB -> 0xBBAA
check_sum= bit.lshift(b:byte(-2),8) + b:byte(-3)
-- Compare checksum bytes with calculated cheksum
if ( check_sum ~= crc16( b:sub(2,-4))) then
print("Error: CRC mismatch")
return
end
-- Verify list version
if ( b:sub(34,47) ~= "Kamstrup_V0001") then
print("Error: Not Kamstrup_V0001 list")
return
end
-- All looks good so far. Read out data from package
local t = {}
t.time = date_time(b:sub(18,29))
t.version = "Kamstrup_V0001"
t.id = b:sub(58,73)
t.type = b:sub(84,101)
-- P: Active power in Watt
-- Q: Reactive power in kVar
-- I: Current in Amp
-- U: Voltage in Volt
t.P = { imp = uint32(b:sub(111,114)), exp = uint32(b:sub(124,127)) }
t.Q = { imp = uint32(b:sub(137,140)), exp = uint32(b:sub(150,153)) }
t.I = { uint32(b:sub(163,166)) / 100, uint32(b:sub(176,179)) / 100, uint32(b:sub(189,192)) / 100 }
t.U = { uint16(b:sub(202,203)) , uint16(b:sub(213,214)) , uint16(b:sub(224,225)) }
return t
end
result = parse(sample_1)
--result = parse(sample_2)
if (result) then
print_r(result)
--update_influxDB(result)
--update_kamstrup_grp(result)
end
Some more info how to parse the data:
Code:
Datatypes:
structure 02 LL (LL elements)
octet-string 09 LL XX .. (LL bytes)
visible-string 0A LL XX .. (LL bytes)
unsigned32 06 XX XX XX XX (04 bytes)
unsigned16 12 XX XX (02 bytes)
OBIS CODE - octet-string[06]
09 06 01 01 1F 07 00 FF = 1.1.31.07.00.255
Date/time - octet-string[0C]
09 0C 07 E3 01 1D 02 0B 00 05 FF 80 00 00
2019/01/29 Tu 11:00:05
Byte[1-2] Year
Byte[3] Month
Byte[4] Day of month
Byte[5] Day of week, 1= Mon, 7 = Sun
Byte[6] Hours
Byte[7] Min
Byte[8] Sec
Byte[9-0C] ....
This date/time format is also used in the header,
but without the octet-string identifier 0x09