OpenDTU icon indicating copy to clipboard operation
OpenDTU copied to clipboard

Add Modbus

Open b0661 opened this issue 10 months ago • 18 comments

OpenDTU is extended by a Modbus server.

The Modbus server serves TCP at port 502.

  • At Modbus ID 1 the server mimicks the Hoymiles Modus TCP Interface registers in the original DTUPro.
  • At Modbus ID 125 the server serves a SunSpec compatible "total inverter" that provides the OpenDTU aggregated data from all registered inverters.
  • At Modbus ID 243 the server serves a SunSpec power meter that provides AC power and AC yield as if measuring all registered inverters. Same approach as with the "total inverter" but now disguised as a meter.

The webapp is extended by Modbus configuration and info views.

Modbus can be enabled/ disabled. Only a minimal subset of DTUPro/ OpenDTU aggregated data is currently supported.

Main intention was to have an easy feed to my HomeAssistant installation that already monitors and controls my other inverters by Modbus.

modbus:
  ##########################################
  # OpenDTU Hub
  # slave: 1   - Hoymiles DTUPro
  # slave: 125 - OpenDTU Total Inverter (SunSpec)
  - type: tcp
    name: opendtu
    delay: 5
    timeout: 10
    host: 192.168.178.xxx
    port: 502

    sensors:
      - name: "OpenDTU#Total AC Power"
        unique_id: "opendtu_total_ac_power"
        slave: 125
        address: 40192
        input_type: holding
        data_type: float32
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        scan_interval: 10
      - name: "OpenDTU#Total AC Yield Total"
        unique_id: "opendtu_total_ac_yield_total"
        slave: 125
        address: 40202
        input_type: holding
        data_type: float32
        unit_of_measurement: Wh
        device_class: energy
        state_class: total
        scan_interval: 20
      - name: "OpenDTU#Total DC Power"
        unique_id: "opendtu_total_dc_power"
        slave: 125
        address: 40208
        input_type: holding
        data_type: float32
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        scan_interval: 10

The OpenDTU Modbus sources were inspired by : https://github.com/ArekKubacki/OpenDTU. See https://github.com/tbnobody/OpenDTU/pull/582 for the orignal pull request.

The Modbus library used for Modbus communication is: https://github.com/eModbus/eModbus. Documentation for the library is here: https://emodbus.github.io/.

Hints

Get the firmware image

Here are some compiled firmware images:

Unzip the image before flashing to OpenDTU:

  gunzip firmware.xxx.bin.gz

Most firmware images are untested as I do not own the hardware. Reports on working images are welcome.

Build the firmware image

To check out the pull request locally see https://docs.github.com/de/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally.

  git clone https://github.com/tbnobody/OpenDTU 
  cd OpenDTU
  git fetch origin pull/1893/head:pr_modbus
  git switch pr_modbus

Compile following https://www.opendtu.solar/firmware/compile_vscode/ or https://www.opendtu.solar/firmware/compile_cli/.

You do not have to compile the webapp (see https://www.opendtu.solar/firmware/compile_webapp/) as an updated webapp is alread part of this pull request.

Modbus addresses

The SunSpec "total inverter" and SunSpec "power meter" addresses start at 40000. This is an address offset definition, don't subtract 40001 for holding register access. Here is a discussion #2134 covering the address definition.

There is currently no address documentation besides the source code. Please see https://github.com/tbnobody/OpenDTU/pull/1893/files:

  • src/ModbusDtuMeter.cpp
  • src/ModbusDtuTotal.cpp

b0661 avatar Apr 08 '24 10:04 b0661

Very nice work! This would absolutely be something I could use.

Do you think that, expanding on this commit, making the same modbus data also available through modbus RTU would be possible?

OpenDTU Fusion already includes a Renesas ISL3178E TTL to RS485 transceiver, meaning the hardware is already prepared for this. It's just not used yet at the moment.

EDIT: A suggestion I just thought of is, perhaps it would be useful to change variable and function names to MODBUS_TCP? That way, a possible future implementation of MODBUS_RTU is not going to cause conflicts with this functionality.

phol avatar Apr 08 '24 20:04 phol

Very nice work!

Thank you.

Do you think that, expanding on this commit, making the same modbus data also available through modbus RTU would be possible?

eModbus supports RTU also, so it should be possible. My Fusionv2 board is used to monitor my system, so I would be a little bit reluctant to use it for testing Modbus RTU. I will have a look into RTU. Maybe there is a way to keep memory footprint low in case one or both servers are not used.

EDIT: A suggestion I just thought of is, perhaps it would be useful to change variable and function names to MODBUS_TCP? That way, a possible future implementation of MODBUS_RTU is not going to cause conflicts with this functionality.

Good suggestion, will work on that.

b0661 avatar Apr 09 '24 09:04 b0661

I'm very glad that I was an inspiration :) And I'm even more glad that someone took proper care of a problem that I personally didn't have time to deal with.

ArekKubacki avatar Apr 10 '24 09:04 ArekKubacki

Hello, I compiled the program from your project. Unfortunately, I don't have any tabs for Modbus. Something needs to be defined in the code?

ArekKubacki avatar Apr 14 '24 10:04 ArekKubacki

Hello, I compiled the program from your project. Unfortunately, I don't have any tabs for Modbus. Something needs to be defined in the code?

Edit:

You have to build the webapp also, before building the firmware.

Added app.js.gz to the pull request. Now you can build the firmware without building the webapp before.

b0661 avatar Apr 14 '24 12:04 b0661

Added modbus server for minimal SunSpec meter. Only provides AC power and AC yield as if measuring the output of all registered inverters. Same approach as for the "Total Inverter" but now disguised as a meter.

To be used e.g. as a "Fronius Smart Meter TCP" as orginally done in https://github.com/AloisKlingler/OpenDTU-FroniusSM-MB.

b0661 avatar Apr 15 '24 17:04 b0661

Hello, Some quick notes I found :)

  1. If the inverter is not reachable, the reading should behave as selected in the settings/inverter option in openDTU, i.e. either show the old result or 0.
  2. If the inverter is unreachable, Modbus communication does not work on these addresses and throws errors. Maybe it's worth just entering 0 and 1 in the Link Status field?
  3. In the original DTU, if we enter the field of an inverter that is not there, it has the address 00000000000. Thanks to this, many libraries check whether all inverters have already been searched. I think you can do something similar here.

And now the strangest thing when it comes to reading from a real DTU. Generally, real DTU has 8-bit registers, not 16-bit registers like real Modbus... The question is whether it would be worth adding a selection option. Especially if openDTU was to be compatible with various DTU libraries.

ArekKubacki avatar Apr 16 '24 08:04 ArekKubacki

Hello, Some quick notes I found :)

Thx for testing and advise.

1. If the inverter is not reachable, the reading should behave as selected in the settings/inverter option in openDTU, i.e. either show the old result or 0.

Changed now - just takes the values provided by OpenDTU. Does not check for not reachable anymore.

2. If the inverter is unreachable, Modbus communication does not work on these addresses and throws errors. Maybe it's worth just entering 0 and 1 in the Link Status field?

Added an alarm code in case the inverter is unreachable. Can you advise on the link status? I though it is the DTU communication status (not the inverter communication status).

3. In the original DTU, if we enter the field of an inverter that is not there, it has the address 00000000000. Thanks to this, many libraries check whether all inverters have already been searched. I think you can do something similar here.

Changed - Now 0 is provided for all inverter data list registers that are supported by the DTU if they are unused.

And now the strangest thing when it comes to reading from a real DTU. Generally, real DTU has 8-bit registers, not 16-bit registers like real Modbus... The question is whether it would be worth adding a selection option. Especially if openDTU was to be compatible with various DTU libraries.

Changed - adapted to your original code. It is very strange that the start address for the data (0x1000)/ SN (0x2000) list in the documentation seems to be a "normal" Modbus address, but the following fields are per byte.

I tested with your https://github.com/ArekKubacki/Hoymiles-Plant-DTU-Pro app for HomeAssistant. At least the application starts. This was tested without inverters.

By the way during testing I triggered several Python exceptions due to configuration problems. Especially if the number of panels is > 0 and there are no inverters, the app won't start. Fault indication can only be found in the system log and it is somehow misleading as it is an index out of bounds fault.

I don't own a Hoymiles DTU, so your help is very much appreciated.

b0661 avatar Apr 19 '24 15:04 b0661

Hi, Unfortunately, I don't have time to work on it today. But looking at it quickly, something seems to have been moved. I'll look for the problem next week.

[HMSeriesMicroinverterData(data_type=0, serial_number='00000000003c', port_number=17, pv_voltage=Decimal('2494.7'), pv_current=Decimal('165.3'), grid_voltage=Decimal('409.7'), grid_frequency=Decimal('3.96'), pv_power=Decimal('20.1'), today_production=2325, total_production=327551956, temperature=Decimal('0'), operating_status=1384, alarm_code=58, alarm_count=12431, link_status=0, reserved=[107, 0, 3, 0, 0, 0, 0]),

ArekKubacki avatar Apr 20 '24 11:04 ArekKubacki

Hello @ArekKubacki ,

I did some more tests using synthetic test data (fixed values for 1 inverter with 6 channels) for the DTUPro data. The test data can be activated in platformio_override.ini by -DOPENDTU_SIM_DTUPRO:

[env:generic_esp32]
build_flags =
    -DPIOENV=\"$PIOENV\"
    -D_TASK_STD_FUNCTION=1
    -D_TASK_THREAD_SAFE=1
    -Wall -Wextra -Wunused -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference
    -std=c++17
    -std=gnu++17
    -DLOG_LEVEL=LOG_LEVEL_DEBUG
    -DOPENDTU_SIM_DTUPRO

I used this HomeAssistant configuration.yaml

sensor:
  ### Test ###
  - platform: hoymiles_dtu
    host: 192.168.178.xxx
    name: Hoymiles PV
    dtu_type: 0
    monitored_conditions:
      - "pv_power"
      - "today_production"
      - "total_production"
      - "alarm_flag"
    monitored_conditions_pv:
      - "pv_power"
      - "today_production"
      - "total_production"
      - "pv_voltage"
      - "pv_current"
      - "grid_voltage"
      - "temperature"
      - "operating_status"
      - "alarm_code"
      - "alarm_count"
      - "link_status"
    panels: 3

As a result HomeAssistant shows the following. Test data seems to be correctly displayed. Screenshot_20240421_153844 Screenshot_20240421_153947

I this the expected output?

b0661 avatar Apr 21 '24 13:04 b0661

Unfortunately not yet. HMSeriesMicroinverterData(data_type=60, serial_number='xxx', port_number=1, pv_voltage=Decimal('37.1'), pv_current=Decimal('2.71'), grid_voltage=Decimal('232.2'), grid_frequency=Decimal('50.03') ), pv_power=Decimal('384.1'), today_production=5356, total_production=3837759

today_production, total_production, pv_power, pv_current, pv_voltage should be for each port (string, panel), not for the entire inverter.

port_number should change depending on the number of ports on the inverter. In my case 1-4. It is currently growing to 24, which is the maximum panels I have.

ArekKubacki avatar Apr 25 '24 12:04 ArekKubacki

Thank youu for testing. Will look into this. I hopefully can now understand your original code.

b0661 avatar Apr 28 '24 08:04 b0661

Hello @ArekKubacki

today_production, total_production, pv_power, pv_current, pv_voltage should be for each port (string, panel), not for the entire inverter.

Now switched to values per string.

port_number should change depending on the number of ports on the inverter. In my case 1-4. It is currently growing to 24, which is the maximum panels I have.

Reported port numbers adapted to per inverter.

Would be nice if you could test this version. I tried to make a one to one copy of your code.

What is still open to me is the first byte (data type) at 0x1000. According to the specification it shall be 0x3C, you are using 0x0C. Do you know more? What does the DTUPro use here?

Thanks again for testing.

b0661 avatar May 07 '24 19:05 b0661

Looks very good. As far as register 1 is concerned, it does not matter. According to the specifications, it is 3C in the original DTU, at least in my case it is 0C. It is not used for anything so I think you can leave it as per specifications.

Have you thought about making 2 choices? How is "DTU Clone" and how it should be according to the specification with normal 16-bit registers? This will give users more space to use.

ArekKubacki avatar May 10 '24 09:05 ArekKubacki

Sorry for confusion. Somehow this happened during testing in VSCode.

fabwal avatar Jun 25 '24 11:06 fabwal

@b0661 Bobby there is a request for Modbus RTU in this discussion: https://github.com/tbnobody/OpenDTU/discussions/2127

I do not know if this would be a rather complicated change, as the register addresses (4.3 vs. 5.3 Modbus Registers) are mostly the same, though the messages commands / functions (4.2 Modbus RTU Functions vs. 5.2 Modbus TCP Functions) are slightly different, for reason of RTU vs. TCP transport schemes.

Also note that your PR may be accepted into OpenDTU-OnBattery rather than OpenDTU, as far as I understood @tbnobody But then again Modbus / Sunspec support should be in the mainline IMHO just like in the Hoymiles DTU Pro / Pro S. If I read the Hoymiles data sheet correctly, it supports up to 100 ports / MPPT-channels, i.e. a max. of 25x 4-in-1, 50x 2-in-1 or 100x 1-in-1 MI/HM/HMS/HMT models.

BTW: If you need clarification on some of the registers we may have a look into the gitee.com code drop of the original DTU Pro Source Code. It definitely contains the original Modbus & Sunspec implementations from Hoymiles.

Technical-Note-Modbus-implementation-using-3Gen-DTU-Pro-V1.2.pdf

Modbus TCP Settings

5 Hoymiles Modus TCP Interface and Registers

This section describes the register mapping for the monitoring data and remote control using Hoymiles Modbus TCP protocol on the Ethernet interface.

5.1 Modbus TCP Settings

In the Modbus TCP protocol, the DTU-Pro will work as the slave and receive the control from the third-party monitoring device. Usually users can directly connect the ethernet cable to the ethernet port to use Hoymiles Modbus protocol. The default port number of Modbus TCP is 502 and can be changed according to the protocol in this chapter. The IP address can be obtained in the network device, such as the router.

5.2 Modbus TCP Functions

The Modbus protocol is a protocol, with only one master and multiple slaves. If the DTU is acting as a slave device, it will respond only when queried. In most of the case, your Modbus software automatically uses the correct Modbus command for any action you wish to perform, but the Hoymiles DTU-Pro only supports the followings:

  • 01 (0x01) Read Single Device Status

DTU or microinverter status registers can be read and written, and are intended for configuration values, so you can use functions 01 or 02 to read any registers.

  • 02 (0x02) Read Multiple Device Status

DTU or microinverter status registers can be read and written, and are intended for configuration values, so you can use functions 01 or 02 to read any registers.

  • 03 (0x03) Read Device Data

DTU or microinverter data registers are generally read-only and report voltage, current, power, energy, and related values.

  • 05 (0x05) Write Single/All Device Status

This writes a new value to a single or all DTU or microinverter status registers.

  • 15 (0x0F) Write Multiple Device Status

This writes a new value to multiple DTU or microinverter status registers. Please refer to the next section for more details.

5.2.1 Read Single Device Status
  • [x] Command sending format
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x01
Starting Address 2 Big-Endian
No. of Registers 2 0x0001 Big-Endian
  • [x] Command response format (if success)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x01
Data length 1 0x02
Data 2 Big-Endian
  • [x] Command response format (if failed)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x81
Error Data Code 1 0x01
5.2.2 Read Multiple Device Status
  • [x] Command sending format
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x02
Starting Address 2 Big-Endian
No. of Registers 2 Big-Endian
  • [x] Command response format (if success)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x02
Data length 1
Data 1 2 Big-Endian
Data 2 2 Big-Endian
...
  • [x] Command response format (if failed)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x82
Error Data Code 1 0x01
5.2.3 Read Device Data
  • [x] Command sending format
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x03
Address Code 2 Big-Endian
No. of Registers 2 Big-Endian
  • [x] Command response format (if success)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x03
Data length 1
Data 1 2 Big-Endian
Data 2 2 Big-Endian
...
  • [x] Command response format (if failed)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x83
Error Data Code 1 0x01
5.2.4 Write Single/All Device Status
  • [x] Command sending format
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x05
Address Code 2 Big-Endian
No. of Registers 2 Big-Endian
  • [x] Command response format (if success)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x05
Address Code 2 Big-Endian
Data 2 Big-Endian
  • [x] Command response format (if failed)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x85
Error Data Code 1 0x01
5.2.5 Write Multiple Microinverters Status
  • [ ] Command sending format
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x0F
Address Code 2 Big-Endian
No. of Registers 2 Big-Endian
Data length 1
Data 1 1
Data 2 1
...
  • [x] Command response format (if success)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x0F
Address Code 2 Big-Endian
No. of Registers 2 Big-Endian
  • [x] Command response format (if failed)
Name Length Value Remark
Header Transaction ID 2 Big-Endian
Protocol ID 2 Big-Endian
Length 2 Big-Endian
Unit ID 1
Modbus Data Function Code 1 0x8F
Error Data Code 1 0x01

5.3 Modbus Registers

5.3.1 Microinverter Status Register List

The following registers provide a microinverter status register list, which can be both read and written.

Registers Name R/W? Function Code (Supported) Remark
0xC000 Turn ON/OFF (All Microinverters) W 0x05 0: OFF, 1: ON
0xC001 Limit Active Power (All Microinverters) W 0x05 Percentage 2~100 for HM series, 10~100 for MI series
0xC002 Reserved W 0x05
0xC003 Reserved W 0x05
0xC004 Reserved W 0x05
0xC005 Reserved W 0x05
0xC006 Turn ON/OFF (Port 1) R/W 0x01\0x02\0x05\0x0F 0: OFF, 1: ON
0xC007 Limit Active Power (Port 1) R/W 0x01\0x02\0x05\0x0F Percentage 2~100 for HM series, 10~100 for MI series
0xC008 Reserved R/W 0x01\0x02\0x05\0x0F
0xC009 Reserved R/W 0x01\0x02\0x05\0x0F
0xC00A Reserved R/W 0x01\0x02\0x05\0x0F
0xC00B Reserved R/W 0x01\0x02\0x05\0x0F
0xC00C Turn ON/OFF (Port 2) R/W 0x01\0x02\0x05\0x0F 0: OFF, 1: ON
0xC00D Limit Active Power (Port 2) R/W 0x01\0x02\0x05\0x0F Percentage 2~100 for HM series, 10~100 for MI series
0xC00E Reserved R/W 0x01\0x02\0x05\0x0F
0xC00F Reserved R/W 0x01\0x02\0x05\0x0F
0xC000 Reserved R/W 0x01\0x02\0x05\0x0F
0xC001 Reserved R/W 0x01\0x02\0x05\0x0F
0xC002 Turn ON/OFF (Port 3) R/W 0x01\0x02\0x05\0x0F 0: OFF, 1: ON
0xC003 Limit Active Power (Port 3) R/W 0x01\0x02\0x05\0x0F Percentage 2~100 for HM series, 10~100 for MI series
0xC004 Reserved R/W 0x01\0x02\0x05\0x0F
0xC005 Reserved R/W 0x01\0x02\0x05\0x0F
0xC006 Reserved R/W 0x01\0x02\0x05\0x0F
0xC007 Reserved R/W 0x01\0x02\0x05\0x0F
... (Maximum 99 ports) ...
0x9D9C Turn ON/OFF (All Microinverters) W 0x05 0: OFF, 1: ON
0x9D9D Limit Active Power (All Microinverters) W 0x05 Percentage 2~100 for HM series, 10~100 for MI series
0x9D9E Reserved W 0x05
0x9D9F Reserved W 0x05
0x9DA0 Reserved W 0x05
0x9DA1 Reserved W 0x05

Note:

  1. 4 ports for 4 in 1 microinverters, 2 ports for 2 in 1 microinverter and 1 port for 1 in 1 microinverter.
  2. For 4 in 1 and 2 in 1 microinverters, control on all ports in one microinverter should be same.
5.3.2 Microinverter Data Register List

The following registers provide a microinverter data register list, which can be read-only with the function code 0x03.

Registers Name Decimal Units Remark
0x1000 Data Type / / Default, 0x3C
0x1001 / /
0x1002 Microinverter SN 12-digit decimal number Big-Endian For example, 116151200012
0x1003 Microinverter SN 12-digit decimal number Big-Endian For example, 116151200012
0x1004 Microinverter SN 12-digit decimal number Big-Endian For example, 116151200012
0x1005 Microinverter SN 12-digit decimal number Big-Endian For example, 116151200012
0x1006 Microinverter SN 12-digit decimal number Big-Endian For example, 116151200012
0x1007 Port Number / /
0x1008 PV Voltage 1 V
0x1009 PV Voltage 1 V
0x100A PV Current 1/2 A 1 for MI Series, 2 for HM Series
0x100B
0x100C Grid Voltage 1 V
0x100D
0x100E Grid frequency 2 Hz
0x100F
0x1010 PV Power 1 W
0x1011
0x1012 Today Production / Wh
0x1013
0x1014 Total Production / Wh
0x1015
0x1016
0x1017
0x1018 Temperature 1 Microinverter internal temperature
0x1019
0x101A Operating Status / /
0x101B
0x101C Alarm Code / /
0x101D
0x101E Alarm Count / /
0x101F
0x1020 Link Status / / Communication status with DTU
0x1021 / / / Fixed, 0x07
0x1022 Reserved / /
0x1023 Reserved / /
0x1024 Reserved / /
0x1025 Reserved / /
0x1026 Reserved / /
0x1027 Reserved / /
0x1028 Data Type / / Default, 0x3C
0x1029 Microinverter SN / / 12-digit decimal number
0x102A
0x102B
0x102C For example, 1161512000120x102D
0x102D
0x102E
0x102F Port Number / /
0x1030 PV Voltage 1 V
0x1031
0x1032 PV Current 1/2 A 1 for MI Series, 2 for HM Series
0x1033
0x1034 Grid Voltage 1 V
0x1035
0x1036 Grid frequency 2 Hz
0x1037
0x1038 PV Power 1 W
0x1039
0x103A Today Production / Wh
0x103B
0x103C Total Production / Wh
0x103D
0x103E
0x103F
0x1040 Temperature 1 Microinverter internal temperature
0x1041
0x1042 Operating Status / /
0x1043
0x1044 Alarm Code / /
0x1045
0x1046 Alarm Count / /
0x1047
0x1048 Link Status / / Communication status with DTU
0x1049 / / / Fixed, 0x07
0x104A Reserved / /
0x104B Reserved / /
0x104C Reserved / /
0x104D Reserved / /
0x104E Reserved / /
0x104F Reserved / /
... (Maximum 99 ports)
5.3.3 Device SN Register List

The following registers provide the device serial number register list, which can be read and written.

Registers Name Decimal Units R/W? Function Code (Supported) Remark
0x2000 DTU SN R 0x03
0x2001
0x2002
0x2003
0x2004 / /
0x2005
0x2056 Microinverter SN / / R/W 0x03/0x0F
0x2057
0x2058
0x2059
0x205A
0x205B
0x205C Microinverter SN / / R/W 0x03/0x0F
0x205D
0x205E
0x205F
0x2060
0x2061
... (Maximum 99 ports for one DTU)

Note:

  1. 4 ports for 4 in 1 microinverters, 2 ports for 2 in 1 microinverter and 1 port for 1 in 1 microinverter.
5.3.4 DTU Ethernet Setting Register List

The following registers provide DTU ethernet setting register list, which can be read and written.

Registers Name Decimal Units R/W? Function Code (Supported) Remark
0x2501 Ethernet Port Number / / R/W 0x03/0x0F Default: 502
0x2502

Note: When the port number is changed, please restart the DTU.

stefan123t avatar Jul 08 '24 21:07 stefan123t

Hello @stefan123t,

the current Sunspec Modbus implementation is a minimalistic one. Just enough Sunspec Modbus to suite the use case of getting the total power and energy.

I did not think of a full Sunspec Modbus clone of DTU Pro. I'm happy to implement more Sunspec models if there are concrete requests and testers. I only have one inverter in production use and I do not own a DTU Pro - so my test scenario is very limited.

Also feel free to provide pull requests.

The Hoymiles Modbus implementation you mentioned is already provided (addresses 0x1xxx, 0x2xxx) . Not to the full extend, but enough to suffice @ArekKubacki 's home assistant integration.

Thank you for mentioning #2127. I answered in #2134.

Bobby

b0661 avatar Jul 10 '24 06:07 b0661

Adding #553 and #1146 as references / dependencies.

stefan123t avatar Jul 10 '24 21:07 stefan123t

@b0661 your PR completely superseeds the PR #582 from @ArekKubacki or is there a delta left ?

@schlimmchen is this something that would somehow better fit into OpenDTU-OnBattery or do you think @tbnobody would be willing to add Modbus and DPL support into OpenDTU itself ?

stefan123t avatar Sep 30 '24 21:09 stefan123t

The title is "Add Modbus", and there is a similar PR in the downstream project, which also lacks sufficient motivation to merge such a big feature. In case of this upstream project, I assume the amount of flash consumed is so considerably high that it warrants not merging it. This is only a guess based on the size of the changeset. For the downstream project, as I said, I don't understand why this is important and I wont't research it myself. I also don't understand why such a huge changeset should be necessary for what seems to me like only a small improvement/feature for a small audience. So, my honest opinion is currently, that this bridge fits best on an some sort of external third-party component.

schlimmchen avatar Oct 01 '24 04:10 schlimmchen

@stefan123t, this pull request supersedes PR https://github.com/tbnobody/OpenDTU/pull/582. @ArekKubacki tested it and it works with his HomeAssistant integration for Hoymiles DTU Pro.

@schlimmchen, I´m perfectly fine if this pull requests will never be merged into the main branch. I created it to give something back to the OpenDTU community. Nevertheless I would really appreciate if you refrain from wild guessing without a sound analysis of the change (large changeset, small improvement/feature, small audience). In my world open source is fun work to give something to the people. If it helps somebody else I'm happy. I don't do a business in need of big features for a big audience.

For me Modbus is easier than MQTT. I don't need an MQTT server. I get actual data when I pull and do not have to wait for an MQTT update. The data set is standardised, either by Hoymiles or by Sunspec. It fits to my other "big" inverters that are all running on Modbus.

Cheers Bobby

b0661 avatar Oct 06 '24 07:10 b0661

@b0661 I think you may explain a bit better what this Modbus implementation can be used for.

For me this is a feature that was requested since early in the projects Ahoy and OpenDTU both had plans to support Modbus protocol for either reading a Smartmeter and/or giving access to the DTU for others via Modbus / Sunspec.

Actually the Hoymiles DTU (Pro ?) also supports the Sunspec protocol, hence it somehow should fit into our code without breaking the code size limits.

If you need a copy of the original code for reference let me know, maybe I even have a version with translated comments ?

@schlimmchen I understand your hesitation to merge new code into mainline / downstream projects perfectly fine, because not all functionality is needed by all / many users.

I do not think that this changeset introduces a overly large footprint of code in binary form but that is something that can be checked best after compilation.

On the other hand I also understand the reservation in this and the downstream project which just switched from the 4MB -> 8MB recommendation of flash size for the ESP32S3 MCU.

Maybe it is going to be necessary / useful to start thinking of a kind of plugin system, which allows to compile in certain distinct feature sets and use a wizard like in Tasmota to choose the build time flags wanted by the user ?

I had the same idea when looking at syslog feature, but also some of the OpenDTU-OnBattery features like Dynamic Power Limit or Power Meter reading which should be implemented as plugins / options in the upstream project IMHO.

stefan123t avatar Oct 06 '24 19:10 stefan123t