OpenDTU icon indicating copy to clipboard operation
OpenDTU copied to clipboard

Added Modbus to OpenDTU

Open ArekKubacki opened this issue 2 years ago • 37 comments

Added Modbus TCP/IP

ArekKubacki avatar Feb 13 '23 10:02 ArekKubacki

Hello, Any idea why there is error? In VisualStudio Code there is not any error.

ArekKubacki avatar Feb 27 '23 13:02 ArekKubacki

Ist die Modbus unterstützung dann auch für das OLIMEX Modul gedacht? also man kann dann direkt per LAN damit arbeiten?

PhiDiWoe avatar Mar 04 '23 21:03 PhiDiWoe

the build fails as it can't find the "ModbusDTU.h" file you include

horfic avatar Mar 08 '23 15:03 horfic

For my understanding: will this PR provide the capability to read the Hoymiles power/energy from e.g. a Kostal Smart Energy Meter? -- Or is there another use case behind?

mzurhorst avatar May 01 '23 15:05 mzurhorst

This change allows you to read data from OpenDTU over Modbus. It runs parallel to MQTT.

ArekKubacki avatar Jun 05 '23 09:06 ArekKubacki

@ArekKubacki can you please offer me a compiled binary? I would like to try out if this can be added at fronius gen24 as "Power Meter"

AloisKlingler avatar Jun 07 '23 05:06 AloisKlingler

firmware.zip

If you need Modbus registers, see here

https://github.com/ArekKubacki/OpenDTU/blob/master/src/ModbusDtu.cpp

ArekKubacki avatar Jun 07 '23 05:06 ArekKubacki

to get sure: This would act as Modbus TCP (port 502 on OpenDTU-IP) or as Modbus RTU ? is there additional config necessary? I tried to read registers 40072 to 40193 - I think this would be sunspec, but not sure at all - but nothing there. the registers you mentioned are maybe (decimal) 4096 to 4115 ?

thanks. :-)

AloisKlingler avatar Jun 07 '23 07:06 AloisKlingler

As TCP, port 502. No additional config necessary.

If you need Modbus registers, see here

https://github.com/ArekKubacki/OpenDTU/blob/master/src/ModbusDtu.cpp

ArekKubacki avatar Jun 07 '23 08:06 ArekKubacki

the good thing is: something works. the bad thing is: I have no plan and knowledge to handle the data.

I can see it is not in the format which the fronius inverter would require. it would like to communicate with a "fronius smart meter" which has (like kostal seem to have in registers 8192 and upwards) vendor specific identification in registers 40001 and upwards. see some code here: https://github.com/americanium/fronius_sm_simulator/blob/a3ce3f847d0bddc48378f469ed677cf376da1fd1/bin/frsmsimulator.py#LL323C9-L323C14

out of interest: you chose modbus registers 4096 and above (at least there I get some data with node red, which I cannot interpret). is there a reason for not chosing sunspec defined one? would be (I think) 40071 and above for the data, or 40000 and above for the manufacurer specific data (like fronius has). reference for sunspec I found: https://www.kostal-solar-electric.com/en-gb/products/accessories/smart-energy-meter/-/media/document-library-folder---kse/2020/12/15/14/22/ba_kostal_interface_ksem_de.pdf page 9

the idea behind: e.g. fronius gen24 inverters offers to add "production meters" so it is able to calculate correctly the current power usage of your entire house (as soon as you add another inverter, the gen24 does not know it's production and therefore it does not know how much power you use currently in your house). the inverter powermeter(fronius smart meter) you could add is about 350€ - and additional cable needed.

so, if your implementation would follow the sunspec registers, and would offer to announce itself as "Fronius Smart Meter" it most probably could just be added ad Modbus TCP and be used. 👍 🥇

AloisKlingler avatar Jun 07 '23 08:06 AloisKlingler

Selected registers are the same as those in the original DTU and adapted to libraries reading data from the original DTU. Unfortunately, they implemented 8-bit modbus there, which hangs all the time. Add-on designed to work with Home Assistance and add-on https://github.com/ArekKubacki/Hoymiles-Plant-DTU-Pro

When it comes to Sunspec, user should select the operating mode in the original DTU, and it should be probably the same in the case of OpenDTU. Currently, this is a redundant function for me, so adding it would have to wait.

ArekKubacki avatar Jun 07 '23 09:06 ArekKubacki

trying on myself leads to: [{ "resource": "/d:/Profile/Documents/VSCode/OpenDTU/src/ModbusDtu.cpp", "owner": "C/C++: IntelliSense", "code": "1696", "severity": 8, "message": "cannot open source file "Arduino.h" (dependency of "ModbusDtu.h")", "source": "C/C++", "startLineNumber": 1, "startColumn": 1, "endLineNumber": 1, "endColumn": 23 },{ "resource": "/d:/Profile/Documents/VSCode/OpenDTU/src/ModbusDtu.cpp", "owner": "C/C++: IntelliSense", "code": "1696", "severity": 8, "message": "#include errors detected. Please update your includePath. Squiggles are disabled for this translation unit (D:\Profile\Documents\VSCode\OpenDTU\src\ModbusDtu.cpp).", "source": "C/C++", "startLineNumber": 1, "startColumn": 1, "endLineNumber": 1, "endColumn": 23 }]

the file itself is there - I think there are something rudimentary missing - a fresh installed vscode with your repo checked out like described in the wikipage from the project do you have any hints for me? Thanks. :-)

AloisKlingler avatar Jun 07 '23 14:06 AloisKlingler

Are you using PlatformIO?

ArekKubacki avatar Jun 08 '23 08:06 ArekKubacki

Are you using PlatformIO?

yes. fixed it by myself, this was most probably a (by whatever reason) not-complete installed PlatformIO extension in vscode. 🤦

AloisKlingler avatar Jun 08 '23 09:06 AloisKlingler

@ArekKubacki can you please help me out with two values I would need?

registers 0x1038 and 0x1039 (current inverter PV Power) and registers 0x103C 0x103D 0x103E 0x103F (inverter Total Production)

I do not know which variables to use there and in case of total production how to split the values in 4 registers

so e.g. the things you did here: b.Hreg(chan* 20 + 0x100A, ((uint16_t)(((uint32_t)(inv->Statistics()->getChannelFieldValue(t, c, FLD_YT)1000)) >> 16)) & 0xFFFF); mb.Hreg(chan 20 + 0x100B, (((uint16_t)(inv->Statistics()->getChannelFieldValue(t, c, FLD_YT)*1000))) & 0xFFFF);

where FLD_YT would need the currently overall inverter PV power

and the same for interter total production splittet to 4 registers instead of two

thank you so much :-)

AloisKlingler avatar Jun 08 '23 09:06 AloisKlingler

There you have ready to use library: https://github.com/ArekKubacki/Hoymiles-Plant-DTU-Pro/tree/main/custom_components/hoymiles_dtu/hoymiles

in python plant_data = HoymilesModbusTCP('192.168.1.xx', port=502, microinverter_type=MicroinverterType.HM, unit_id=modbusID, dtu_type=1).plant_data

ArekKubacki avatar Jun 08 '23 10:06 ArekKubacki

I am not in python, I am in ModbusDtu.cpp I adding there registers to match the fronius smart meter stuff in reference to https://www.shinetech-power.de/wp-content/uploads/2021/07/Technical-Note-Modbus-implementation-using-3Gen-DTU-Pro-V1.2.pdf

and the only "dynamically" written register I need would match to 0x103C 0x103D 0x103E 0x103F (Total Production) and 0x1038 and 0x1039 (current inverter PV Power)

in fronius smartmeter emulation the registers 9CA1 needs to be filled with current inverter power and 9CC1 and 9CC1 for total production.

but I do not know which opendtu variables (like getChannelFieldValue(t, c, FLD_UDC) ) need to be used and how to split the values accordingly to two registers

AloisKlingler avatar Jun 08 '23 10:06 AloisKlingler

Fornius 9CA1 = 0x1008 For power 0x100A and 0x100B

Remember, however, that if you have more than one microinverter, the addresses jump every 20. If you want to send all the power and all the production, you will not be able to do without an additional script.

ArekKubacki avatar Jun 08 '23 10:06 ArekKubacki

thank you I could now write to the correct registers, but now I am stuck with data types (again):

e.g. the original smartmeter uses two registers to reflect the "current inverter power". in nodejs, on reading the buffers, this looks like (also with values parsed from smartmeter below): parseFloat(msg.payload.buffer.readFloatBE(52,53,54,55).toFixed(0)) where 52 and 53 are values "c5f4" from modbus register 0x9ca1 and 54 and 55 are values "e800" from modbus register 0x9ca2

the "c5f4e800" was in this case converted to "-7837"

how do I get now from (uint16_t)(inv->Statistics()->getChannelFieldValue(t, c, FLD_PDC)*10) a) to a negative value - should be only multiplied by "-10" instead of 10, should it b) furthermore to the BE float using two registers instead of one

thank you! :-)

AloisKlingler avatar Jun 09 '23 06:06 AloisKlingler

have it. this does the trick ... (also inverter totals used) :-) float CurrPWR = (Datastore.getTotalAcPowerEnabled()*-1); uint16_t *myhex = (uint16_t *)&CurrPWR; mb.Hreg(0x9ca1, myhex[1]); mb.Hreg(0x9ca2, myhex[0]); float TotYLD = (Datastore.getTotalAcYieldTotalEnabled()*1000); uint16_t *myhex2 = (uint16_t *)&TotYLD; mb.Hreg(0x9cc1, myhex2[1]); mb.Hreg(0x9cc2, myhex2[0]);

AloisKlingler avatar Jun 09 '23 15:06 AloisKlingler

success 🥳 image

AloisKlingler avatar Jun 09 '23 15:06 AloisKlingler

is there a reason for not merging this PR into OpenDTU?

AloisKlingler avatar Jun 21 '23 09:06 AloisKlingler

firmware.zip

If you need Modbus registers, see here

https://github.com/ArekKubacki/OpenDTU/blob/master/src/ModbusDtu.cpp

I tried your precompiled firmware to use Modbus with iobroker. But everything i tried failed. If I try to read a register, for example 4096 I only get an error "illegal data address". Is Modbus provided in the firmware you posted above? If not, is there a ready-to-flash firmware available?

MIC1981 avatar Jun 30 '23 19:06 MIC1981

Any chance that this will be implemented in the master branch?

fantilator avatar Jul 07 '23 20:07 fantilator

@AloisKlingler @fantilator @ArekKubacki

is there a reason for not merging this PR into OpenDTU?

IMHO yes.

  • The commits mess around with libraries (I assume a change in CR/LF cause by an editor).
  • Every new feature which is not essentially necessary should be able to be turned on/off. At least at runtime, and if it consume a lot of codespace then it should be configurable at compile time also.
  • Pull requests should look "nice" in the history of the main project when they get merged. So if this would be my project, I would not merge a PR with 22 commits and half of it are "fix this" "fix that" "merge...". I recommend a rebase of the pull request.

This is just my personal opinion, I am not the project owner.

madmartin avatar Jul 18 '23 07:07 madmartin

Hello, I am new here,

@AloisKlingler is it possible that I get the AC_Power in an Register?

mschimpfhuber avatar Aug 18 '23 06:08 mschimpfhuber

Unfortunately, only the DC channels are provided. I would have been more interested in the AC channel.

heppth avatar Oct 30 '23 10:10 heppth

@ArekKubacki have you considered / asked to add your PR/changes to the OpenDTU-onBattery fork from Helge Erbe ?

I know Helge has included a couple of plugins to retrieve Power Meter readings via MQTT, so maybe adding ModBus support there might be easier and straight forward ?

https://github.com/helgeerbe/OpenDTU-OnBattery

stefan123t avatar Oct 30 '23 10:10 stefan123t

I think that one day I will come back to mastering Modbus and creating a subpage where I can set everything up. How closely does this version follow the original OpenDTU?

ArekKubacki avatar Nov 06 '23 07:11 ArekKubacki

@ArekKubacki I have not checked what your PR actually does, but OpenDTU-onBattery has a PowerMeter.cpp class which has support for several PowerMeter Sources, including HTTP, MQTT, SML and something called SOURCE_SDM1PH/3PH. As far as I know the SDM Smart Meters are read via ModBus / RS485 too ?

https://github.com/helgeerbe/OpenDTU-OnBattery/blob/development/src/PowerMeter.cpp

Regarding the fork Helge seems to be rather quick in merging changes which are inteoduced by Thomas in upstream OpenDTU. I can not remember any delays in the past year. So thanks to the great jobs of both project maintainers this fork is really well maintained !

stefan123t avatar Nov 06 '23 20:11 stefan123t