Markdown source

# How to connect a Modbus RS-485 power and energy meter on Linux

On Linux it is very easy to interface Modbus devices on RS-485, 
in this article how to connect a power meter is explained.

##DZT 6001: 80A, Single phase kWh meter, LCD, RS485/Modbus
The device I selected as power meter for home usage is from Deutsche Zähler Technik - DZT (DZT for short),
apparently a German company but really a trademark of Ineprometering 
Anyway the products are sold via a web shop in Netherlands (put aside Amazon).

The DZT 6001 is the simplest one, single phase and it lacks of more sophisticated 
features, likewise the TID measurement, as found in other products.

Among defects (but inconveninence would be a better term) we found a quite crude
clamp on the live line, so you need to protect the wire with a cowling
because it is missing.

Anyway, it allows the measurement of the following parameters:

* Active energy (kWh)
* Reactive energy (kWh)
* Active Power (kW)
* Reactive Power (kVAr) 
* Apparent power (kVA)
* Power factor (cos φ)
* Voltage (V)
* Current (A)

All device's registries readable via Modbus are documented quite well in the manual
(see below [5]), with the notable exception of cos  φ, but, as shown in the next
paragraph, it can be easily identified making a full register dump. 

##Interfacing RS-485 on Linux
If your Linux system doesn't support natively a RS-485 serial interfaces (as CM3-HOME does),
no worries, fortunately a few RS-485 serial/USB converters are readily available on the market.
I used this one:

<img src="./rs485-usb.jpg">

this device is based on the CP2104 for the USB/serial conversion (and so it is fully 
supported on recent Linux kernels) and on the venerable SN75176 for the RS-485 
electrical implementation.

Once connected to USB port, a new /dev/ttyUSBxxx Linux device will appear and thats it.

##Interfacing DTV-6001 on Acmesystems CM3-HOME

When using the Acmesystems CM3-HOME the interface dongle is not anymore neede, in fact the CM3-HOME
has up two RS-485 natively available as /dev/ttyUSB0 and /dev/ttyUSB2, so nothing change, put aside the
name of serial interface.

<img src="./rs485_left.jpg">

<img src="./rs485_right.jpg">


<div class="alert alert-danger">
<strong>Danger!</strong> Take care when wiring the DZT device as dangerous, 
potentially deadly, voltages and currents are involved.
If you don't master high voltages techniques, ask for a skilled technician intervention.

In order to achieve a correct working of DZT 6001, it has to be wired in series with
the live wire (phase), whilst the neutral has to be connected in parallel, otherwise 
it won't work due to missing power supply for its internal circuits.

<img src="./dzt-6001-wiring.png">

##Implementing Modbus communications

The Modbus protocol is well described on several articles and papers on the Internet: 
however it turned out that, just using the libraries for Python and C found on Linux, 
it is not even needed to delve into the protocol intricacies, 
as the libraries do all the dirty job.

From a conceptual point of view,  a Modbus device is seen as a list of register,
accessible with an address (index) starting from zero.

When using RS-485 as bearer channel, as we do, another important point to know is 
the line polling address: in the examples we will assume that
only one device is attached to the line, and the address is the default (1).

##Python example

The library I selected is pymodbus: you can install it using pip:

sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get install python-pip
sudo pip install  -U pymodbus

The minimalistic Python code is made of just five rows:

<pre class="prettyprint"  "lang-python">
from pymodbus.client.sync import ModbusSerialClient as ModbusClient

client = ModbusClient(method='rtu', port='/dev/ttyUSB0', timeout=1, stopbits = 1, bytesize = 8,  parity='N', baudrate= 9600)
request = client.read_holding_registers(0x00,0x2c,unit=1)
print request.registers

we are assuming that the serial interface is seen as __/dev/ttyUSB0__ , 
9600 b/s, 8 bits, no parity, 1 stop bit.

We are using the RTU, remote terminal unit mode. 

From [6]:

>The transmission mode in serial communications defines the way the Modbus messages are coded. 
With Modbus/ASCII, the messages are in a readable ASCII format. 
The Modbus/RTU format uses binary coding which makes the message unreadable when monitoring, 
but reduces the size of each message which allows for more data exchange in the same time span.

The ModbusClient object is what represents the serial line, the connect() method
takes care to open it.
In case of success all the registries in the range 0..44 are read and contents
dumped to terminal.

In this way, and comparing the dump with what is shown on device's display,
we can see that the register #6, not documented in [5], contains the of COS PHY value
(times 100).

All the values, duly converted to meaningful values, can be printed one by one
with the following python program:

<pre class="prettyprint"  "lang-python">
#!/usr/bin/env python
# Andrea Montefusco 2018
# Test program for DZT 6001: 80A, Single phase kWh meter, LCD, RS485/Modbus
# Home page:
# User manual:
# Register reference:
# Prerequisite: install pymodbus
# sudo pip install  -U pymodbus
import time  # sleep

from pymodbus.client.sync import ModbusSerialClient as ModbusClient

client = ModbusClient(method='rtu', port='/dev/ttyUSB0', timeout=1, stopbits = 1, bytesize = 8,  parity='N', baudrate= 9600)

while True:

	# query the device with address 1 (unit=1)
	# the last readable register has index 0x2c
	r = client.read_holding_registers(0x00,0x2c,unit=1)

	print "Voltage: %.1f V" % (int(r.registers[0])/10.0)
	print "Current: %.1f A" % (float(r.registers[1])/10.0)

	print "Active power: %d W" % r.registers[3]
	print "Reactive power: %d W" % r.registers[4]
	print "Apparent power: %d W" % r.registers[5]

	print "CosPhy: %.3f" % (float(r.registers[6])/1000.0)

	print "Active energy: %.2f kWh" % (float(r.registers[7])/100.0)
	print "Reactive energy: %.2f kWh" % (float(r.registers[0x11])/100.0)

	print "Bit rate: %d " % (1200 << (r.registers[0x2a] - 1))
	print "--------------"

	time.sleep (5)


##C language example

For the C language I selected the libmodbus library (see reference [3]).
It has to be installed first (Debian/Ubuntu) as usual:

	sudo apt-get install libmodbus-dev

The following program shows how to dump all DZT device registers.

<pre class="prettyprint" "lang-c">
 * Andrea Montefusco 2018
 * Test program for DZT 6001: 80A, Single phase kWh meter, LCD, RS485/Modbus
 * Home page:
 * User manual:
 * Register reference:
 * Prerequisite: install libmodbus
 * sudo apt-get install libmodbus-dev
 * Compile and run with:
 * gcc -Wall -I/usr/include/modbus test_dzt6001.c  -lmodbus  -o test_dzt6001 && ./test_dzt6001
 * This program has been slightly modified from:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;errno.h&gt;
#include &lt;stdbool.h&gt;

#include &lt;modbus.h&gt;

int main()
  modbus_t *ctx = 0;

  // create a libmodbus context for RTU
  // doesn't check if the serial is really there
  ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);

  if (ctx == 0) {

    fprintf(stderr, "Unable to create the libmodbus context\n");
    return -1;

  } else {
    struct timeval old_response_timeout;
    struct timeval response_timeout;

    // enable debug
    modbus_set_debug(ctx, true);

    // initialize timeouts with default
    modbus_get_response_timeout(ctx, &old_response_timeout);
    response_timeout = old_response_timeout;

    // set the message and charcater timeout to 2 seconds
    response_timeout.tv_sec = 2;
    modbus_set_response_timeout(ctx, &response_timeout);
    modbus_set_byte_timeout(ctx, &response_timeout);


  // try to connet to the first DZT on the line
  // assume that line address is 1, the default
  // send nothing on the line, just set the address in the context
  if(modbus_set_slave(ctx, 1) == -1) {
    fprintf(stderr, "Didn't connect to slave/n");
    return -1;

  // establish a Modbus connection
  // in a RS-485 context that means the serial interface is opened
  // but nothing is yet sent on the line
  if(modbus_connect(ctx) == -1) {

    fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
    return -1;

  } else {

	int nreg = 0;
    uint16_t tab_reg[64];

    fprintf(stderr, "Connected\n");

    // read all registers in DVT 6001
    // the function uses the Modbus function code 0x03 (read holding registers).
    nreg = modbus_read_registers(ctx,0,0x2c,tab_reg);

    if (nreg == -1) {

       fprintf(stderr, "Error reading registers: %s\n", modbus_strerror(errno));

       return -1;

    } else {
       int i;

       // dump all registers content

       fprintf (stderr, "Register dump:\n");
       for(i=0; i &lt; nreg; i++)
         printf("reg #%d: %d\n", i, tab_reg[i]);


       return 0;

##Link on topic

* [[1] Modbus, a serial communications protocol](
* [[2] PyModbus](
* [[3] libmodbus(7) Manual Page](
* [[4] DZT 6001: 80A, Single phase kWh meter, LCD, RS485/Modbus](
* [[5] DZT-6001: Modbus registers](
* [[6] Modbus interface tutorial](