2

I'm gonna to read/write under the modbus-tcp specification. So, I'm trying to code the client and server in the linux environment. (I would communicate with the windows program(as a client) using the modbus-tcp.)

but it doesn't work as I want, so I ask you here.

  1. I'm testing the client code for linux as a client and the easymodbus as a server.
  2. I used the libmodbus code.
  3. I'd like to read coil(0x01) and write coil(0x05).
  4. When the code is executed using the libmodbus, 'ff' is printed out from the Unit ID part.(according to the manual, 01 should be output for modbus-tcp. I don't know why 'ff' is printed(photo attached).

Wrong result:

Wrong result

Expected result:

Expected result

  1. '[00] [00] .... [00]' ; Do you know where to control this part?
  2. Do you have or do you know the sample code that implements the 'read/write' function using the libmodbus?

please let me know the information, if you know that.

ctx = modbus_new_tcp("192.168.0.99", 502);
modbus_set_debug(ctx, TRUE);

if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Connection failed: %s\n",
            modbus_strerror(errno));
    modbus_free(ctx);
    return -1;
}

tab_rq_bits = (uint8_t *) malloc(nb * sizeof(uint8_t));
memset(tab_rq_bits, 0, nb * sizeof(uint8_t));

tab_rp_bits = (uint8_t *) malloc(nb * sizeof(uint8_t));
memset(tab_rp_bits, 0, nb * sizeof(uint8_t));

nb_loop = nb_fail = 0;

/* WRITE BIT */
rc = modbus_write_bit(ctx, addr, tab_rq_bits[0]);
if (rc != 1) {
    printf("ERROR modbus_write_bit (%d)\n", rc);
    printf("Address = %d, value = %d\n", addr, tab_rq_bits[0]);
    nb_fail++;
} else {
    rc = modbus_read_bits(ctx, addr, 1, tab_rp_bits);
    if (rc != 1 || tab_rq_bits[0] != tab_rp_bits[0]) {
        printf("ERROR modbus_read_bits single (%d)\n", rc);
        printf("address = %d\n", addr);
        nb_fail++;
    }
}   
printf("Test: ");
if (nb_fail)
    printf("%d FAILS\n", nb_fail);
else
    printf("SUCCESS\n");

free(tab_rq_bits);
free(tab_rp_bits);

/* Close the connection */
modbus_close(ctx);
modbus_free(ctx);

return 0;
thmspl
  • 2,437
  • 3
  • 22
  • 48
Jasone J
  • 31
  • 1
  • 4

2 Answers2

3

That FF you see right before the Modbus function is actually correct. Quoting the Modbus Implementation Guide, page 23:

On TCP/IP, the MODBUS server is addressed using its IP address; therefore, the MODBUS Unit Identifier is useless. The value 0xFF has to be used.

So libmodbus is just sticking to the Modbus specification. I'm assuming, then, that the problem is in easymodbus, which is apparently expecting you to use 0x01as the unit id in your queries.

I imagine you don't want to mess with easymodbus, so you can fix this problem pretty easily from libmodbus: just change the default unit id:

modbus_set_slave(ctx, 1);

You could also go with:

 rc = modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS);
 ASSERT_TRUE(rc != -1, "Invalid broadcast address");

to make your client address all slaves within the network, if you have more than one.

You have more info and a short explanation of where this problem is coming from in the libmodbus man page for modbus_set_slave function.

For a very comprehensive example, you can check libmodbus unit tests

And regarding your question number 5, I don't know how to answer it, the zeros you mean are supposed to be the states (true or false) you want to write (or read) to the coils. For writing you can change them with the value field of function modbus_write_bit(ctx, address, value).

Marcos G.
  • 3,371
  • 2
  • 8
  • 16
  • To avoid confusion, it should be noted that the unit ID is only "useless" with Modbus TCP when you're not talking through a Modbus bridge. In practice, many if not most Modbus devices are connected via a bridge (or two). Most client software seems to be written with this in mind, as this is the usual troubleshooting mode. PLCs are often (but not always!) the exception, and the PLCs I'm familiar with (Wago PLCs and Modicon Momentum and M340 PLCs) all respond to unit 1. I haven't tested if they respond to other unit IDs. – Jeff Spaulding Nov 21 '20 at 00:20
1

I'm very grateful for your reply.

I tested the read/write function using the 'unit-test-server/client' code you recommended. I've reviewed the code, but there are still many things I don't know.

However, there is an address value that acts after testing each other with unit-test-server/client code and there is an address value that does not work (Do you know why?).

-Checked and found that the UT_BITS_ADDRESS (address value) value operates from 0x130 to 0x150 -'error Illegal data address' occurs at values below -0x130 and above 0x150 -The address I want to read/write is 0x0001 to 0x0004(Do you know how to do?).

I want to know how to process and transmit data like the TX part of the right picture.

enter image description here

I'm running both client and server in my Linux environment and I'm doing read/write testing. Among the wrong pictures...[06][FF]... <-- I want to know how to modify FF part (to change the value to 01 as shown in the picture)

enter image description here

and "modbus_set_slave" is the function for modbus rtu? I'd like to communicate PC Program and Linux device in the end. so Which part do I use that function?

I thanks for your concern again.

Jasone J
  • 31
  • 1
  • 4
  • you're welcome. To change the slave ID `FF` to `01` you have to call function `modbus_set_slave(ctx, 1); ` somewhere in your code before you read or write any coils or registers (so early on, after you define `ctx`). You get illegal address if you try to read or write registers or coils that are not defined on your server (that happens when you use the unit-tests). If you have them defined you should be able to do `rc = modbus_write_bit(ctx, any_address, ON);` with `const uint16_t any_address = 0x0001;` or any other value you want. – Marcos G. Nov 01 '19 at 19:04
  • For more details, you can use the search function on [github](https://github.com/stephane/libmodbus/search?q=UT_BITS_ADDRESS&unscoped_q=UT_BITS_ADDRESS) – Marcos G. Nov 01 '19 at 19:06