M-Bus - Meter-Bus

The M-Bus is serial line protocol popular within SCADA and metering deployments within Europe. The underlying norms are EN 13757-3 for application and EN 13757-2 for physical and data link layers. Since M-Bus is primarily serial protocol, which can be also bridged over TCP, there is single encoding scheme. Backing serial port might be of any type - RS232 (more common) or RS485.

Table 1. M-Bus binding capability table
Device discovery Channel discovery Read Write Subscribe

Yes

Yes

Yes

No

No

While binding does not discover compatible interfaces out of the box. This is due to fact that any serial interface might be used as M-Bus interface. Binding does discover devices through bus scanning procedure, if conducted. It can also detect channels from received frames.

Device discovery

Each M-Bus device nowadays can be identified through primary or secondary address. The primary address is a serial bus identifier which is a number from 1 up to 250. Secondary scan procedure is much longer as it iterates over possible combinations of secondary address which is composed of 8 bytes.

The secondary address is constructed from several fields:

  • manufacturer - a three char manufacturer code.

  • device - device identifier, its serial number.

  • version - number which indicate hardware revision or model.

  • type - number which translates to pre-defined device types (oil meter, electricity meter, gas meter etc).

Based on above it is possible to construct a scanning mask which will limit address range. Once device is discovered it is being reported as a Thing which can be accepted through "inbox" functionality.

Thing and Channel discovery

As mentioned above - each meter which properly answers for primary or secondary address call is identified as a separate Thing. Once Thing is accepted through inbox functionality, additional configuration can be supplied.

Binding by default will create a new channel for each properly encoded data record within frame. The protocol defines several encoding schemes for data:

  • Empty data block

  • INT8

  • INT16

  • INT24

  • INT32

  • INT48

  • INT64

  • Real (32 bit)

  • BCD2

  • BCD4

  • BCD6

  • BCD8

  • BCD12

  • Date

  • String

Types are simplified at binding level to top-level primitive kinds - Long, Double, Date, String, BCD and None. From that point of view linked items do not need to worry about exact encoding of data record.

Readout principles

The readout specification rely on cyclic polling (refresh interval). In case of M-Bus all values (data records) are available at single poll attempt. This means that there is no support for refresh interval at channel level, because all channels are fetched at one call.

Polling based on refreshInterval parameter can be cascaded to limit amount of places where it is defined. Most of our bindings provide this parameter at multiple levels - from Bridge, through Thing down to individual Channel. Value for refresh interval is always defined in milliseconds providing fine-grained control over cycle time. The refresh time is minimum time between poll cycles. Please be aware that it is not guaranteed that cycle time will always be the same.

Read-outs are grouped by refresh interval and, if possible, conducted in groups. Each refresh interval value will result in spinning of a separate polling task.

For example, by defining refreshInterval of 60000 (ms) at the Bridge and leaving it at 0 (ms) at Thing and Channel you will have single task. This task, if possible, will attempt to read the largest possible set of data, unless it is not permitted by configuration. Channels which do not fit into single request will be split and conducted in follow-up requests after response for earlier call arrives. All these are assumed to be executed in the same readout cycle, even if their request is generated after specified refreshInterval.

If you define a refreshInterval of 1000 (ms) at the Channel instance, next to above bridge you will end up with two tasks. First which will fire every minute and second which will trigger every second which will poll single channel.

Optimization of readouts is always specific to protocol.

Write principles

There is no write support within this binding. There are no specific remarks for writing.

Channel definitions

Each channel is configured with div and vib parameters which uniquely identify each record within frame. The dib stands for Data Information Block and vib represents Value Information Block. Both blocks deliver together information of how its value should be interpreted. The information which comes from these two places are:

DIB ships:

  • Storage - a present (0), or historical data value register.

  • Function

    • Instant value

    • Max value

    • Min value

    • Error value

  • Sub unit

  • Tariff

  • Storage number

VIB delivers:

  • Measurement kind

  • Multiplier

  • Measurement unit

All above information by default is placed in detected channel label / description, if available. It does not trigger any specific behavior when it comes to data acquisition.

Configuration example

dsl
Bridge co7io-mbus:openhab-serial:port2 "Serial Interface " [ serialPort="/dev/ttyS2", baudRate=2400, dataBits="DATABITS_5" ] { (1)

  Thing device heatMeter1 "Kamstrup Multical" [ address=1, refreshInterval=60000 ] { (2)
    Type number : power "Heat power" [ dib="AO", vib="10" ] (3)
    Type number : flowRate "Flow rate" [ dib="AO", vib="11" ]
  }

  Thing device heatMeter2 "Techem ultra" [ address=2, refreshInterval=360000 ] {
    Type number : power "Heat power" [ dib="1D", vib="F0" ]
    Type number : flowRate "Flow rate" [ dib="3D", vib="FB" ]
  }

}
1 The serial bridge which points to serial port and transmission settings.
2 Device with defined primary address to poll.
3 Channel associated with device, it does rely on hex notation for DIB and VIB parameters.
co7io
<?xml version="1.0" encoding="utf-8" ?>
<things xmlns="http://connectorio.com/xmlns/managed/things">

  <bridge type="co7io-wmbus:serial-jrxtx" label="Radio interface">
    <id>radio1</id>
    <config>
      <serialPort>/dev/ttyS3</serialPort>
      <manufacturer>AMBER</manufacturer>
    </config>
  </bridge>

  <thing type="co7io-wmbus:device" label="Kamstrup Multical">
    <id>heatMeter1</id>
    <bridge>co7io-wmbus:serial-jrxtx:radio1</bridge>
    <config>
      <serialNumber>01234</serialNumber>
      <manufacturer>KAM</manufacturer>
      <version>3</version>
      <deviceType>HEAT_METER</deviceType>
    </config>
    <channel>
      <id>co7io-wmbus:device:radio1:heatMeter1:power</id>
      <type>co7io-wmbus:number</type>
      <label>Heat power</label>
      <config>
        <dib>AO</dib>
        <vib>10</vib>
      </config>
    </channel>
    <channel>
      <id>co7io-wmbus:device:radio1:heatMeter1:flowRate</id>
      <type>co7io-wmbus:number</type>
      <label>Flow rate</label>
      <config>
        <dib>AO</dib>
        <vib>11</vib>
      </config>
    </channel>
  </thing>

  <thing type="co7io-wmbus:device" label="Techem ultra">
    <id>heatMeter2</id>
    <bridge>co7io-wmbus:serial-jrxtx:radio1</bridge>
    <config>
      <serialNumber>43210</serialNumber>
      <manufacturer>TCH</manufacturer>
      <version>23</version>
      <deviceType>HEAT_METER</deviceType>
    </config>
    <channel>
      <id>co7io-wmbus:device:radio1:heatMeter2:power</id>
      <type>co7io-wmbus:number</type>
      <label>Heat power</label>
      <config>
        <dib>1D</dib>
        <vib>F0</vib>
      </config>
    </channel>
    <channel>
      <id>co7io-wmbus:device:radio1:heatMeter2:flowRate</id>
      <type>co7io-wmbus:number</type>
      <label>Flow rate</label>
      <config>
        <dib>3D</dib>
        <vib>FB</vib>
      </config>
    </channel>
  </thing>

</things>
yaml
---
things:
- kind: "Bridge"
  UID: "co7io-mbus:openhab-serial:port2"
  label: "Serial Interface "
  configuration:
    baudRate: 2400
    serialPort: "/dev/ttyS2"
    dataBits: "DATABITS_5"
  things:
  - id: "heatMeter1"
    type: "co7io-mbus:device"
    label: "Kamstrup Multical"
    configuration:
      refreshInterval: 60000
      address: 1
    channels:
    - id: "power"
      type: "number"
      label: "Heat power"
      configuration:
        dib: "AO"
        vib: "10"
    - id: "flowRate"
      type: "number"
      label: "Flow rate"
      configuration:
        dib: "AO"
        vib: "11"
  - id: "heatMeter2"
    type: "co7io-mbus:device"
    label: "Techem ultra"
    configuration:
      refreshInterval: 360000
      address: 2
    channels:
    - id: "power"
      type: "number"
      label: "Heat power"
      configuration:
        dib: "1D"
        vib: "F0"
    - id: "flowRate"
      type: "number"
      label: "Flow rate"
      configuration:
        dib: "3D"
        vib: "FB"