Skip to content

modbus protocol over serial interface to MQTT publish messages

License

Notifications You must be signed in to change notification settings

fernandorpardo/modbus2MQTT

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

modbus2MQTT

Apache-2.0 license ESP32

The project consists of the implementation of the modbus protocol over serial interface to get the measures from two wattmeters and translating the data into MQTT publish messages sent to a MQTT broker such as Mosquitto.

The problem to solve

The picture shows a simple installation of a home solar system for self-consumption, that is the test environment for this project. image1

I wanted to read the power measures and calculate the excess power to turn on and off the home appliances accordingly.

The first thing I tried was getting the values through the inverter’s API but for that you need an “API account” and the manufacturer declines to provide me with it, so I took the path explained here.

We need to measure the power generated by the inverter and the power from the grid. Home consumption is calculated from these two values.

We have already a DDSU666-H wattmeter at the grid side from which we can get the measures of the power coming in and out to the grid. To avoid interfering the dialogue between inverter and wattmeter we don’t generate traffic at this side but sniff the responses. RS-485 is not a point-to-point connection but a bus so other devices can be added.

On the inverter side, we need to add a wattmeter. The one here is an Eastron SDM120CT-MV and in this case Tx and Rx are wired.

The add-ons to the installation are the ones in red colour in the picture above.

HW module

The HW module is made of an ESP-WROOM-32 connected to the wattmeters through two TTL to RS-485 modules: through UART1 (pins 17 and 16) to the DDSU666-H and UART2 (pins 14 and 13) to the SDM120CT.

image2

DDSU666-H

One of the serial interfaces of the HW module is connected to the DDSU666-H wattmeter serial interface for reading only. No transmission is generated to avoid interfering into the inverter which can cause malfunctions. The information is extracted from the dialogue between inverter and the DDSU666-H which performs as follows:

The inverter sends periodically 3 types of requests to the slave address 0x0B

(1) Single register at 2006

Every 250-300ms the inverter requests reading register 2006 (active power)

The inverter sends		0B 03 20 06 00 02 2F 60
The DDSU666H responds	0B 03 04 BE 0D 6A 16 4A B6

(2) Multiple registers from 2000 to 2020

Around every 5 seconds the inverter sends a multiple register read from starting address 2000 for 34 (0022hex) positions = 17 registers

The inverter sends    0B 03 20 00 00 22 CE B9
The DDSU666H responds	
                      0B 03 44 
                      43 68 33 33 3F 16 45 A2 00 00 00 00 BE 0B C6 A8 
                      BE 0B C6 A8 00 00 00 00 00 00 00 00 00 00 00 00 
                      00 00 00 00 3E 0B C6 A8 3E 0B C6 A8 00 00 00 00 
                      BF 80 00 00 BF 80 00 00 BF 80 00 00 00 00 00 00 
                      42 47 F5 C3 
                      63 F0

                byte  value			
                00    0B  slave address
                01    03  function code 	0x03 = Read Multiple Holding Registers
                02    44  byte count = 44 hex (68 dec) bytes =>  68 / 4 bytes per register = 17 registers
                      value (4 bytes)  register
                03    43 68 33 33      2000
                      3F 16 45 A2      2002
                      00 00 00 00      2004
                      BE 0B C6 A8      2006
                      BE 0B C6 A8      2008
                      00 00 00 00      200A
                      00 00 00 00      200C
                      00 00 00 00      200E
                      00 00 00 00      2010
                      3E 0B C6 A8      2012
                      3E 0B C6 A8      2014
                      00 00 00 00      2016
                      BF 80 00 00      2018
                      BF 80 00 00      201A
                      BF 80 00 00      201C
                      00 00 00 00      201E
                      42 47 F5 C3      2020
                      63 F0            CRC

(3) Multiple registers from 4000 to 401E

Around every 10 seconds the inverter sends a multiple register read from starting address 4000 for 32 (0020hex) positions = 16 registers

The inverter sends    0B 03 40 00 00 20 51 78
The DDSU666H responds	
                      0B 03 40 
                      C4 EA DB 33 C4 EA DB 33 00 00 00 00 00 00 00 00
                      00 00 00 00 44 E5 D5 71 44 E5 D5 71 00 00 00 00 
                      00 00 00 00 00 00 00 00 45 68 58 52 45 68 58 52 
                      00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                      CE 89

                byte  value			
                00    0B  slave address
                01    03  function code 	0x03 = Read Multiple Holding Registers
                02    40  byte count = 40 hex (64 dec) bytes => 64 / 4 bytes = 16 registers
                      value (4 bytes)  register
                03    C4 EA DB 33      4000
                      C4 EA DB 33      4002
                      00 00 00 00      4004
                      00 00 00 00      4006
                      00 00 00 00      4008
                      44 E5 D5 71      400A
                      44 E5 D5 71      400C
                      00 00 00 00      400E
                      00 00 00 00      4010
                      00 00 00 00      4012
                      45 68 58 52      4014
                      45 68 58 52      4016
                      00 00 00 00      4018
                      00 00 00 00      401A
                      00 00 00 00      401C
                      00 00 00 00      401E
                      CE 89            CRC

DDSU666-H registers map

Observed, not from manufacturer spec.

Register Value Units
2000 voltage Volts (4-bytes floating decimal)
2002 current Amps (4-bytes floating decimal)
2004
2006 active power kW (4-bytes floating decimal)
2008
200A
200C reactive power Kvar (4-bytes floating decimal)
200E
2010
2012 apparent power kW (4-bytes floating decimal)
2014
2016
2018 power factor (4-bytes floating decimal)
201A
201C
201E
2020 frecuency Hz (4-bytes floating decimal)
Register Value Units
4000 Active in electricity W (4-bytes floating decimal)
400A Negative active energy Wh (4-bytes floating decimal)
4014 Positive active energy Wh (4-bytes floating decimal)

Code

The source is in C language.

The development environment is the ESP-IDF. I am running ESP-IDF v5.4-dev-2194-gd7ca8b94c8 for Linux on a Raspberry Pi.

The IP connection is through Wi-Fi. You need to set your SSID and password in the file config.h

// WiFi
#define	WIFI_SSID	    "YOUR_SSID"
#define	WIFI_PASSWORD	"THE_PASSWORD_OF_YOUR_SSID"

The SW is expecting an MQTT broker to publish the measurements. So you need to overwrite the IP address and Port of your MQTT server.

// Mosquitto address
#define MQTT_HOST_IP_ADDR 	"192.168.1.2"
#define MQTT_HOST_IP_PORT 	1883		// default Mosquitto port

And if you want, modify the MQTT device name.

#define DEVICE_MQTT_NAME	"modbus2mqtt"

REST API

Version 2 adds a Rest API interface so that data can be retrieved via MQTT PUBLISH messages or as a WEB service available at <device_ip>:80. To get the information include the following json as payload:

{"type":"data_request","key":"qWpJnwA0crlmgv"}

“key” – is a shared key added for security.

“type” – can be either “data_request” to retrieve the measures or "device_info" to get some perfomance information such WiFi and TCP connection lost count and TCP and MQTT connection status.

You can test the Rest API with CURL as follows:

curl  -X GET http://192.168.1.110:80 -d '{"type":"data_request","key":"qWpJnwA0crlmgv"}'
{"DDSU666H":{"v":"233.50","c":"1.81","ap":"306.90","rp":"-291.40"},"SDM120CT":{"v":"233.20","c":"8.14","ap":"1867.70","rp":"5.30"}}
curl  -X GET http://192.168.1.110:80 -d '{"type":"device_info","key":"qWpJnwA0crlmgv"}'
{"TCP":"ok","MQTT":"ok","WIFIlost":"0","TCPlost":"0"}

The IP address of the device is reported via MQTT in the first PUBLISH message as follows:

{"ip":"192.168.1.110","MAC":"B0:A7:32:27:FF:5C"}

Build

Follow the regular process to generate and flash the code as described in the ESP IDF instructions "Configure Your Projec"

Run ESP-IDF script to make the tools usable from the command line and set the necessary environment variables.

. $HOME/esp/esp-idf/export.sh

Go to your project directory, e.g.

cd ~/esp/modbus2MQTT

Select your target

idf.py set-target esp32

You can skip the menuconfig for the are no project specific variables to set up

idf.py menuconfig

Compile and generate the code

idf.py build

Execute (choose the right port for your enviroment)

idf.py -p /dev/ttyUSB1 flash monitor

Test

Use an MQTT client to cusbscribe to the MQTT topic to receive the PUBLISH messages. You can use the Mosquitto client and the mosquitto_sub command like this:

mosquitto_sub -d -t 'modbus2mqtt/set'

Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: modbus2mqtt/set, QoS: 0, Options: 0x00)
Client null received SUBACK
Subscribed (mid: 1): 0
Client null received PUBLISH (d0, q0, r0, m0, 'modbus2mqtt/set', ... (64 bytes))
{"SDM120CT":{"v":"228.80","c":"0.00","ap":"-0.00","rp":"28.80"}}
Client null received PUBLISH (d0, q0, r0, m0, 'modbus2mqtt/set', ... (67 bytes))
{"DDSU666H":{"v":"228.60","c":"1.99","ap":"363.50","rp":"-272.70"}}

The Mosquitto client is an optional installation. Run the following to install it.

sudo apt install -y mosquitto mosquitto-clients

About

modbus protocol over serial interface to MQTT publish messages

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published