使用C语言创建一个简单的Modbus客户端/服务器

13

我目前正在开发一个项目,以允许不同的自动化设备进行通信。为此,我想创建一个客户端和服务器,它们将使用modbus协议进行通信。目前我不确定是否要使用ModBus/TCP、ModBus/RTU还是ModBus/ASCII。

我已经在C语言中搜索了客户端/服务器示例,尽管可以找到库,但没有简单的通信示例。我想从头开始编写,因此不需要这些库。

我所寻求的是,如果有人能给我一个用C语言编写的简单代码,用于客户端和/或服务器之间使用Modbus进行通信,因为我不确定将使用哪种类型的Modbus(RTU/TCP/ASCII)都会非常有帮助。

最简单的代码越好,我希望代码可以演示例如:与服务器的初始化、请求、回复、关闭连接等操作。

非常感谢您的时间。


1
如果我理解正确,ModbusTCP只是一种普通的TCP通信方式,其中数据被结构化为Modbus。我已经创建了TCP服务器/客户端,所以应该没问题。因此,我更感兴趣的是串行实现,例如RTU,对此我完全不了解。 - PiggyGenius
1
一种方法是带有进度更新的编辑,另一种方法是赏金,但您必须等待至少两天。不过,请保持关注,我正在写一些东西 :) - matpop
1个回答

25

三件事:

  1. 在开发自己的客户端和服务器组件时,我建议你只在必要或方便的情况下使用Modbus,并考虑到开放性(即其他制造商必须能够通过标准化协议与您的客户端或服务器组件进行通信-而Modbus符合要求)。
  2. 请注意,Modbus TCP不仅仅是基于TCP/IP的Modbus RTU(/ASCII)(当然仍然允许这样做,UDP也是允许的),还有一些重要的差异需要考虑。
  3. 我理解你需要深入了解Modbus。此时,一旦在C程序中有一个打开的串行通道或(监听)TCP套接字,您可以开始使用简单的Modbus请求/响应。

请查看这个短而相当完整的描述,以及这个不断更新的库的文档


这是一个基于libmodbus的Linux超简化RTU示例。
为了紧凑,允许我放松C99。
在现实世界中,您还应该正确处理诸如SIGTERM等信号...
对于Linux内核2.6.28及以上版本,还有一个modbus_rtu_set_serial_mode(RS232 vs RS485)函数。您可能会发现其他库可以在您的平台上更轻松地处理RS485。

主程序片段

//Create a new RTU context with proper serial parameters (in this example,
//device name /dev/ttyS0, baud rate 9600, no parity bit, 8 data bits, 1 stop bit)
modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}

//Set the Modbus address of the remote slave (to 3)
modbus_set_slave(ctx, 3);


uint16_t reg[5];// will store read registers values

//Read 5 holding registers starting from address 10
int num = modbus_read_registers(ctx, 10, 5, reg);
if (num != 5) {// number of read registers is not the one expected
    fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}

modbus_close(ctx);
modbus_free(ctx);

从属片段

//Prepare a Modbus mapping with 30 holding registers
//(plus no output coil, one input coil and two input registers)
//This will also automatically set the value of each register to 0
modbus_mapping_t *mapping = modbus_mapping_new(0, 1, 30, 2);
if (!mapping) {
    fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
    exit(1);
}


//Example: set register 12 to integer value 623
mapping->tab_registers[12] = 623;


modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

//Set the Modbus address of this slave (to 3)
modbus_set_slave(ctx, 3);


if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}


uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];// request buffer
int len;// length of the request/response

while(1) {
    len = modbus_receive(ctx, req);
    if (len == -1) break;

    len = modbus_reply(ctx, req, len, mapping);
    if (len == -1) break;
}
printf("Exit the loop: %s\n", modbus_strerror(errno));

modbus_mapping_free(mapping);
modbus_close(ctx);
modbus_free(ctx);

3
非常感谢您的答复,我抱有希望。我被迫使用Modbus,即使我不想用,但我正在处理的情况使得使用Modbus成为逻辑上的解决方案之一。是的,我理解Modbus TCP不是RTU或ASCII over TCP,我想表达的是Modbus TCP像任何TCP通信一样,唯一的区别是数据结构(对吗?)。谢谢您提供的链接,我已经获取了Modbus库,但我真的想要一个不使用任何库的C语言示例。 - PiggyGenius
1
他们解释了如何做,但我想要一个书面示例(最好用C编写,但其他语言也可以),以确保在开始实现此通信协议之前我理解得正确。你似乎对Modbus有所了解,也许你可以编写一个简短的代码,展示Modbus RTU或ASCII(我理解Modbus TCP)客户端和服务器之间的交互?我会非常感激! - PiggyGenius
1
我只是想更新一下,我仍然卡在Modbus串行通信上,无论是RTU还是ASCII。 - PiggyGenius
1
考虑到你所说的一切,我真的认为在你的情况下从头开始是非常不明智的。虽然了解Modbus协议变体的细节非常重要,但我建议你不要重复造轮子,而是选择一个适合你平台的现有C库。我会尽快发布一个基于libmodbus的简化RTU示例(ASCII实际上是一种遗留问题)。在这种类型的程序中,你只需引用操作系统设备名称(例如/dev/ttyS0、COM6);你需要确保该设备作为rs232/rs485串行链接正常工作。 - matpop
3
非常感谢您提供的代码,我会尽快尝试实现它。您花费的时间来帮助我让我非常惊喜,非常感谢您。 - PiggyGenius
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接