【问题标题】:How to work out 'read/write' function using the libmodbus?(c code)如何使用 libmodbus 实现“读/写”功能?(c 代码)
【发布时间】:2020-02-27 01:01:21
【问题描述】:

我将根据 modbus-tcp 规范进行读/写。 所以,我正在尝试在 linux 环境中编写客户端和服务器。 (我会使用 modbus-tcp 与 windows 程序(作为客户端)通信。)

但它没有按我的意愿工作,所以我在这里问你。

  1. 我正在测试 linux 作为客户端和 easymodbus 作为服务器的客户端代码。
  2. 我使用了 libmodbus 代码。
  3. 我想读取线圈(0x01)并写入线圈(0x05)。
  4. 当使用libmodbus执行代码时,从Unit ID部分打印出'ff'。(根据手册,modbus-tcp应该输出01。 我不知道为什么要打印“ff”(附照片)。

错误的结果:

预期结果:

  1. '[00] [00] .... [00]' ;你知道在哪里控制这部分吗?
  2. 您是否拥有或知道使用 libmodbus 实现“读/写”功能的示例代码?

如果您知道,请告诉我信息。

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;

【问题讨论】:

    标签: modbus modbus-tcp


    【解决方案1】:

    您在 Modbus 功能实际上正确之前看到的 FF。引用Modbus Implementation Guide,第 23 页:

    在 TCP/IP 上,MODBUS 服务器使用其 IP 地址进行寻址;因此, MODBUS 单元标识符是无用的。必须使用值 0xFF。

    所以 libmodbus 只是坚持 Modbus 规范。那么,我假设问题出在 easymodbus 中,这显然是希望您在查询中使用 0x01 作为单位 ID。

    我想你不想弄乱 easymodbus,所以你可以很容易地从 libmodbus 解决这个问题:只需更改默认单元 ID:

    modbus_set_slave(ctx, 1);
    

    你也可以选择:

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

    让您的客户端寻址网络中的所有从属设备(如果您有多个从属设备)。

    您可以在libmodbus man page for modbus_set_slave function 中获得更多信息和关于此问题出处的简短说明。

    非常全面的例子可以查看libmodbus unit tests

    关于您的第 5 个问题,我不知道如何回答,您所说的零应该是您想要写入(或读取)线圈的状态(真或假)。对于编写,您可以使用函数modbus_write_bit(ctx, address, value) 的值字段更改它们。

    【讨论】:

    • 为避免混淆,应注意,当您不通过 Modbus 桥接器通话时,单元 ID 仅对 Modbus TCP “无用”。在实践中,许多(如果不是大多数)Modbus 设备是通过一个(或两个)网桥连接的。大多数客户端软件似乎都考虑到了这一点,因为这是通常的故障排除模式。 PLC 通常(但并非总是如此!)例外,我熟悉的 PLC(Wago PLC 和 Modicon Momentum 和 M340 PLC)都响应单元 1。我没有测试它们是否响应其他单元 ID。
    【解决方案2】:

    非常感谢您的回复。

    我使用您推荐的“unit-test-server/client”代码测试了读/写功能。 我已经查看了代码,但仍有很多我不知道的地方。

    但是,有一个地址值在使用单元测试服务器/客户端代码相互测试后起作用,并且有一个地址值不起作用 (你知道为什么吗?)。

    -检查发现UT_BITS_ADDRESS(地址值)值从0x130到0x150运行 -'error Illegal data address'发生在低于-0x130和高于0x150的值 -我要读/写的地址是0x0001到0x0004(你知道怎么做吗?)。

    我想知道如何处理和传输数据,如右图的TX部分。

    enter image description here

    我在我的 Linux 环境中同时运行客户端和服务器,并且正在进行读/写测试。 在错误的图片中...[06][FF]...

    enter image description here

    而“modbus_set_slave”是modbus rtu的功能吗? 我想最后沟通PC程序和Linux设备。 那我该用哪个部分的功能呢?

    再次感谢您的关心。

    【讨论】:

    • 不客气。要将从站 ID FF 更改为 01,您必须在读取或写入任何线圈或寄存器之前在代码中的某处调用函数 modbus_set_slave(ctx, 1); (这么早,在您定义 ctx 之后)。如果您尝试读取或写入服务器上未定义的寄存器或线圈(当您使用单元测试时会发生这种情况),您将获得非法地址。如果您定义了它们,您应该能够使用 const uint16_t any_address = 0x0001; 或您想要的任何其他值来执行 rc = modbus_write_bit(ctx, any_address, ON);
    • 更多详情可以使用github上的搜索功能
    猜你喜欢
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 2018-11-11
    • 1970-01-01
    相关资源
    最近更新 更多