This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Norwegian HAN-interface - Kamstrup meter
#6
Thanks for improvement suggestion, Admin. I didn't know that successive concatenations to a single string have poor performance. Smile 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:
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
Reply


Messages In This Thread
RE: Norwegian HAN-interface - Kamstrup meter - by oveh - 10.02.2019, 19:28

Forum Jump: