Introduction: How Read and Decode Data From M-BUS Smartmeters Using Microcontrollers Such As RP2040 or ESP32

In times of high energy prices, it is always good to have an overview of your own consumption of electricity, gas or heating oil. Many smart consumption meters are equipped with an M-BUS interface that can be used to query the current meter readings.

For microcontroller boards such as the Raspberry Pi Pico, Feather boards or the Arduino MKR, there are corresponding add-on boards with which this interface can be read out. I would like to present three such boards in my article and show how to read and decode the data in C++.

Supplies

There are various suitable expansion boards for the above-mentioned platforms, all of which have the same circuitry but match the respective board type in terms of shape and pin assignment:

Step 1: MBUS Master

Each of these boards provides an MBUS master. The MBUS can be connected to the extension with up to 6 slaves via a screw terminal. The 36V DC voltage for the MBUS is generated on the board itself from a DC voltage of 9 - 24V. Each board is equipped with a supply terminal for this purpose. On the board for the Raspberry Pi Pico, the 5V supply voltage of the Pico can also be used as a DC voltage source via solder jumpers on the HAT.

Step 2: Software

To query and decode the data of a connected MBUS slave - i.e. the meter - we have made a C++ programme for the Arduino IDE available on GitHub that can be used for all three platforms. The programme uses the MBusino library from Zeppelin500 to decode the received data.

Before compiling, the baud rate and the primary bus address of the slave must be set in the source code. The typical baudrate is 2400, but slower baudrates and bautrates up to 9600 are also possible.

#define MBUS_BAUD_RATE 2400   // slave baudrate
#define MBUS_ADDRESS 2        // slave address

Step 3: Read and Decode Data

The demo periodically reads the data from the connected meter, decodes the received data and outputs the result via the serial monitor of the Arduino IDE. Here an example of Zenner zelsius heat meter:

mbus: requesting data from address: 2
mbus: start long frame
mbus: good frame:
68 71 71 68 8 2 72 36 47 6 11 49 6a 88 4 a 0 0 0 c 78 36 47 6 11 4 6 2 0 0 0 82 4 6c e1 21 c2 84 0 6c ff ff 84 4 6 0 0 0 0 c4 84 0 6 0 0 0 80 82 a 6c e1 2c 84 a 6 0 0 0 0 4 13 76 0 0 0 2 59 6a 7 2 5d 8a 7 2 61 0 0 4 2d 0 0 0 0 4 3b 0 0 0 0 4 6d 13 c e5 2c 4 26 99 1 0 0 2 fd 17 0 0 1f cc 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Creating payload buffer...
Packet size: 119
Start Address: 19
Decoding...
[
  {
    "vif": 120,
    "code": 33,
    "scalar": 0,
    "value_raw": 11064736,
    "value_scaled": 1.1064736e7,
    "units": "",
    "name": "fab_number"
  },
  {
    "vif": 6,
    "code": 0,
    "scalar": 3,
    "value_raw": 2,
    "value_scaled": 2000,
    "units": "Wh",
    "name": "energy"
  },
  {
    "vif": 108,
    "code": 23,
    "scalar": 0,
    "value_raw": 230101,
    "value_scaled": 230101,
    "units": "Date_JJMMDD",
    "name": "time_point"
  },
  {
    "vif": 108,
    "code": 23,
    "scalar": 0,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "Date_JJMMDD",
    "name": "time_point"
  },
  {
    "vif": 6,
    "code": 0,
    "scalar": 3,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "Wh",
    "name": "energy"
  },
  {
    "vif": 6,
    "code": 0,
    "scalar": 3,
    "value_raw": -2147483648,
    "value_scaled": -2.147483648e12,
    "units": "Wh",
    "name": "energy"
  },
  {
    "vif": 108,
    "code": 23,
    "scalar": 0,
    "value_raw": 231201,
    "value_scaled": 231201,
    "units": "Date_JJMMDD",
    "name": "time_point"
  },
  {
    "vif": 6,
    "code": 0,
    "scalar": 3,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "Wh",
    "name": "energy"
  },
  {
    "vif": 19,
    "code": 2,
    "scalar": -3,
    "value_raw": 118,
    "value_scaled": 0.118,
    "units": "m3",
    "name": "volume"
  },
  {
    "vif": 89,
    "code": 18,
    "scalar": -2,
    "value_raw": 1898,
    "value_scaled": 18.98,
    "units": "C",
    "name": "flow_temperature"
  },
  {
    "vif": 93,
    "code": 19,
    "scalar": -2,
    "value_raw": 1930,
    "value_scaled": 19.3,
    "units": "C",
    "name": "return_temperature"
  },
  {
    "vif": 97,
    "code": 20,
    "scalar": -2,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "K",
    "name": "temperature_diff"
  },
  {
    "vif": 45,
    "code": 12,
    "scalar": 2,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "W",
    "name": "power"
  },
  {
    "vif": 59,
    "code": 14,
    "scalar": -3,
    "value_raw": 0,
    "value_scaled": 0,
    "units": "m3/h",
    "name": "volume_flow"
  },
  {
    "vif": 109,
    "code": 24,
    "scalar": 0,
    "value_raw": 2312051219,
    "value_scaled": 2.312051219e9
  }
]
Detected data fields: 17
Detected error: 0

Field 1 (fab_number): 11064736.00
Field 2 (energy): 2000.00 Wh
Field 3 (time_point): 230101.00 Date_JJMMDD
Field 4 (time_point): 0.00 Date_JJMMDD
Field 5 (energy): 0.00 Wh
Field 6 (energy): ovf Wh
Field 7 (time_point): 231201.00 Date_JJMMDD
Field 8 (energy): 0.00 Wh
Field 9 (volume): 0.12 m3
Field 10 (flow_temperature): 18.98 C
Field 11 (return_temperature): 19.30 C
Field 12 (temperature_diff): 0.00 K
Field 13 (power): 0.00 W
Field 14 (volume_flow): 0.00 m3/h
Field 15 (): 2312051200.00 Time_JJMMDDhhmm
Field 16 (): 0.00 Wh
Field 17 (): 0.00 Wh