libmodbus icon indicating copy to clipboard operation
libmodbus copied to clipboard

Add disabling flag for rtu address filtering

Open epsilonrt opened this issue 5 years ago • 11 comments

Hi, I developed a complementary project to libmodbus which allows you to use your work in C++ and adds functionalities. This project is libmodbuspp.

The current RTU implementation of libmodbus (in _modbus_rtu_check_integrity()), filters messages that are not intended for the context slave.

This poses a problem when you want to manage more than one slave on a serial link (for the Modbus::Server class of libmodbuspp for example).

This pull request makes it possible to add the possibility of disabling this address filtering in the RTU reception function.

I would therefore be very satisfied if you could integrate this modification into libmodbus.

Regards

epsilonrt avatar Feb 27 '20 19:02 epsilonrt

everyone else just calls https://libmodbus.org/docs/v3.1.6/modbus_set_slave.html when they'r emaking new requests on the rtu side. Could you elaborate further on why this change is necessary?

karlp avatar Feb 27 '20 23:02 karlp

I think @epsilonrt wants to handle messages of different masters in his multi-master-implementation.

I did the same thing in my server-callback-fork. When handling multiple clients/masters within one server/slave-instance this is needed.

pboettch avatar Feb 27 '20 23:02 pboettch

Can you explain it to me better then? If you're the master, you're still only sending one request on an rtu line at a time, sending a second one and disabling filtering is just inviting collisions, because you can't control the timing that the slaves will reply with. If you have separate RTU lines, you have separate contexts, I'm not following what this is for?

Even if you're a relay, allowing (say) multiple tcp masters to access an RTU line, you still have to serialize access to the line.

karlp avatar Feb 28 '20 10:02 karlp

So that you understand better, I explain the context of the project to you: I have to supervise an industrial site (seismic station) by MODBUS. This site has several MODBUS RTU RS485 2W slaves. I set up a router to access its slaves remotely from the outside. This router can be reached with a LoRa wireless connection (not TCP / IP, not LoRaWan). So I use the OSL RTU implementation on the outside and inside. Here is what the test gives from inside with the modification that I propose:

--- Json Modbus Router --- Press CTRL+C to stop... opening ../router-virtual-rs232.json...

Master rs232 connected through /dev/ttyUSB0:38400E1 with the slaves below:

id: 32 id: 33 id: 34 id: 35

Opening /dev/ttyUSB0 at 38400 bauds (E, 8, 1) Opening /dev/tnt1 at 38400 bauds (E, 8, 1) Listening server on /dev/tnt1:38400E1...

Waiting for a indication... <21><04><00><00><00><01><36><AA> [21][04][00][00][00][01][36][AA] Waiting for a confirmation... <21><04><02><27><38><22><D5> [21][04][02][27][38][22][D5] Waiting for a indication... <21><04><00><00><00><01><36><AA> [21][04][00][00][00][01][36][AA] Waiting for a confirmation... <21><04><02><27><38><22><D5> [21][04][02][27][38][22][D5] Waiting for a indication... <21><04><00><00><00><01><36><AA> [21][04][00][00][00][01][36][AA] Waiting for a confirmation... <21><04><02><27><38><22><D5> [21][04][02][27][38][22][D5] Waiting for a indication... <21><04><00><00><00><01><36><AA> [21][04][00][00][00][01][36][AA] Waiting for a confirmation... <21><04><02><27><38><22><D5> [21][04][02][27][38][22][D5] Waiting for a indication... <20><04><00><00><00><01><37><7B> [20][04][00][00][00][01][37][7B] Waiting for a confirmation... <20><04><02><0F><26><81><1D> [20][04][02][0F][26][81][1D] Waiting for a indication...

and this is what it looks like on the outside with mbpoll:

$ mbpoll -mrtu -a33 -b38400 -t3 /dev/tnt0 mbpoll 1.4-12 - FieldTalk(tm) Modbus(R) Master Simulator Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU Slave configuration...: address = [33] start reference = 1, count = 1 Communication.........: /dev/tnt0, 38400-8E1 t/o 1.00 s, poll rate 1000 ms Data type.............: 16-bit register, input register table

-- Polling slave 33... Ctrl-C to stop) [1]: 10040 -- Polling slave 33... Ctrl-C to stop) [1]: 10040 -- Polling slave 33... Ctrl-C to stop) [1]: 10040 -- Polling slave 33... Ctrl-C to stop) [1]: 10040 ^C--- /dev/tnt0 poll statistics --- 4 frames transmitted, 4 received, 0 errors, 0.0% frame loss

everything was closed. Have a nice day !

$ mbpoll -mrtu -a32 -b38400 -t3 -1 /dev/tnt0 mbpoll 1.4-12 - FieldTalk(tm) Modbus(R) Master Simulator Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU Slave configuration...: address = [32] start reference = 1, count = 1 Communication.........: /dev/tnt0, 38400-8E1 t/o 1.00 s, poll rate 1000 ms Data type.............: 16-bit register, input register table

-- Polling slave 32... [1]: 3878

As you can see I have a server/router which uses libmodbus which is listening on the link /dev/tnt1:38400E1 and which relays the messages to the RS485 slaves on /dev/ttyUSB038400E1. Everything works fine with the modification I made, however it does not work without it because libmodbus only knows a single slave identifier.

The option I propose is therefore useful in the case of the implementation of a server (and not a client/master).

This use case is in full compliance with the MODBUS standard ...

epsilonrt avatar Feb 28 '20 12:02 epsilonrt

So you're listening on one serial, and writing on another serial and don't want to look at the wire? I just looked at the byte for the unit id, it's always in the same location.

karlp avatar Feb 28 '20 13:02 karlp

on the router, I activated debug on the outside link and on the inside, so we see for each message 4 lines of debug. you can see the poll messages from slave 33 (0x21), then the only message to slave 32. everything works, the data are consistent (pressure and humidity). you just have to adjust the timeouts ...

epsilonrt avatar Feb 28 '20 14:02 epsilonrt

Right, so in my modbus relay, I just have this:

// receive from one context....
req_len = modbus_receive(st->mb_tcp, tcp_rbuf);
// set slave on other context
rc = modbus_set_slave(st->mb_rtu, tcp_rbuf[6]);
... continue with "other" context.

The only thing you woudl do for two RTU contexts is adjust the offset in the receive buffer to the unit id?

I get the idea of what you want here, I just think you're throwing out lots of safety and verification for something that you can easily actually just do correctly.

karlp avatar Feb 28 '20 15:02 karlp

Yes but there you show me a TCP -> RTU gateway and I have no problem in this case ... since there is no address filtering in this case. Here is the filtering code in modbus-tcp.c:

static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length)
{
    return msg_length;
}

There can only be one context connected by serial link, that's how libmodbus is designed. With your solution, you will have to multiply the contexts (2 contexts per slave), then connect one side, then the other, then disconnect both... this does not seem reasonable to me... it seems to me that there is a logic to having a context by connection and not more (in object-oriented approach).

Why not filtering in TCP and filtering in RTU ? I looked for an answer in the MODBUS reference documents, but could not find one. I looked in the RTU code and saw that Stéphane justifies adding filtering in the RTU implementation with this comment:

/* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless
     * CRC computing. */

However with the current computing powers, it seems reasonable to calculate a 16-bit CRC on twenty bytes, ten times a second ?

epsilonrt avatar Feb 28 '20 16:02 epsilonrt

You need one context per serial link yes. Is your problem that you can't setup a multi address slave on an RTU link? I think I finally understand what your problem is :) You kept talking about a master, but the real problem is your slave context right?

karlp avatar Feb 28 '20 16:02 karlp

It seemed logical to me to define the initial state of a new functionality to FALSE because it is the only state clearly defined in C for a boolean. I therefore neutralize all values other than FALSE with the code (flag == FALSE)

epsilonrt avatar Feb 28 '20 17:02 epsilonrt

Hello, yes that's exactly it. On the other hand if you re-read my messages, I absolutely did not speak of a problem at the level of the master, it is pboettch who interpreted my initial message ... After my English is not very good ... The problem may be the use of 2 words for the same function, client/server which corresponds in order to master/slave ... I have no problem at the master level, only as a slave RTU. In summary, if you could integrate this modification which makes the processing of RTU/TCP messages symmetrical, I would be very satisfied. I validated all the unit tests, can I add test cases for this functionality if you wish?

epsilonrt avatar Feb 29 '20 09:02 epsilonrt