Hacky integration for M-Bus

I just finished slapping together a setup in ESPHome for my Kamstrup electricity meter which uses MBus to communicate. I found there were not very many examples of using the UART integration in ESPHome, so although probably not a lot of people will have the same setup as me, maybe it can serve as an example for similar projects. Keep in mind the code is not exactly what you’d call elegant… :slight_smile:

ESPHome config:

esphome:
  name: ams
  platform: ESP32
  board: nodemcu-32s
  includes:
    mbus.h

wifi:
  power_save_mode: light
  networks:
  - ssid: "LulzNettOppe"
    password: ##PASSWORD##
  - ssid: "LulzNettEkstra"
    password: ##PASSWORD##
  - ssid: "LulzNett"
    password: ##PASSWORD##

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:

uart:
  id: uart_bus
  tx_pin: GPIO17
  rx_pin: GPIO16
  baud_rate: 2400

# Example configuration entry
dallas:
  - pin: GPIO25
  
sensor:
  - platform: dallas
    address: 0xEA0214808622FF28
    name: "Temperature Sikringsskap"

  - platform: custom
    lambda: |-
      auto mbus_reader = new MbusReader(id(uart_bus));
      App.register_component(mbus_reader);
      return {mbus_reader->wattage_sensor, mbus_reader->reactive_power_sensor, mbus_reader->amperage_sensor, mbus_reader->voltage_sensor, mbus_reader->energy_sensor, mbus_reader->reactive_energy_sensor};
    
    sensors:
    - name: "AMS Wattage"
      unit_of_measurement: kW
      accuracy_decimals: 3
      filters:
        - multiply: 0.001
    - name: "AMS Reactive Power"
      unit_of_measurement: VAr
      accuracy_decimals: 0
      internal: true
    - name: "AMS Amperage"
      unit_of_measurement: A
      accuracy_decimals: 2
      filters:
        - multiply: 0.01
    - name: "AMS Voltage"
      unit_of_measurement: V
      accuracy_decimals: 0
    - name: "AMS Hourly Energy"
      unit_of_measurement: kWh
      accuracy_decimals: 3
      filters:
        - multiply: 0.01
    - name: "AMS Hourly Reactive Energy"
      unit_of_measurement: kVArh
      accuracy_decimals: 3
      internal: true
      filters:
        - multiply: 0.01

mbus.h:

#include "esphome.h"

class MbusReader : public Component, public uart::UARTDevice, public Sensor {
 public:
  MbusReader(uart::UARTComponent *parent) : uart::UARTDevice(parent) {}  
  uint8_t temp_byte = 0;
  uint8_t *temp_byte_pointer = &temp_byte;
  uint8_t uart_buffer_[512]{0};
  uint16_t uart_counter = 0;
  char uart_message[550];
  char temp_string[10];
  char obis_code[32];
  char temp_obis[10];
  uint32_t obis_value = 0;
  float wattage = 0;
  float amperage = 0;
  float voltage = 0;
  float energy = 0;

  Sensor *wattage_sensor = new Sensor();
  Sensor *amperage_sensor = new Sensor();
  Sensor *voltage_sensor = new Sensor();
  Sensor *energy_sensor = new Sensor();
  Sensor *reactive_power_sensor = new Sensor();
  Sensor *reactive_energy_sensor = new Sensor();

  void setup() override {

  }

  void loop() override {
    bool have_message = read_message();
  }

  bool read_message() {
    while(available() >= 1) {
      read_byte(this->temp_byte_pointer);
      if(temp_byte == 126) {
        if(uart_counter > 2) {
          uart_buffer_[uart_counter] = temp_byte;
          uart_counter++;
          uart_message[0] = '\0';
          strcpy(uart_message, "");
          for (uint16_t i = 0; i < uart_counter && i < 256; i++) {
            //sprintf(temp_string, "%02X", uart_buffer_[i]);
            //strncat(uart_message, temp_string, 2);
            if(uart_buffer_[i-1] == 9 && uart_buffer_[i] == 6) {
              obis_code[0] = '\0';
              strcpy(obis_code, "");
              for (uint16_t y = 1; y < 6; y++) {
                sprintf(temp_obis, "%d.", uart_buffer_[i + y]);
                strcat(obis_code, temp_obis);
              }
              sprintf(temp_obis, "%d", uart_buffer_[i + 6]);
              strcat(obis_code, temp_obis);
              ESP_LOGV("uart", "OBIS code found: %s message length: %d", obis_code, uart_buffer_[i + 7]);
              obis_value = 0;
              if(uart_buffer_[i + 7] == 6) {
                for(uint8_t y = 0; y < 4; y++) {
                  obis_value += (long)uart_buffer_[i + 8 + y] << ((3-y) * 8);
                }
              } else if(uart_buffer_[i + 7] == 18) {
                for(uint8_t y = 0; y < 2; y++) {
                  obis_value += (long)uart_buffer_[i + 8 + y] << ((1-y) * 8);
                }
              }
              
              if(strcmp(obis_code, "1.1.1.7.0.255") == 0) {
                  ESP_LOGV("uart", "Wattage: %d", obis_value);
                  wattage_sensor->publish_state(obis_value);
              } else if (strcmp(obis_code, "1.1.31.7.0.255") == 0) {
                  ESP_LOGV("uart", "Amperage: %d", obis_value);
                  amperage_sensor->publish_state(obis_value);
              } else if (strcmp(obis_code, "1.1.32.7.0.255") == 0) {
                  ESP_LOGV("uart", "Voltage: %d", obis_value);
                  voltage_sensor->publish_state(obis_value);
              } else if (strcmp(obis_code, "1.1.1.8.0.255") == 0) {
                  ESP_LOGV("uart", "Energy Usage Last Hour: %d", obis_value);
                  energy_sensor->publish_state(obis_value);
              } else if (strcmp(obis_code, "1.1.4.7.0.255") == 0) {
                  ESP_LOGV("uart", "Reactive Power: %d", obis_value);
                  reactive_power_sensor->publish_state(obis_value);
              } else if (strcmp(obis_code, "1.1.4.8.0.255") == 0) {
                  ESP_LOGV("uart", "Reactive Power Last Hour: %d", obis_value);
                  reactive_energy_sensor->publish_state(obis_value);
              } else {
                ESP_LOGV("uart", "Unknown OBIS %s, value: %d", obis_code, obis_value);
              }
            }
            //strncat(uart_message, " ", 1);
            }
          ESP_LOGV("uart", "%d length received", uart_counter);
          //ESP_LOGI("uart", "%d length received: %s", uart_counter, uart_message);
          ESP_LOGV("uart", "Message length: %d", uart_message[3]);
          uart_counter = 0;
          uart_message[0] = '\0';
          strcpy(uart_message, "");
        } else {
          uart_counter = 0;
        }
      }
      uart_buffer_[uart_counter] = temp_byte;
      uart_counter++;
    }

    return false;
  }  
};
6 Likes

I am looking for solution to read my water meter information via M-Bus.
With ESP32 or NodeMCU with use of ESPHome.
What hardware do you use to convert M-Bus signal to UART?

I’ve been using one of these:

US $8 | TTL to MBUS, Serial to MBUS Slave Module, Instead of TSS721A, Signal Isolation!

2 Likes

Hey, did you manage to somehow realize communication between two slaves, or is there a master on the bus and you just listening to the communication using a slave module?

I just received the module you pasted here, connected battery powered water meter ( http://www.apator.com/en/offer/water-and-heat-metering/archives/at-mbus-01-02-03a-03b-04 ) to it and the module itself to a FTDI chip in order to listen for communication on my PC first, but I see no data.

Do you know if I need a master module in my scenario?

Edit: Misread your last post.

Are you seeing junk data at all? If not, you might be right as I believe the master is responsible for powering the line. It is possible to make a circuit to do this, but it would probably be considerably easier to just buy a ready module of some sort.

Nope, there’s not even a piece of junk on the console :slight_smile:
I just ordered a master module, let’s see how this will work out.

The mbus master module just came in.
It turns out that the slave module does not communicate on its own - it sends frames only when polled by a master.
So currently I am searching for any option to integrate mbus master module with home assistant, ideally with ESP :wink:

1 Like

Presuming that you need a master to poll the slave meters, did you find one that worked, and just to check, I’d then need something to decode the output from the master to get it into ESPHome and then into HomeAssistant?

Sorry just starting out on this HA journey. I just bought some M-Bus meters on an auction site and want to read them remotely so I can track consumption.

How can you read a meter with a slave module?

Hi @Oleksii_Zelivianskyi
I want to read my Kamstrup multical 21water meter mbus.
did you fint any usefull solutions ?

@dkcsn Try https://github.com/weetmuts/wmbusmeters

Hi all,

This topic is quite old, but it shows up at the top, when asking google about ESPhome and mbus. First, I want to state, that M-Bus and wireless M-Bus are quite different. They look quite similar when it comes to interpretation of the data, but widely used wmBus slaves transfer with T1 and send on their own in regular intervals. (wired) M-Bus slaves need to be polled.

Now to the ESPhome questions:

With wireless M-Bus, I see no easy solution to connect it with some ESP or ESP32. You need another radio module (868 MHz) to receive the data. For USB, there are some more or less cheap USB-to-wmBus-Converters (some use DVB-T-Sticks for it, other like me take the IMST iM871A-USB).

With M-Bus, you need a special circuit to level-shift the master’s TX data and also a part of the circuit to measure the current drawn by the slaves (master’s RX). There exist quite many cuircuits from chinese shops (aliexpress) that sell ready-to-use USB-to-M-Bus-Converters. Nevertheless, I also would find it great, if somebody could address it on some ESP32, maybe I’ll do this sooner or later…

With some Raspberry Pi and the mentioned converters, it is much easier to integrate M-Bus and wmBus meters into Home Assistant or other Smart Home systems.

For further Information, I wrote some stuff on my blog about M-Bus and wmBus, so anybody still interested could read through and if still some stuff is not clear, I would be happy to extend my post or answer questions here:

1 Like

Hi all,

I had the job to integrate 10 heat meters (Zenner Zelsius C5) into HA. They work over M-Bus (Meter Bus) wireless oder wired. I decided to use wires, since we could reuse some existing wires. They act as M-Bus slaves. All slaves are connected to the M-Bus in a star - topology.

The bus is connected to the Gateway. I use one from ADFweb. I decided to use the M-Bus/MQTT Gateway, because this was for me the easiest possibility to get the sensor values into HA.

I had to view, select and configure the M-Bus Parameters and publish them as MQTT-Topics. The Gateway reads the configured M-Bus data, converts it into MQTT-Topics and publishes it to the MQTT server (Mosquitto) in Home Assistant. From there I can easily configure them as sensors.

It is not the direct way, since I have to configure the sensors twice; but after a long research I found this the best solution for my situation.

As a result I get a nice view for every apparment. Something like this:

2 Likes

@klacol thank you very much for sharing. I am looking for a solution to read my water meters as well.
I have a few questions, maybe you know the answers to?

  1. I see in the ADFWEB specification that the gateway also has the wi-fi config option:
    http://www.adfweb.com/download/filefold/MN67931_ENG.pdf

How are you connecting the gateway to your LAN? via the ethernet cable or wifi? I would like to use the wifi and it is interesting if the connection is stable, etc.

  1. Do you happen to know if several M-BUS masters/gateways can talk to the same meter/device/slave at the same time? I might have a situation where my water company is querying the devices and I would like to install a personal gateway for my HA as well (my understanding is that there should be no problems?)

Thank you.

There is a link further up to the module I am using, with the caveat that I am connecting to a Kamstrup electricity meter that powers the line. Other MBUS devices may behave differently…

@dknt - could you please update how it went for you? Maybe post a link to what mbus master module you ordered? Is everything working for you now? On Rpi, or maybe on ESP? Thank you!

I checked the price. These ADFweb modules are quite expensive for “home use”… :roll_eyes:

Modbus_controller has been merged into esphomes dev branch yesterday.
It let’s you connect most modbus RTU devices if you know the modbus registers used by the device.
Documentation is not yet online on esphome.io (but already in the ‘next’ branch.

Until then have a look at

And the staged docs at Modbus Controller — ESPHome

@martgras this is great! could you build the same for M-bus? :slight_smile:

Didn’t know that m-bus is not modbus. Just saw register and got triggered :slightly_smiling_face:

2 Likes