Integrating an M-Bus meter in Home Assistant

Integrating an M-Bus meter in Home Assistant

M-Bus (also known as Meter-Bus) is a standard dating back to the '90s, designed for reading water and power meters. A wireless variant using the same data coding was later defined, but for this post we'll focus on the original version, which powers and reads up to 250 meters over 1 km, linked by a single pair of twisted wires.



Aim of this project

Our aim is to set up a long-term solution for measuring different points of water consumption in a household, so as to be able to

  1. measure their relative importance in order to better target savings measures;
  2. detect any leaks quickly and efficiently.

Six years ago, we showed you how to interface a pulse water meter. But this type of meter does not provide an absolute value of total consumption, and is therefore prone to errors in the event of a power outage.

In contrast, an M-Bus meter incorporates a battery that enables it to provide an absolute digital reading of total consumption, even after a power outage. It's a more reliable solution. Here's the meter supplied by our plumber:

A water meter with an M-Bus interface
A water meter with an M-Bus interface


Now we need to work out how to connect this M-Bus meter to our MQTT infrastructure, so that it's available in Home Assistant for continuous monitoring.

M-Bus: transmission principles

Basically, M-Bus is based on simple synchronous serial transmission, like RS-485. But to enable sensors to be powered directly by the data bus, without complex electronics, M-Bus has opted for the following conventions:

  • When not transmitting, the bus master supplies a constant voltage of 36V (state "1").
  • To transmit a "0" bit, the master lowers the voltage to 24V.
  • When not transmitting, a meter must draw a constant current.
  • To transmit a "1" bit, a meter increases its current consumption by 15mA (+-5mA).

The master therefore transmits its bits by modulating the voltage, whereas a counter transmits its bits by modulating its consumption. Using high voltages and currents guarantees immunity to interference. To simplify the electronics, a few additional rules apply:

  • Only one meter transmits at a given time, and only when the master is not transmitting.
  • The maximum bus load is <400mA.
  • The maximum bus resistance is <30 Ohm.
  • Even at maximum load, each meter is sure to receive at least 12V.

Communication speed can be configured by the installer between 300 baud and 9600 baud, depending on bus length. M-Bus uses 8 data bits, even parity and a stop bit (8E1).

Connecting an M-Bus to Home Assistant

In view of the above description, you won't be able to use an RS-232 or RS-485 interface directly to communicate with M-Bus sensors: you'll need dedicated electronics.

Ideally, we'd have used a Yocto-MBus, but it doesn't exist: to date, we've only had one such request, some ten years ago...

We could possibly have used the M-Bus Master Hat for Raspberry Pi, but firstly its bus-powering capabilities are very limited, and secondly we don't want to complicate the maintenance of our home automation system by adding an extra mini-computer.

We therefore chose a slightly more expensive, but entirely stateless option: the MM20-24 RS232 to M-Bus gateway, manufactured by DECODE. We don't need the more expensive Ethernet variant, as we'll be connecting the gateway directly to a Yocto-RS232, which will take care of periodic meter interrogation:

A Yocto-RS232 and a MM20-24 gateway enable us to integrate M-Bus into the Yoctopuce ecosystem
A Yocto-RS232 and a MM20-24 gateway enable us to integrate M-Bus into the Yoctopuce ecosystem


From there, integration into Home Assistant can be carried out via MQTT, as with the integration of the IntelliFlo pump presented last year.

The complete path from M-Bus meter to Home Assistant
The complete path from M-Bus meter to Home Assistant


This conversion solution adds a few hoops to what could be a native M-Bus integration in Home Assistant, but it has the enormous advantage of greatly facilitating system maintenance and data traceability.

But for this to work, we still need to configure the Yocto-RS232 to poll the M-Bus water meters and publish the measures, so that they are automatically injected into the MQTT infrastructure.

Configuring the Yocto-RS232

The first step is to check that we can communicate with the meters. We start by sending hand-coded messages via the Yocto-RS232 test interface.

According to the M-Bus standard, all M-Bus meters must respond to a message called SND_NKE, which corresponds to a ping. It consists of 5 bytes, given in hexadecimal below:

10"start" code, present at the beginning of each message sent
40control byte corresponding to the SND_NKE command
00address of the meter for which the message is intended
40checksum = (0x40 + 0x00) modulo 0x100
16"stop" code, present at the end of each message sent


The expected response is byte E5.

Testing M-Bus connectivity with a Yocto-RS232
Testing M-Bus connectivity with a Yocto-RS232


For this to work, you must have configured your Yocto-RS232 correctly. The standard tells us that this is aframe-based binary protocol, transmitted using eight data bits, an even parity bit, and a stop bit (8E1). It is also specified that, on leaving the factory, a counter should respond at address 00. However, it doesn't say what the default communication speed is, so you'll have to do a few tests between 300 bauds and 9600 bauds. Our sensor responded when we sent it SND_NKE at 2400 bauds. Here's our Yocto-RS232 configuration:

Configuring the Yocto-RS232 for M-Bus communication
Configuring the Yocto-RS232 for M-Bus communication


In the event that your meter never responds, whatever the communication speed, you can try using the FE address: this corresponds to a broadcast to which all meters must respond, whatever their address. If you add the checksum, the message to send is 10 40 FE 3E 16.

Testing a meter reading

To request a reading from meter 00, send the message REQ_UD2:

10"start" code, present at the beginning of each message sent
7Bcontrol byte corresponding to the REQ_UD2 command
00address of the meter for which the message is intended
7Bchecksum = (0x7B + 0x00) modulo 0x100
16"stop" code, present at the end of each message sent


The exact format of the response is a little more complex, and varies according to the type of meter. But it should respect the structure described below:

fixed part
68 C4 C4 68 RSP_UD header, length=C4(in our case)
08 control byte corresponding to an RSP_UD answer
00 address of answering meter
72 additional info: 72 = variable response size
23 42 27 00meter serial number (BCD format): 00274223
47 5F manufacturer identification = 5F47
03 hardware version: 03
07 meter type: 07 = water meter
44 40 00 00access code, status, signature
data block 1
04 variable 0, stored over 4 bytes (32 bit)
13 measuring unit: code 13 = 1 liter
E1 28 00 000x28E1 = 10'465 liters
data block 2
04 variable 0, stored over 4 bytes (32 bit)
6D measuring unit: time stamp
2B 11 1A 3"60x361a112b = 2024/06/26 19:49
etc.


If necessary, you can find full details of these mysterious encodings in the M-Bus specification, but be warned, it's pretty indigestible. It was obviously written by automaticians who don't have the same conception of data structure as computer scientists do: it's a mixture of representations inefficiently mixing storage and display (BCD type), control bits with names all but explicit stuck everywhere possible, and arbitrary codes organized with no apparent logic... To make things easier for you, you'll find:

  • on page 74, a table of meter types
  • on pages 38-39, the coding of the first byte of the data block, known as the DIF
  • on pages 42 and 78, the coding of the data block byte indicating the unit, called the VIF
  • on page 41, an explanation of the optional DIFE bytes that are sometimes inserted between the DIF and the VIF; their presence is identified by the fact that the upper bit of the preceding byte is active (value >= 0x80)
  • on page 72, the format for representing dates and time stamps on 16 and 32 bits, which illustrates the point made earlier about M-Bus data formats...

If you're lost, you can always send an e-mail to support@yoctopuce.com and we'll help you out.

Automatic polling

Once you've checked that you can poll your meter and you know where to find the answer, you're ready to create an automatic polling job in the Yocto-RS232.

If you're content to just poll the counter itself, the job can be summed up as a periodic custom protocol task consisting of two commands: sending the REQ_UD2 message, and receiving the answer:

writeHex107B007B16
expect68(WORD)68080072(DWORD)(DWORD)(DWORD)0413($1:DWORDL).*


The expect expression is understood as follows: on receipt of a message in the expected format, genericSensor1 (denoted $1) is assigned the value of the 32-bit little-endian word (denoted DWORDL) at the given position. In our case, the expected format corresponds to the extraction of data from data block 1.

If we want to be a little more subtle, we can even compute the flow rate per minute directly in the job, so that it can also be published via MQTT. To do this, we store the counter value in a $prevVal variable, and compute the difference every 60 seconds. Based on this principle, here's the job we use to read two meters (addresses 01 and 02) in parallel, and publish the flow rate (in l/min) and meter reading (in m^3):

Init job, executed once:
compute$prevTime = 0
compute$prevVal1 = 0
compute$prevVal2 = 0
Measure task, executed every 500ms:
compute$currTime = utctime() div 60
assert$currTime != $prevTime
writeHex107B017C16
expect68(WORD)68080172(DWORD)(DWORD)(DWORD)0413($currVal1:DWORDL).*
writeHex107B027D16
expect68(WORD)68080272(DWORD)(DWORD)(DWORD)0413($currVal2:DWORDL).*
compute$6 = $currVal1 / 1000.0
compute$7 = $currVal2 / 1000.0
compute$delta1 = $currVal1 - $prevVal1
compute$delta2 = $currVal2 - $prevVal2
compute$prevTime = $currTime
assert$delta1 != $currVal1 || $delta2 != $currVal2
compute$1 = $delta1
compute$2 = $delta2


Once this job has been run, measures appear every minute in the corresponding genericSensor functions.

Publication to MQTT

To make it easier to retrieve measures with MQTT Discovery, it's a good idea to specify their name and measure unit of directly in the Yocto-RS232 configuration:

Configuring genericSensor functions
Configuring genericSensor functions


And above all, for measures to be sent to the MQTT broker, you need to enable the sending of timed reports for the corresponding functions, at the desired frequency:

Enabling timed reports, frequency 1/min
Enabling timed reports, frequency 1/min



To connect your Yocto-RS232 to the MQTT broker, you can either connect it to a YoctoHub-Ethernet, or connect it directly to the Home Assistant computer using VirtualHub. In either case, if you haven't already done so, configure an MQTT HTTP callback to your MQTT broker, and if everything has been done correctly, you should soon see your Yocto-RS232 appear in the list of MQTT devices. In its properties, you'll directly discover the sensors we've configured. Now all you have to do is give them a nice icon, and add them to your dashboard!

Sensors found by MQTT Discovery
Sensors found by MQTT Discovery


Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.