三件事:
- 在开发自己的客户端和服务器组件时,我建议你只在必要或方便的情况下使用Modbus,并考虑到开放性(即其他制造商必须能够通过标准化协议与您的客户端或服务器组件进行通信-而Modbus符合要求)。
- 请注意,Modbus TCP不仅仅是基于TCP/IP的Modbus RTU(/ASCII)(当然仍然允许这样做,UDP也是允许的),还有一些重要的差异需要考虑。
- 我理解你需要深入了解Modbus。此时,一旦在C程序中有一个打开的串行通道或(监听)TCP套接字,您可以开始使用简单的Modbus请求/响应。
请查看这个短而相当完整的描述,以及这个不断更新的库的文档。
这是一个基于libmodbus的Linux超简化RTU示例。
为了紧凑,允许我放松C99。
在现实世界中,您还应该正确处理诸如SIGTERM等信号...
对于Linux内核2.6.28及以上版本,还有一个modbus_rtu_set_serial_mode
(RS232 vs RS485)函数。您可能会发现其他库可以在您的平台上更轻松地处理RS485。
主程序片段
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);
}
modbus_set_slave(ctx, 3);
uint16_t reg[5];
int num = modbus_read_registers(ctx, 10, 5, reg);
if (num != 5) {
fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}
modbus_close(ctx);
modbus_free(ctx);
从属片段
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);
}
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);
}
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];
int len;
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);