espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.34k stars 7.2k forks source link

Modbus RTU over TCP with same UID's but different IP's (IDFGH-9436) #10803

Open NicoBouckaert opened 1 year ago

NicoBouckaert commented 1 year ago

Answers checklist.

IDF version.

v5.1-dev-2066-g7869f4e151

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

Custom board

Power Supply used.

External 3.3V

What is the expected behavior?

In modbus tcp master mode I need to use RTU over TCP in order for the slave to respond. It doesn't respond to slave address (UID) 0. In my use case I'm addressing 2 devices with a different ip address but they both respond only to slave address (UID) 1. Obviously I can't use: enum { MB_DEVICE_ADDR1 = 1, // Slave address device 1 MB_DEVICE_ADDR2 = 1 // Slave address device 2 };

char* slave_ip_address_table[] = { "192.168.75.151", "192.168.75.152", NULL };

If I define all mb_parameter_descriptors with slave address 1 then they're all linked to the first ip address, which I don't want.

Ideally I would like to have 2 lists: 1 for IP addresses and another one for slave addresses:

char* slave_ip_address_table[] = { "192.168.75.151", "192.168.75.152", NULL }; uint8_t slave_address_table[] = { 1, 1 };

What is the actual behavior?

Since the slave addresses need to be unique I can't use the same slave address for 2 devices.

Steps to reproduce.

  1. Open the mb_master_tcp example.
  2. Configure like this: const mb_parameter_descriptor_t Alfen_parameters[] = { // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode} { DEV1_I1, STR("METER_CUR_L1"), STR("A"), 1, MB_PARAM_HOLDING, 1210, 2, HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 20000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, { DEV2_I1, STR("METER_CUR_L1"), STR("mA"), 1, MB_PARAM_HOLDING, 320, 2, HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 20000, 1 ), PAR_PERMS_READ_TRIGGER };
  3. Enter the following ip address list: char* slave_ip_address_table[] = { "192.168.75.151", "192.168.75.152", NULL };
  4. Notice that only the first device is read.

Also, the slave address is not send in the TCP packet. To fix this I added pucMBTCPFrame[MB_TCP_UID] = pxInfo->ucSlaveAddr; after pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(pxInfo->usTidCnt & 0xFF); in port_tcp_master.c

Debug Logs.

No response

More Information.

No response

alisitsyn commented 1 year ago

@NicoBouckaert,

My answers are below:

It doesn't respond to slave address (UID) 0.

The short address 0 is the broadcast address in the Modbus and can not be assigned to concrete slave according to specification. This is expected behavior not an issue. The spec uses UID=0 or 0xFF in MBAP that means it does not support the RTU over TCP. I understand also that there are many non standard slaves that can use communication with slave with UID=0 and > 0 but this is not issue against the stack as per standard.

If I define all mb_parameter_descriptors with slave address 1 then they're all linked to the first ip address, which I don't want.

The goal of the address in the data dictionary and IP table is to assign the Unique IP address for each Unique short address in the table. The esp-modbus master is designed and can work within one configured network segment. In your case both slaves have the same short addresses and indeed they can not have different IP addresses in the same segment. It is intentionally checked in the setup code. If you need to access other network segment through the gateway you need to change the short address of slave or do custom address translation for your network segment in your gateway code. I understand what is desired and there are plans to extend the options in IP address table. I will take it into account to implement in updated design of stack.

Also, the slave address is not send in the TCP packet. To fix this I added pucMBTCPFrame[MB_TCP_UID] = pxInfo->ucSlaveAddr;

This is known issue, the line is gone on some merge stage. The corresponded MR will be merged once the other critical MRs are ready for merge. I will try to merge it ASAP.

Unfortunately, for now I can not confirm this issue as a bug.

alisitsyn commented 1 year ago

@NicoBouckaert,

In the new version of esp-modbus the new options are going to be implemented:

  1. The new Kconfig option CONFIG_FMB_TCP_UID_ENABLED allows to support the UID in the master and slave applications. When it disabled the TCP slave will answer to request with any UID. Modbus TCP master sends requests with UID =0 when this option is disabled. When it is enabled the TCP master sends the slave UID in the request for each slave.
  2. The TCP slave supports an additional option comm_info->slave_uid and the slave will be able to answer only to the requests for the set UID when the CONFIG_FMB_TCP_UID_ENABLED is enabled, otherwise slave ignores UID field and answers to any UID in request.
  3. The Modbus TCP slave is able to answer to request with UID = 0.
  4. Unfortunately your request to support the same UID mapped on two different IP addresses can not be realized for now due to limitation of the interface which is the common for TCP and Serial ports of Modbus.

Please take a look to the changes and let me know if it can solve some of your issues showed above. You can review the changes applying the patch below: 0001_fix_support_for_unitid_master_and_slave.patch.log

Thank you.

NicoBouckaert commented 1 year ago

Hi Alex,

I looked at the diff file but couldn't patch my files. I guess I'm using a different version?

Anyway, being able to use a slave id other than the default 0 is a great feature and will help a lot of people. However, as already explained, using the slave address as an index is not a good idea. It doesn't allow to use the same slave address multiple times. Instead, it should be an index to a list of ip addresses and a list of slave addresses. Modbus tcp would use the list of ip addresses, modbus rtu would use the list of slave addresses. All combinations would be possible.

You already have the list of ip addresses. Just add a list of slave addresses.

Thank you.

alisitsyn commented 1 year ago

Hi @NicoBouckaert,

I looked at the diff file but couldn't patch my files. I guess I'm using a different version?

The changes are already merged and available in esp-modbus v1.0.10.

However, as already explained, using the slave address as an index is not a good idea. It doesn't allow to use the same slave address multiple times. Instead, it should be an index to a list of ip addresses and a list of slave addresses. Modbus tcp would use the list of ip addresses, modbus rtu would use the list of slave addresses.

I understand this and will take it into account. However, it is not a bug but design approach to bind the characteristic with concrete device it resides in. This come from mesh networks concept, and I was limited to change the original interface significantly. What you mention does not include just one list but involves making changes in the interface as well. This will be combined with the other tasks related to interface change.

Thank you for feedback. Unfortunately, according to plans it will not be changed soon and if you need it quickly please implement it by yourself.

Thank you for your feedback and proposal.

crmabs commented 1 year ago

I'm using the lib over a year with different modbus devices, and to be honest situations like this did not make my life easier. Ofcourse the whole thing is already tedious since subtle differences exist device by device. Plus the migration from 4.x to 5.x, etc. On top of it it's hard to remember which device required what in order to operate since you do the same stuff over and over.

So. I went for the connection agnostic mbc_master_send_request(..) approach. To my biggest surprise I still needed to supply those register descriptions. This is one thing. The other one - and I may be flat out wrong - that is the default way and which is terribly overthought. Yes, the register read log does look cool but other than that it's rigid in a way and also totally over-administered. ( supplying the measurement units manually. etc. ) I think it's never too late to elegantly decouple that part of the system, or make something similar next to it. So the result would be two options: go with the easy register descriptions thing or do it yourself with the 'send request' approach like a real man. Ofcourse I could work it around by supplying simply one reg.description. Comparing to the whole this would be only a small extra. Or correction if you are lucky. Or setting, wateva. But not HC required. The other one is this modbus rtu over tcp uuid slave address madness. First of all for the completeness sake you should print out the network port it tries to connect. RTU over TCP should NOT be a constellation, it must be somehow annotated, or at least when one gets it right he/she should see some trace of it in the log. The best if it is annotated in the code with an enum.

Modbus inherently has some quirks so noone needs more obscurity. You guys already did the heavy lifting wonderfully, these subtle details would raise level of professionality by a magnitude. I also have a feeling that you guys did not experience it as an "end user" or simply for you it's not standig out, since you are in the core of it 0/25.

By the way the cmake log is really useful / rather cool. Helped me out a lot recently On the other hand the sdkconfig rename script is a sneaky thing. I started duobting my sanity. :)) especially it freaked me out that it runs as a last step. A line of log would have been much appreciated. If I'm not mistaken it got in with a recent update.

alisitsyn commented 1 week ago

The issues mentioned here are partly resolved in the esp-modbus stack v2 version. It is updated and supports the multi-instance TCP Slave and Master with its own communication options and can send the CID requests with different UID associated with IP address or host name. The pre-release esp-modbus component version 2.0.0-beta.1. See the mbc_master_get_parameter_with() documentation.