Connecting a Modbus energy meter to Home Assistant
I have an electricity meter that supports communication over Modbus. I wanted to add it to Home Assistant. Modbus is supported out-of-the-box by Home Assistant but I could not find an end-to-end tutorial how to connect a device and how to use a Modbus sensor for energy tracking. This article uses an LE-01MR electricity meter and Waveshare RS-485 to Ethernet/WiFi converter as an example.
Modbus basics
Modbus is an open (and libre) communication standard. It allows one master device to control many slave devices connected to a shared bus. It is a common protocol in industrial automation but nowadays you can also find very cheap devices for home automation that support Modbus. There are two most popular flavors of the protocol: Modbus RTU and Modbus TCP. Modbus TCP obviously runs over TCP/IP (and Ethernet/Wi-Fi). Modbus RTU most commonly uses RS-485 as the physical layer.
There is only one master on a single bus and up to 255 slave devices. The practical limit is often lower due to wiring, bandwidth requirements and RS-485 transceiver unit load.
I had an extra complication. I want to avoid having to run another cable from my breaker box to the device running Home Assistant. There are plenty of Modbus RTU to Modbus TCP converters available. Unfortunately almost all of them are made for the industrial market so they are pretty expensive. However, I found a Waveshare RS-485 to Ethernet/WiFi converter that is pretty cheap and still works well. A cheap USB to RS-485 converter is also an option.
RS-485 wiring
RS-485 uses a single twisted pair and a ground wire. Wires of the twisted pair are commonly called A & B. The ground wire is often forgotten which can lead to random communication problems. The bus needs electrical termination on both ends (commonly 120Ω resistors) but at very short lengths can also work without it.
In my case the wiring was as simple as connecting one end of a twisted pair (recovered from an Ethernet cable) to the electricity meter, second end to the converter box (A screw terminal to A, B screw terminal to B) plus a ground wire between these devices. As they were located next to each other in the breaker box and the wiring was extremely short (below 50 cm) I found out that communication at 9600 baud works reliably without any termination.
Slave addressing
Each Modbus slave has an address from 1 to 255 and each slave must have a unique address.
Modbus registers
Modbus has a concept of a register. A register is basically a 16-bit number. Every register has its own address. And... that is it. Modbus does not specify what a register means or what it does. It is only a number.
There are different kinds of registers, like: input registers, holding registers, output registers (coils). They can have independent functions (so that input register #5 is something totally unrelated to holding register #5). Everything depends on the slave device implementation. Some devices have a single addressing space, some don't. Some devices accept all read operations on any register, some are more picky and only support holding register reads to holding register addresses etc. Ultimately you always have to experiment to see what your device actually supports.
Every slave device manufacturer publishes a register map for their device is a similar format. Here is an example for the LE-01MR electricity meter. The map also contains default RS-485 settings (baud, parity) which are important to getting the initial communication right. The settings themselves are often configurable via registers. If the default settings won't work with your existing bus you can use a USB to RS-485 converter to reconfigure the slave and only then connect it to the bus.
If a value larger than 16-bit has to be exchanged the Modbus convention is to use a pair of adjacent registers. The one with lower address holds the upper bits (MSB).
Practically all devices use decimal scaling when transmitting fractional values instead of floating point numbers. For example: the LE-01MR meter represents line frequency in 0.01 Hz. Register value of 5002 means that the frequency is 50.02 Hz.
Quick testing
Before getting into Home Assistant configuration a basic check of communication with the slave is nice to have. There are many things that can go wrong and all will manifest just as "communication is broken". Some examples:
- Power (is the slave powered on?)
- RS-485 bus wiring (are all wires connected? is the ground connected? is termination okay? A wired to A? B wired to B?)
- Baud and parity settings (all devices on the bus should use the same settings)
- Slave address (does the slave have the address you think it should?)
- RTU to TCP converter (is the network configured correctly? is its baud and parity set right?)
- Are the correct register addresses being read?
- Are the correct read opcodes being used (input/holding/coils etc.)?
After connecting the Waveshare converter to my wireless network I used a utility called mbpoll
to check basic communication. The exact command was mbpoll 192.168.10.15 -p 8899 -a 1 -t 4 -r 304 -c 2 -0
. The IP address is of course the address of my RTU-to-TCP converter. 8899 is the port number. Modbus TCP standard port is 502 but my converter box uses a different port so I had to pass it to mbpoll
. -a 1
is the slave address. -t 4
tells to use the read holding register opcode. -r 304
is the register address to read (304 is the line frequency of my electricity meter). -c 2
tells mbpoll
to read 2 registers (304 & 305). -0
is a strange option, if I did not add it I had to decrement register addresses by one to read them out.
Working communication
This is how working communication should look like. You can see that the line frequency register value is 4997 (line frequency = 49.97 Hz) and voltage register value is 22872 (voltage = 228.72 VAC).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
How to debug
- Connection failed - the converter box did not respond (check networking using
ping
for example) or the slave address is wrong. - Connection refused - the converter box is reachable, the port number may be wrong, another connection is active (converters don't have to support multiple masters).
- Illegal data address - wrong register address. This response is generated by the slave so if you see it you can be sure that the network, the converter, the RS-485 bus, baud/parity and slave address are all correct! Yay!
- Illegal function - the particular combination of register address and operation are not supported by the slave. To fix it you can try using read input register instead of read holding register or another opcode. This response is generated by the slave so if you see it you can be sure that the network, the converter, the RS-485 bus, baud/parity and slave address are all correct! Yay!
Home Assistant configuration
Modbus in Home Assistant can only be configured by editing configuration.yaml
so here is the magic code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
|
Type, host and port are obvious. These may have to be changed if you plan to use an USB to RS-485 adapter but everything else in the config is independent of TCP or RTU.
It is a good example because it contains almost all possible combinations of register widths (uint16, int16, uint32). The scale
field specifies the scaling factor. You can try to come up with the right value based on the register map but you can also find it by trial and error until the numbers make sense (as I did). I know approximately if my house is consuming 100 W, 0.001 W or 100000 W. The precision
field specifies the number of digits after the decimal point that will be displayed.
The active energy entity is the only one that has a field state_class: total
. This tells Home Assistant that it is an incrementing counter. This entity can be used as the source for grid consumption data in the "Energy" tab which was the main point of my mini-project.
After adding the code to configuration.yaml
and restarting Home Assistant the entities should automatically appear in the entity list ("Settings", "Devices & Services", "Entities" at the top).
The entities can be added to the dashboard for a live view of your electrical system.
I release the code into public domain.