Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Packages
Already included in 2016.09.30 firmware
Library docs
http://openrb.com/docs/mbus.htm
Examples
Note: as with any scripts which are using serial ports, make sure that only one script is running at any moment of time.
1. Scan the bus
If all short addresses are already programmed, you can scan the bus to find all connected meters. Scan function will return Lua table where key is meter's short address and value is the scan result. True means that the meter is working correctly, false means that probably several meters share the same short address which caused a collision during scan.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- scan from range 0 to 5 (inclusive)
res = mbus.scan(0, 5)
-- save result to Logs tab
log(res)
2. Change short address of a meter
For small installations you can connect meters one by one and change short addresses for each meter via an event script mapped to a 1-byte unsigned object. Object value is the new short address to set. Script assumes that all new meters have a default short address of 0.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- new address to write
addr = event.getvalue()
-- change short address from default 0 to new one
res, err = mbus.writeaddr(0, addr)
-- change ok
if res then
alert('[mbus] changed short address to' .. addr)
-- change failed
else
alert('[mbus] failed to change short address to ' .. addr .. ' ' .. tostring(err))
end
3. Reading meter data
You can read all single meter data via an event script mapped to a 1-byte unsigned object. Object value is the short address to read, make sure to wait before previous read completes or you will get collision errors.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- new address to write
addr = event.getvalue()
-- read all data
res, err = mbus.getdata(addr)
-- read ok
if res then
log(res)
-- read failed
else
alert('[mbus] failed to read data from short address ' .. addr .. ' ' .. tostring(err))
end
The return value will look similar to this:
Code: * table:
[data]
* table:
[0]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Fabrication number
[dif]
* string: 14
[value]
* string: 0
[vif]
* string: 120
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
[1]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Time Point (time & date)
[dif]
* string: 4
[value]
* string: 2016-08-04T07:27:00
[vif]
* string: 109
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
[2]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Volume (m m^3)
[dif]
* string: 4
[value]
* string: 27
[vif]
* string: 19
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
...
For this meter, current value resides at id 2, but it will be different for different meter models.
4. Gathering data from multiple meters
You can use this resident script to read values from meters and update object values as soon as meter value changes. Object datatype should be set to 4-byte unsigned integer when divisor is not set, otherwise it should be set to 4-byte floating point. Refer to the previous example on how to find the id value at which the current value resides.
Code: -- config init
if not meters then
require('mbus')
-- time to wait between each meter read
sleeptime = 10
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- base address for meter values, meter short address will be added to form the meters group address
base = '2/1/'
-- meter definition
-- addr - short address
-- id - number from data table where value resides
-- div - optional divisor for convertion to the final value
meters = {
{ addr = 1, id = 2, div = 1000 }, -- hot water meter, convert from m3 to liters
{ addr = 2, id = 2, div = 1000 }, -- cold water meter, convert from m3 to liters
{ addr = 3, id = 5 }, -- heating meter 1
{ addr = 4, id = 5 }, -- heating meter 2
}
-- reset meter values on first run
for _, meter in ipairs(meters) do
meter.value = 0
end
end
-- read each meter
for _, meter in ipairs(meters) do
res = mbus.getdata(meter.addr)
-- read ok
if type(res) == 'table' then
data = res.data[ meter.id ]
value = nil
-- get current value
if type(data) == 'table' then
value = tonumber(data.value)
end
-- value is valid and different from previous read
if value and meter.value ~= value then
-- apply divisor if set
div = meter.div
dpt = div and dt.float32 or dt.uint32
if div then
value = value / div
end
-- update group address value
grp.update(base .. tostring(meter.addr), value, dpt)
meter.value = value
end
end
-- wait for next read
os.sleep(sleeptime)
end
Posts: 429
Threads: 100
Joined: Jun 2015
Reputation:
45
Posts: 2
Threads: 0
Joined: Aug 2016
Reputation:
0
Hi, this is great!
I had long awaited for this option. For better usability there is necessary to add secondary addressing / scanning option, as it is main mode in bigger networks. For example the Qundis/ Siemens metering gateway WTT16 itself has primary m-bus address and meters inside the network is accessible thru secondary addressing.
Best regards
Posts: 3
Threads: 0
Joined: Jan 2021
Reputation:
0
Good morning, I am reading different meters by mbus communication, I have managed to read them following the tutorial by the port ('/ dev / RS232'). Would it be possible to read such data via Ethernet? I have a 4g router that has the possibility to read and transmit RS485 and RS232, it would be very useful to be able to install the router on the different counters and be able to read it all over Ethernet.
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Right now only serial connection is supported. TCP support can be added if needed. Do you want to use this solution to read meter data from different locations? Locally it's more cost-effective to connect all counters using MBUS and use a single gateway.
Posts: 3
Threads: 0
Joined: Jan 2021
Reputation:
0
(25.02.2021, 12:04)admin Wrote: Right now only serial connection is supported. TCP support can be added if needed. Do you want to use this solution to read meter data from different locations? Locally it's more cost-effective to connect all counters using MBUS and use a single gateway.
Thank you in advance.
We have counters in different locations that are not accessible to each other. Our idea is to put a gateway and a router in each building and read all the parameters with an lm5p-DR logic machine that we have installed in one of them.
I don't know if this is possible.
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
We've never tested MBUS over TCP. It should work in theory but this needs to be implemented first and then tested.
Posts: 3
Threads: 0
Joined: Jan 2021
Reputation:
0
(25.02.2021, 13:44)admin Wrote: We've never tested MBUS over TCP. It should work in theory but this needs to be implemented first and then tested.
Forgive me, I'm new with the scripts, I understand them a little but I don't know how to develop them, could you tell me how I can move forward to implement this and then test it?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
This cannot be done via scripting at the moment. TCP support must be added to the MBUS library that LM has.
Posts: 29
Threads: 6
Joined: Jan 2016
Reputation:
4
Hello
Is it possible to read data using a secondary address?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
At this moment only primary addresses can be used
Posts: 156
Threads: 40
Joined: Mar 2021
Reputation:
2
02.08.2022, 13:30
(This post was last modified: 02.08.2022, 13:31 by Fahd.)
(04.08.2016, 06:50)admin Wrote: Packages
Already included in 2016.09.30 firmware
Library docs
http://openrb.com/docs/mbus.htm
Examples
Note: as with any scripts which are using serial ports, make sure that only one script is running at any moment of time.
1. Scan the bus
If all short addresses are already programmed, you can scan the bus to find all connected meters. Scan function will return Lua table where key is meter's short address and value is the scan result. True means that the meter is working correctly, false means that probably several meters share the same short address which caused a collision during scan.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- scan from range 0 to 5 (inclusive)
res = mbus.scan(0, 5)
-- save result to Logs tab
log(res)
2. Change short address of a meter
For small installations you can connect meters one by one and change short addresses for each meter via an event script mapped to a 1-byte unsigned object. Object value is the new short address to set. Script assumes that all new meters have a default short address of 0.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- new address to write
addr = event.getvalue()
-- change short address from default 0 to new one
res, err = mbus.writeaddr(0, addr)
-- change ok
if res then
alert('[mbus] changed short address to' .. addr)
-- change failed
else
alert('[mbus] failed to change short address to ' .. addr .. ' ' .. tostring(err))
end
3. Reading meter data
You can read all single meter data via an event script mapped to a 1-byte unsigned object. Object value is the short address to read, make sure to wait before previous read completes or you will get collision errors.
Code: -- load mbus library
require('mbus')
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- new address to write
addr = event.getvalue()
-- read all data
res, err = mbus.getdata(addr)
-- read ok
if res then
log(res)
-- read failed
else
alert('[mbus] failed to read data from short address ' .. addr .. ' ' .. tostring(err))
end
The return value will look similar to this:
Code: * table:
[data]
* table:
[0]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Fabrication number
[dif]
* string: 14
[value]
* string: 0
[vif]
* string: 120
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
[1]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Time Point (time & date)
[dif]
* string: 4
[value]
* string: 2016-08-04T07:27:00
[vif]
* string: 109
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
[2]
* table:
[timestamp]
* string: 1470292199
[unit]
* string: Volume (m m^3)
[dif]
* string: 4
[value]
* string: 27
[vif]
* string: 19
[function]
* string: Instantaneous value
[storagenumber]
* string: 0
...
For this meter, current value resides at id 2, but it will be different for different meter models.
4. Gathering data from multiple meters
You can use this resident script to read values from meters and update object values as soon as meter value changes. Object datatype should be set to 4-byte unsigned integer when divisor is not set, otherwise it should be set to 4-byte floating point. Refer to the previous example on how to find the id value at which the current value resides.
Code: -- config init
if not meters then
require('mbus')
-- time to wait between each meter read
sleeptime = 10
-- use /dev/ttyUSB0 serial port with 2400 baud rate
mbus.init('/dev/ttyUSB0', 2400)
-- base address for meter values, meter short address will be added to form the meters group address
base = '2/1/'
-- meter definition
-- addr - short address
-- id - number from data table where value resides
-- div - optional divisor for convertion to the final value
meters = {
{ addr = 1, id = 2, div = 1000 }, -- hot water meter, convert from m3 to liters
{ addr = 2, id = 2, div = 1000 }, -- cold water meter, convert from m3 to liters
{ addr = 3, id = 5 }, -- heating meter 1
{ addr = 4, id = 5 }, -- heating meter 2
}
-- reset meter values on first run
for _, meter in ipairs(meters) do
meter.value = 0
end
end
-- read each meter
for _, meter in ipairs(meters) do
res = mbus.getdata(meter.addr)
-- read ok
if type(res) == 'table' then
data = res.data[ meter.id ]
value = nil
-- get current value
if type(data) == 'table' then
value = tonumber(data.value)
end
-- value is valid and different from previous read
if value and meter.value ~= value then
-- apply divisor if set
div = meter.div
dpt = div and dt.float32 or dt.uint32
if div then
value = value / div
end
-- update group address value
grp.update(base .. tostring(meter.addr), value, dpt)
meter.value = value
end
end
-- wait for next read
os.sleep(sleeptime)
end
Here I'm again,
My first time will work with m-bus and I was able to read values from the meter
Quote:* table:
["data"]
* table:
[0]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Actuality Duration (seconds)
["dif"]
* string: 9
["value"]
* string: 4
["vif"]
* string: 116
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[1]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Averaging Duration (seconds)
["dif"]
* string: 9
["value"]
* string: 4
["vif"]
* string: 112
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[2]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Energy (kWh)
["dif"]
* string: 12
["value"]
* string: 27342
["vif"]
* string: 6
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[3]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Volume (1e-2 m^3)
["dif"]
* string: 12
["value"]
* string: 156690
["vif"]
* string: 20
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[4]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Power (kW)
["dif"]
* string: 11
["value"]
* string: 0
["vif"]
* string: 46
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[5]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Volume flow ( m^3/h)
["dif"]
* string: 11
["value"]
* string: 0
["vif"]
* string: 62
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[6]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Flow temperature (deg C)
["dif"]
* string: 10
["value"]
* string: 23
["vif"]
* string: 91
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[7]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Return temperature (deg C)
["dif"]
* string: 10
["value"]
* string: 23
["vif"]
* string: 95
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[8]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Temperature Difference (1e-1 deg C)
["dif"]
* string: 10
["value"]
* string: 2
["vif"]
* string: 98
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[9]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Volume (1e-2 m^3)
["dif"]
* string: 76
["value"]
* string: 0
["vif"]
* string: 20
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 1
[10]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Energy (kWh)
["dif"]
* string: 76
["value"]
* string: 0
["vif"]
* string: 6
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 1
[11]
* table:
["timestamp"]
* string: 1659446656
["unit"]
* string: Fabrication number
["dif"]
* string: 12
["value"]
* string: 67095040
["vif"]
* string: 120
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[12]
* table:
["unit"]
* string: Averaging Duration (minutes)
["timestamp"]
* string: 1659446656
["dif"]
* string: 137
["device"]
* string: 0
["value"]
* string: 60
["tariff"]
* string: 1
["vif"]
* string: 113
["function"]
* string: Instantaneous value
["storagenumber"]
* string: 0
[13]
* table:
["unit"]
* string: Power (100 W)
["timestamp"]
* string: 1659446656
["dif"]
* string: 155
["device"]
* string: 0
["value"]
* string: 302
["tariff"]
* string: 1
["vif"]
* string: 45
["function"]
* string: Maximum value
["storagenumber"]
* string: 0
[14]
* table:
["unit"]
* string: Power (100 W)
["timestamp"]
* string: 1659446656
["dif"]
* string: 219
["device"]
* string: 0
["value"]
* string: 0
["tariff"]
* string: 1
["vif"]
* string: 45
["function"]
* string: Maximum value
["storagenumber"]
* string: 1
[15]
* table:
["unit"]
* string: Volume flow (m m^3/h)
["timestamp"]
* string: 1
I didn't know how to manage the script that will convert that data into objects!
Could you just give me an example of how to convert the [7] * table ? or what should I do to convert all the data into objects ?
Posts: 4646
Threads: 24
Joined: Aug 2017
Reputation:
207
See the point 4 again, You have to create objects in the sub group '2/1/' based on the instructions. If you want to use different one then change this in line 10.
------------------------------
Ctrl+F5
Posts: 156
Threads: 40
Joined: Mar 2021
Reputation:
2
(02.08.2022, 13:48)Daniel Wrote: See the point 4 again, You have to create objects in the sub group '2/1/' based on the instructions. If you want to use different one then change this in line 10.
Okay,
that's what I did.
1 - I have created 4-byte unsigned objects, "32/1/1, 32/1/2, 32/1/3, 32/1/4 ".
2 - Activated the script below
After that I got a value only on the first object 32/1/1 the other once doesn't update at all, and it looks like the 4 ids are writing their value on the 32/1/1
Code: -- config init
if not meters then
require('mbus')
-- time to wait between each meter read
sleeptime = 10
mbus.init('/dev/RS232', 9600)
-- base address for meter values, meter short address will be added to form the meters group address
base = '32/1/'
-- meter definition
-- addr - short address
-- id - number from data table where value resides
-- div - optional divisor for convertion to the final value
meters = {
{ addr = 1, id = 6},
{ addr = 1, id = 7},
{ addr = 1, id = 8},
{ addr = 1, id = 9},
}
-- reset meter values on first run
for _, meter in ipairs(meters) do
meter.value = 0
end
end
-- read each meter
for _, meter in ipairs(meters) do
res = mbus.getdata(meter.addr)
-- read ok
if type(res) == 'table' then
data = res.data[ meter.id ]
value = nil
-- get current value
if type(data) == 'table' then
value = tonumber(data.value)
end
-- value is valid and different from previous read
if value and meter.value ~= value then
-- apply divisor if set
div = meter.div
dpt = div and dt.float32 or dt.uint32
if div then
value = value / div
end
-- update group address value
grp.update(base .. tostring(meter.addr), value, dpt)
meter.value = value
end
end
-- wait for next read
os.sleep(sleeptime)
end
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
The script only handles cases when a single value is needed from each meter. It can be modified like this to be able to specify group address for each meter entry:
Code: -- config init
if not meters then
require('mbus')
-- time to wait between each meter read
sleeptime = 10
-- use /dev/RS232 serial port with 2400 baud rate
mbus.init('/dev/RS232', 2400)
-- base address for meter values, meter short address will be added to form the meters group address
base = '2/1/'
-- meter definition
-- addr - short address
-- id - number from data table where value resides
-- div - optional divisor for conversion to the final value
meters = {
{ addr = 1, id = 2, div = 1000, groupaddr = '1/1/1' }, -- hot water meter, convert from m3 to liters
{ addr = 2, id = 2, div = 1000, groupaddr = '1/1/2' }, -- cold water meter, convert from m3 to liters
{ addr = 3, id = 5, groupaddr = '1/1/3' }, -- heating meter 1
{ addr = 4, id = 5, groupaddr = '1/1/4' }, -- heating meter 2
}
-- reset meter values on first run
for _, meter in ipairs(meters) do
meter.value = 0
end
end
-- read each meter
for _, meter in ipairs(meters) do
res = mbus.getdata(meter.addr)
-- read ok
if type(res) == 'table' then
data = res.data[ meter.id ]
value = nil
-- get current value
if type(data) == 'table' then
value = tonumber(data.value)
end
-- value is valid and different from previous read
if value and meter.value ~= value then
-- apply divisor if set
div = meter.div
dpt = div and dt.float32 or dt.uint32
if div then
value = value / div
end
-- update group address value
addr = meter.groupaddr or (base .. tostring(meter.addr))
grp.update(addr, value, dpt)
meter.value = value
end
end
-- wait for next read
os.sleep(sleeptime)
end
Posts: 71
Threads: 5
Joined: Mar 2022
Reputation:
1
Hi everyone,
i'm trying to following this guide and the one on the main site https://openrb.com/m-bus-integration-in-lm/ to measure water consumption.
We have bought Tecbase Mbus 10 RS232-MBus gateway and we have connected it on our LM5 Lite (i.MX6) SW: 20210521. We are not using the external RS-232 USB converter but the internal RS-232 port of LM5 near the second RS-485 port. We have connected LM5-Tx to MbusGW-Rx and viceversa and both the GND wires.
Following the guide, i can't even scan the bus or getting communicating with it.
So i've tryied some logging with a scheduled script, changing baudrate from 300 to 9600 but nothing happens:
Code: if not port then
require('serial')
port = serial.open('/dev/RS232', { baudrate = 2400 })
port:flush()
line = ''
end
char, err = port:read(1, 60)
log(char, err)
But the request always goes in timeout, getting a response like this:
Code: * arg: 1
* nil
* arg: 2
* string: timeout
I have tryed enabling both RTU 2 and 3 as '/dev/RS232' setting the connection as 'Full-Duplex', but nothing changed.
Can anyone further help or have some ideas on how debug this?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
RTU is Modbus, not M-bus. Don't enable it for the port that you want to use for M-bus.
Only a single script must access the port at any time. Simply reading data from the port won't produce anything. Meters won't send anything without a request from the gateway.
Most likely you need to set short addresses as stated in the integration guide. Connect only one meter to the bus, set the short address then run the scan.
Also only one GND needs to be connected.
Posts: 71
Threads: 5
Joined: Mar 2022
Reputation:
1
(18.01.2023, 09:01)admin Wrote: RTU is Modbus, not M-bus. Don't enable it for the port that you want to use for M-bus.
Only a single script must access the port at any time. Simply reading data from the port won't produce anything. Meters won't send anything without a request from the gateway.
Most likely you need to set short addresses as stated in the integration guide. Connect only one meter to the bus, set the short address then run the scan.
Also only one GND needs to be connected.
Hi admin, thanks for the fast reply.
For both GND i meant that i've connected the GND pole of the MBus Gateway to the GND pole of LM5/RS232 (the last on the right). I've tryed right now to set the short address but i've got the alert error "[mbus] failed to change short address to 1 no reply" triggered by the script and i've tryed out 0,1,2... to 10 as value.
Obviously i've connected only one meter.
Thank you
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Try different baud rates, and try swapping Rx/Tx wires.
Posts: 71
Threads: 5
Joined: Mar 2022
Reputation:
1
(18.01.2023, 15:48)admin Wrote: Try different baud rates, and try swapping Rx/Tx wires.
Hi admin,
i've swapped Rx/Tx wires and with the total scan of the bus i finally found my 4 counters, one by one. Simply the address of the counters where the last 2-3 digits of the counter's id.
Now all is working fine.
For information purposes we have implemented this 4 counters with a Techbase 10 RS232 MBus converter:
For Hot and Cold water: There are 2 versions, one for hot, one for cold.
https://www.kamstrup.com/en-en/water-sol...lowiq-2101
For Heat and Cool metering: There are 2 versions, one for heat, one for cool.
https://www.kamstrup.com/en-en/heat-solu...ltical-603
|