Modbus调用反复返回相同帧问题排查求助
Modbus RTU读取重复值排查(C语言程序)
环境与执行流程
- 硬件:Kunbus RevPi Connect(树莓派),RS485网络接入6个传感器(含4个Dat3018热电偶数字化传感器)
- 程序核心流程:
- 发送厂商指定的同步命令,触发Dat3018将实时采样值写入内部寄存器
- 执行其他业务操作
- 通过Modbus RTU读取寄存器存储值到树莓派
异常现象
- 首次循环运行正常,能获取随传感器变化的实时数值
- 从第二次循环开始,所有读取请求返回的帧与首次结果完全一致,即便传感器数值已发生变化
- 重启程序后,首次循环又能正常获取新的数值
- 用Python脚本执行完全相同的操作,可稳定获取变化的数值,排除硬件、传感器协议命令本身的问题
关键命令与代码片段
同步命令帧(厂商确认有效,重复调用可刷新寄存器值)
[FF][06][00][00][00][0A][1C][13]
读取同步值的函数与从站命令帧
int read_tempature_synced_values(modbus_t *RTU_context, int slave_addr, uint16_t output[]){ modbus_set_slave(RTU_context, slave_addr); // 减去1以抵消库自带的+1偏移 return modbus_read_registers(RTU_context, _temp_sync_tempatures_start_addr-1, 8, output); }
从站#20的读取命令帧:
[14][03][00][16][00][08][A7][0D]
读取逻辑主代码片段
// 读取温度测量值 for (int i = 0; op_args->settings_instance->tempatureSlaveAddr[i] != -1; ++i){ uint16_t *raw_measures = malloc(8 * sizeof(uint16_t)); read_tempature_synced_values(op_args->modbus_instance, op_args->settings_instance->tempatureSlaveAddr[i], raw_measures); usleep(op_args->settings_instance->delay_ms); for (int u = 0; u <= 7; ++u){ if(valid_sensors[i][u] == FALSE){ // 确认该位置有有效传感器 measurements[measurements_taken].TYPE = TEMPATURE_MEASUREMENT_TYPE; measurements[measurements_taken].bankNumber = i; measurements[measurements_taken].sensorNumber = u; measurements[measurements_taken].measurementTime = sync_time; measurements[measurements_taken].tempatueReading = ((float)raw_measures[u])/10; measurements_taken += 1; } } free(raw_measures); modbus_flush(op_args->modbus_instance); }
额外异常表现
程序运行时会随机出现以下打印信息,且错误发生在串口或SQLite数据库连接操作之前:
ERROR Connection timed out: select ERROR Connection timed out: select
排查方向与建议
- 清空libmodbus内部缓冲区:libmodbus可能存在未清空的接收缓存,导致第二次循环读取到旧数据。尝试在每次循环开始(或同步命令发送后)调用
modbus_clear_rtu_buffer(RTU_context)强制清空缓冲区。 - 确认同步命令执行状态:检查同步命令的发送返回值,确保每次循环的同步命令都成功发送并被传感器接收。若使用自定义帧发送,需校验发送字节数或传感器响应。
- 修正延迟时间单位:当前
usleep的参数单位是微秒,若配置的delay_ms是毫秒值,实际延迟会不足(比如配置100仅等待100微秒),导致Dat3018未完成寄存器写入就开始读取。需确认参数单位匹配,或改为usleep(op_args->settings_instance->delay_ms * 1000)。 - 添加错误处理逻辑:当前代码未处理
read_tempature_synced_values的返回值,若某次读取失败,raw_measures可能残留旧内存数据。需添加返回值判断:int ret = read_tempature_synced_values(op_args->modbus_instance, slave_addr, raw_measures); if (ret == -1) { fprintf(stderr, "Slave %d read failed: %s\n", slave_addr, modbus_strerror(errno)); // 执行重试或跳过逻辑 } - 校验从站地址切换状态:多次切换从站地址后,
modbus_set_slave可能未正确更新上下文状态,可在每次设置从站地址后调用modbus_flush,或检查modbus_set_slave的返回值是否为0(成功)。 - 对齐串口参数:对比Python脚本使用的串口参数(波特率、奇偶校验、停止位、流控)与C程序是否完全一致,RS485场景通常无需硬件流控,需确认配置匹配。
内容的提问来源于stack exchange,提问作者4DoorToyota
相关产品推荐
相关产品推荐

