I am planing to make a script that receives the power data from a Kamstrup Power meter.
In Norway all the power meters has been changed to smart meters, and one of the requirement from the government, was that customer should have access to the data.
Google translate this site for more info: https://www.nek.no/info-ams-han-utviklere/
As far as i know there is 3 types of power meters used, Kamstrup, Aidon and Kaifa.
They are all sending (pushing) data on a port (Physical interface is MBUS), and the data can be read with a Mbus to serial converter (slave type). I am using this, to connect LM to my Kamstrup meter: https://www.aliexpress.com/item/USB-tran...62958.html
-- Check if port is open, open serial port if notifnotportthenrequire('serial')
port = serial.open('/dev/ttyUSB0', { baudrate = 2400 })
port:flush()
data = ''end-- Read on byte, or time out after 1 secbyte = port:read(1, 1)
ifbytethen-- if byte read, add byte to data arraydata = data .. byteelse-- read timeout: No more data in this packageifstring.len(data) > 1thenloghex(data)
enddata = ""end
Here is a residential script that reads the data, pushed from the kamstrup meter.
There is two types of packages:
- every 10 sec we get the basic data
- every 1 hr we get the basic + a little bit more.
Example of a basic data (ID replaced with XX): * hexstring [228]: 7E A0 E2 2B 21 13 23 9A E6 E7 00 0F 00 00 00 00 0C 07 E3 01 1F 04 0D 24 0A FF 80 00 00 02 19 0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31 09 06 01 01 00 00 05 FF 0A 10 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 09 06 01 01 60 01 01 FF 0A 12 36 38 34 31 31 32 31 42 4E 32 34 33 31 30 31 30 34 30 09 06 01 01 01 07 00 FF 06 00 00 0E BB 09 06 01 01 02 07 00 FF 06 00 00 00 00 09 06 01 01 03 07 00 FF 06 00 00 01 C4 09 06 01 01 04 07 00 FF 06 00 00 00 00 09 06 01 01 1F 07 00 FF 06 00 00 05 C1 09 06 01 01 33 07 00 FF 06 00 00 03 27 09 06 01 01 47 07 00 FF 06 00 00 03 91 09 06 01 01 20 07 00 FF 12 00 DC 09 06 01 01 34 07 00 FF 12 00 DE 09 06 01 01 48 07 00 FF 12 00 DD F7 9B 7E
Basic receive logic:
Receive 1 byte with 1 second timeout
In case of timeout, check if buffer is not empty and parse it
Otherwise add byte to buffer
The third byte seems to be packet length without first/last 0x7E so you can use that to parse buffer with multiple messages in case they arrive together (10 second and 1 hour data).
31.01.2019, 16:24 (This post was last modified: 31.01.2019, 18:12 by oveh.)
(31.01.2019, 13:58)admin Wrote: Basic receive logic:
Receive 1 byte with 1 second timeout
In case of timeout, check if buffer is not empty and parse it
Otherwise add byte to buffer
Can you please provide an example?
(31.01.2019, 13:58)admin Wrote: The third byte seems to be packet length without first/last 0x7E so you can use that to parse buffer with multiple messages in case they arrive together (10 second and 1 hour data).
Yes, actually it is both 3rd and lower part on 2nd, but the 1 hr and 10 sec message should not come together:
Quote:Push interval: Fixed at 3600s, The 10 second timer and the 1 hour timer must not collide since then one of the lists will not be pushed. So the 10 sec timer will be sent at xx:xx:x0 (hh:mms) and the 1 hour timer is offset 5 seconds and sent at xx:00:05 (hh:mms)
Some more sample data. ID-bytes have been replaced with 0x01, and checksum bytes have been updated accordingly
Your example already does what I've suggested. The only slight improvement is to use a table to buffer data.
Code:
12345678910111213141516171819
-- Check if port is open, open serial port if notifnotportthenrequire('serial')
port = serial.open('/dev/ttyUSB0', { baudrate = 2400 })
port:flush()
buffer = {}
end-- Read on byte, or time out after 1 secchar = port:read(1, 1)
ifcharthenbuffer[ #buffer + 1 ] = charelseif #buffer > 0thendata = table.concat(buffer)
-- parse(data)loghex(data)
buffer = {}
end
(31.01.2019, 13:58)admin Wrote: Basic receive logic:
Receive 1 byte with 1 second timeout
In case of timeout, check if buffer is not empty and parse it
Otherwise add byte to buffer
Can you please provide an example?
(31.01.2019, 13:58)admin Wrote: The third byte seems to be packet length without first/last 0x7E so you can use that to parse buffer with multiple messages in case they arrive together (10 second and 1 hour data).
Yes, actually it is both 3rd and lower part on 2nd, but the 1 hr and 10 sec message should not come together:
Quote:Push interval: Fixed at 3600s, The 10 second timer and the 1 hour timer must not collide since then one of the lists will not be pushed. So the 10 sec timer will be sent at xx:xx:x0 (hh:mms) and the 1 hour timer is offset 5 seconds and sent at xx:00:05 (hh:mms)
Some more sample data. ID-bytes have been replaced with 0x01, and checksum bytes have been updated accordingly
10.02.2019, 19:28 (This post was last modified: 10.02.2019, 19:34 by oveh.)
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:
Looks good. The issue with uint32 function using bit shifts is caused by bit lib operating on signed 32-bit integers, so your solution with multiplication is correct.