sHedC/python-masterthermconnect

Add support for local Modbus TCP

Opened this issue · 25 comments

So I discovered that in my MasterTherm installation I have access to instant local data via the Modbus TCP protocol. This is open and does not require and authentication or additional hardware.

Hardware Requirements

I have a MasterTherm geo heatpump installed in 2022 which came with a touchscreen display in the living area:
image

The screen used is by CAREL called pGDX (https://www.carel.com/pgdx). The screen connects to my WiFi and I can connect to its Modbus via TCP port 502.

Testing

To test if it works, download the free Open ModScan on Windows (https://github.com/sanny32/OpenModScan/releases) and go to Connection -> Connect. Choose 'Remote TCP/IP Server' and enter your Touchscreen's IP Address. Leave the port at 502 and click OK. Now leave the address and device id at 1. Choose 03: HOLDING REGISTER under 'MODBUS Point Type'. Make sure your data display option is set to integer:
image
It's working if you see data listed in the registers:
image

Home Assistant

To add this to Home Assistant, I suggest adding support for Modbus TCP to Masterthermconnect.

  • There is no module ID or device ID
  • There is no email/password needed

The MasterTherm integration would need to have its settings UI changed. All that's needed is the option to choose between 'MasterTherm Cloud' and 'Modbus TCP'. Then for Modbus users only need to enter an IP address.

Modbus has different types of registers you can read and sometimes write to:
0x = Coil = 00001-09999
1x = Discrete Input = 10001-19999
3x = Input Register = 30001-39999
4x = Holding Register = 40001-49999

We are only interested in the holding registers with address range 40001-49999. All the other registers are not used for this application.

Addresses

Since the first two address locations 40001 and 40002 are used for something else, everything is shifted by 2 when compared to our current implementation. Also, all the data types are one after the other.

Everything is stored as a signed integer.

Analog
A_1 -> 40003
A_2 -> 40004
A_500 -> 40502

All the above need to be divided by 10. For example a temperature of 17.1 is stored as 171.

Digital
D_1 -> 40503
D_2 -> 40504
D_496 -> 40998
Integers
I_1 -> 40999
I_2 -> 41000
I_500 -> 41498

I think the easiest is to use PyModbus. See Solaredge Modbus for an example implementation.

Jealous, mine is locked with password and cert auth.

I will check this out as we discussed to try and make this easier I think map the numbers to the I_1 etc so I can map directly into and from the online mappings, then hopfully only need to replace the API part with a modbusAPI.

Do you think maybe login using the Web to get the setup then in options allow local connection and (hoping I find another way) have an option on type of connection for each device, once enabled it will switch the data updates to local for that specific device.

This probably doesn't matter for most people but just thinking if you have multiple devices to monitor (large houses), still that is a bit off have to get a connection which is going to be interesting as I can't test.

I think I will need you to share me sample data for Unit testing, once I work out how.

This actually does work on mine to but in my case A_2 is 4002 and A_5 is 4006

Oh really? You also have access to modbus TCP then?

seems like it I pointed that app at it and it started updating, so need to work out if there really is a difference in where yours starts and where mine starts

I changed the requested temp to 48.8 and it changed 4006 to 488, now the hot water is requesting 46.0 and its 460

40038 and 40039 are A_37 and A_38 on mine

Weird seems not to be mapped to the same as yours:

D_1 start at 1002 (02 - Input status)
A_1 start at 4002 (03 - Holding Register) also at (04 Input Register)
I_1 start at (No Idea)

Well this is great news! This opens up local access to (nearly) everyone. We just need to allow for these different mapping types. Mine would be 'Carel pGDX' and yours would be whatever Gateway or Mastertherm you have. We'll have it selectable.

yeh just knocked my pump offline :), but can still talk to it with pymodbus.

Coils are 1-bit registers, read/write
Input status are 1-bit registers, read-only.
Input register are 16-bit registers, read-only
Holding register are 16-bit registers read/write

Can you share a pic of your holding registers?

image
image

For the A Registers there are from A_1 to A_524

Okay well this is great. Looks like there is a shift. We'll need at least two mappings then. But this means we can make the integration fully local (local polling) instead of cloud-dependent! Will make testing a lot easier for you with your own heatpump!

So have written a small piece of code that reads the coils and registers (in my case) and converts to A and D Registers. Takes about 1.2s.

Can't work out the I Registers where they are have looked through the registers to 9000, grrrr

Here's another trick, maybe it will help to find the I registers.

I_12 contains the compressor start counter. First I checked in Home Assistant, my current value is 166. Then do a search, set everything up as follows:
image

After exporting, you get a PDF. Now search the PDF for '166'. In my case, I got the register:
image

Of course you can increase the length from 2000 to much higher. And you can also change from HOLDING REGISTER to something else.

Let me know if you manage!

Perfect thank you:
D Registers are in the read_coil from value 0 to 524 (I think)
A Registers are in read_holding_registers from 1 to 525
I Registers are in read_holding_registers from 5001 to 5525

Not sure about the end range need to check the register manual its about 524 for each range.

Can you check the Coil values see if they fit the D Registers? you say that your D registers start at 40503 however there are about 524 A registers so that should not fit as they start at 40003.

ok so first pass with rough code, locally takes 1.9s to retrieve the registers. using online full run takes 1.356 seconds. but will see as my code is grabbing the registers 100 at a time (modbus limitation) and then converting one at a time into registers in a dict so A_1 = etc.

So not so quick, still even if takes 2 seconds it can be pinged every 5 seconds no issues.

Do I understand correctly, that you are connecting to Modbus TCP on the pGDX?
In that case it sounds like I am out of luck as I have too old HP, which doesn't have pGDX.
I already tried connecting to Modbus TCP on the HP itself in the past and I am pretty sure it is not working. The 502 port is open, but refusing connections...

That's correct, I'm connecting to the pGdx, but @sHedC is connecting to his heatpump. Try using the program I suggested above? Does it work for you to read registers?

Do I understand correctly, that you are connecting to Modbus TCP on the pGDX? In that case it sounds like I am out of luck as I have too old HP, which doesn't have pGDX. I already tried connecting to Modbus TCP on the HP itself in the past and I am pretty sure it is not working. The 502 port is open, but refusing connections...

the controller in my heat pump reports as a pco5 which is the carel pco5 controller it supports modbus. I tried to connect to it but nothing then used the app that SebsZ suggested and it gave me registers. What controller do you have reported and in the HP itself?

The Carel PC1 to PC5 plus a few others support modbus with the RJ45 adatper.

Oh, you're right. It works for me as well!
Then I maybe did something wrong when I tried it few years ago...

So what's next? How can I help? Is there some alpha version to try and report the results?

Oh, you're right. It works for me as well! Then I maybe did something wrong when I tried it few years ago...

So what's next? How can I help? Is there some alpha version to try and report the results?

Sorry for the late reply been ill and work is about all I have been doing, plus have another project which might actually earn me money.

As for this working on but have to refactor quite a lot to support cloud and local. My idea is this:

  1. Login to Cloud services will get initial settings and setup
  2. Use the Controller / Ext (I think the EXT is the BMS_EXTENSION which is used for mappings) and other information to identify Register mappings as different controllers may map differently.
  3. Allow set each HP to be mapped locally or cloud (some people will have multiple HP's depending on house size)

The idea is to make this as easy as possible to configure.

So I thinkf or now I need some analysis

  • What Controller do you have and what is reported in the information (diagnostics will give that in HASS)
  • If you can identify the mappings.

Most mappings should be as follows

D Registers are in the read_coil from value 1 to 500 (I think)
A Registers are in read_holding_registers from 1 to 500
I Registers are in read_holding_registers from 5001 to 5500

But more recent HP's have custom firmware and may be mapping else where for example SeBsZ is a upc all his map to the Holding Registers, see top of the thread.

No worries, take your time. Health and source of money for our families are more important than these toys of ours ;-)

Interesting fields to identify my HP:

"version": "1.1.12",
"hp_type": "BAI",
"controller": "pco5",
"output": "1BA22I-1",
"data": {
          "hp_type": 4,

Or do you need whole diag output?

Seems my mapping looks like this:
D_1 start at 2 (02 - Input status)
A_1 start at 2 (03 - Holding Register)
I_1 start at 5003 (03 - Holding Register)

BTW: I have noticed this in the diag:

"error_info": {
            "some_error": false,
            "three_errors": false,
            "reset_3e": false,
            "safety_tstat": true,
            "alarm_a": 0,
            "alarm_b": 0,
            "alarm_c": 0,
            "alarm_d": 115,
            "alarm_e": 330,
            "alarm_f": 0,
            "alarm_g": 8,
            "alarm_h": 0,
            "alarm_i": 6,
            "alarm_j": 0,
            "alarm_k": 0,
            "alarm_l": 0,
            "alarm_m": 0
          },

The integration only exposes some_error and three_errors, which is not very useful on their own, but combined with those specific alarm counters you would be able to recognize what happened. And maybe even the reset_3e would work to get rid of the alarm state which would be useful for vacation situations (I already had one when I was skiing in the mountains and I had to ask MT support to reset the alarm remotely to not freeze my house...).
Alarm mapping of my HP:

"alarm_d": 115, - low evaporation temp
"alarm_e": 330, - flow
"alarm_g": 8, - fan temp protection
"alarm_i": 6, - freeze protection

Seems my mapping looks like this: D_1 start at 2 (02 - Input status) A_1 start at 2 (03 - Holding Register) I_1 start at 5003 (03 - Holding Register)

D should be in the Coils (input is read only)

Rest should look liike mine, its a bit confusing as I think it is actually A_1 is 1 and I_1 is 5002 as the index actually starts at 0, but will check most important is you are usng the standard firmware and pco5 is same as mine for the controller.

Yep, you're right. The D values are visible in '01 - Coil status' as well as in '02 - Input status'.
I am pretty sure the index is shifted +1 for D and A and +2 for I, but maybe it has something to do with a way how this ModScan is interpreting the Modbus data...