我面临一些与Modbus RTU实现有关的问题。我有2x Arduino MKR和RS485 hats/扩展作为我的2个从设备(使用ArduinoModbus库)。我正在尝试使用python和pymodbus库轮询我的个人电脑(Windows)上的设备,运行速度为9600波特。
我可以成功地传输数据。最初的理智测试是一个简单的analogRead()在一个Arduino(传感器1)上,写入它的内部保持寄存器,然后进行pymodbus主投票/注册请求。
我现在连接了第二个Arduino (传感器2),它有一个I2C连接到一个流传感器。arduino正在运行,通过I2C读取传感器,并用数据更新5倍的保存寄存器。主机(PC)一个接一个地轮询Arduino(传感器1和传感器2)。它总是成功地获取传感器1的数据(只有一个寄存器),但间歇性地失败了获取传感器2的数据(5个寄存器)。python控制台如下所示:
Sensor 2: 0,25000,0,0, 0
Sensor 2: 0,25000,0,0, 0
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
Sensor 2: 0,25000,0,0, 0
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
Sensor 2: 0,25000,0,0, 0
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response对日志进行更深入的研究就会发现,这个问题并不是所有Bytes人都在解决的问题,如下所示:
11/24/2022 03:52:59 PM Running transaction 4
11/24/2022 03:52:59 PM SEND: 0x1 0x3 0x0 0x0 0x0 0x5 0x85 0xc9
11/24/2022 03:52:59 PM Changing state to IDLE - Last Frame End - 1669265577.447308, Current Time stamp - 1669265579.457942
11/24/2022 03:52:59 PM New Transaction state "SENDING"
11/24/2022 03:52:59 PM Changing transaction state from "SENDING" to "WAITING FOR REPLY"
11/24/2022 03:52:59 PM {msg_start} received, Expected 15 bytes Received 11 bytes !!!!
11/24/2022 03:52:59 PM Changing transaction state from "WAITING FOR REPLY" to "PROCESSING REPLY"
11/24/2022 03:52:59 PM RECV: 0x1 0x3 0xa 0x0 0x0 0x61 0xa8 0x0 0x0 0x0 0x0
11/24/2022 03:52:59 PM Frame - [b'\x01\x03\n\x00\x00a\xa8\x00\x00\x00\x00'] not ready
11/24/2022 03:52:59 PM Getting transaction 1
11/24/2022 03:52:59 PM Changing transaction state from "PROCESSING REPLY" to "TRANSACTION_COMPLETE"
11/24/2022 03:52:59 PM Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
11/24/2022 03:53:01 PM Current transaction state - TRANSACTION_COMPLETE现在,我已经完全删除了向传感器1请求数据,并且我的python脚本只能从传感器2(问题传感器)请求,但问题仍然存在,我的python脚本如下所示:
import serial
import time
import logging
from pymodbus.client import ModbusSerialClient
from pymodbus.transaction import ModbusRtuFramer
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('main.log', 'w', 'utf-8')
handler.setFormatter(logging.Formatter(fmt='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p'))
logger.addHandler(handler)
client = ModbusSerialClient("COM12", ModbusRtuFramer, baudrate=9600, timeout=10, reset_socket=False)
client.connect()
while(1):
c2 = client.read_holding_registers(0,5,1)
if c2.isError():
logger.error(msg=c2)
print(c2)
else:
print(f"Sensor 2: {c2.getRegister(0)},{c2.getRegister(1)},{c2.getRegister(2)},{c2.getRegister(3)}, {c2.getRegister(4)}")
time.sleep(2)我不太确定什么是失火..。我可以很好地使用像QModMaster这样的图形用户界面来处理传感器2的数据,我知道它在引擎盖下使用libmodbus。我应该使用python库pylibmodbus吗?它需要本地编译的libmodbus,这对Windows来说有点麻烦.
我是不是漏掉了一个能帮上忙的pymodbus设置?
我已经尝试过更改pymodbus超时,但没有起作用。我尝试降低流量传感器的采样率,以降低频率,并确保Arduino是免费的/可用于Modbus请求。这是行不通的,事实上,减少它使问题变得更糟。
我尝试向python代码(time.sleep(2))添加基本延迟,以减缓Modbus请求的速度,但这对错误没有影响。
希望有人知道发生了什么,因为我已经花了很长时间在网上搜索资源,以找到答案,但没有用。如果我最后需要更多的澄清,我可以提供:)
谢谢!
P.S. Arduino代码,供参考
#include <Arduino.h>
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#include <Wire.h>
/**
* Modbus slave/server
*/
#define SAMPLE_RATE 50
const int ADDRESS = 0x08; // Sensor I2C Address
const float SCALE_FACTOR_FLOW = 500.0; // Scale Factor for flow rate measurement
const float SCALE_FACTOR_TEMP = 200.0; // Scale Factor for temperature measurement
int count = 0;
unsigned long startMillis;
unsigned long currentMillis;
enum error_types{
no_error,
write_mode_error,
read_error
};
enum error_types error;
// Protoypes
int stop_continuous_measurement();
int start_continous_measurement();
void setup() {
int ret;
// Start Serial and I2C
Serial.begin(9600); // initialize serial communication
Wire.begin(); // join i2c bus (address optional for master)
// Set up MODBUS
if (!ModbusRTUServer.begin(0x01,9600)) {
Serial.println("Could not begin ModbusRTU server...");
while(1);
}
// configure holding registers at address 0x00, 4 registers for data
ModbusRTUServer.configureHoldingRegisters(0x00, 5);
// start sensor
do {
// Soft reset the sensor
Wire.beginTransmission(0x00);
Wire.write(0x06);
ret = Wire.endTransmission();
if (ret != 0) {
Serial.println("Error while sending soft reset command, retrying...");
delay(500); // wait long enough for chip reset to complete
}
} while (ret != 0);
delay(50); // wait long enough for chip reset to complete
// To perform a measurement, first send 0x3608 to switch to continuous
if(start_continous_measurement() !=0) {
error = write_mode_error;
}
startMillis = millis();
}
void loop() {
ModbusRTUServer.poll();
uint16_t aux_value;
uint16_t sensor_flow_value;
uint16_t sensor_temp_value;
int16_t signed_flow_value;
int16_t signed_temp_value;
float scaled_flow_value;
float scaled_temp_value;
byte aux_crc;
byte sensor_flow_crc;
byte sensor_temp_crc;
// measurement mode (H20 calibration), then read 3x (2 bytes + 1 CRC byte) from the sensor.
// To perform a IPA based measurement, send 0x3615 instead.
// Check datasheet for available measurement commands.
error = no_error;
currentMillis = millis();
if(currentMillis - startMillis > SAMPLE_RATE){
Wire.requestFrom(ADDRESS, 9);
if (Wire.available() < 9) {
error = read_error;
}
else{
sensor_flow_value = Wire.read() << 8; // read the MSB from the sensor
sensor_flow_value |= Wire.read(); // read the LSB from the sensor
sensor_flow_crc = Wire.read();
sensor_temp_value = Wire.read() << 8; // read the MSB from the sensor
sensor_temp_value |= Wire.read(); // read the LSB from the sensor
sensor_temp_crc = Wire.read();
aux_value = Wire.read() << 8; // read the MSB from the sensor
aux_value |= Wire.read(); // read the LSB from the sensor
aux_crc = Wire.read();
signed_flow_value = (int16_t) sensor_flow_value;
scaled_flow_value = ((float) signed_flow_value) / SCALE_FACTOR_FLOW;
signed_temp_value = (int16_t) sensor_temp_value;
scaled_temp_value = ((float) signed_temp_value) / SCALE_FACTOR_TEMP;
// write to MODBUS registers
ModbusRTUServer.holdingRegisterWrite(0, (uint16_t) count);
ModbusRTUServer.holdingRegisterWrite(1, (uint16_t) scaled_temp_value*1000);
ModbusRTUServer.holdingRegisterWrite(2, (uint16_t) scaled_flow_value*1000);
ModbusRTUServer.holdingRegisterWrite(3,(uint16_t) aux_value);
ModbusRTUServer.holdingRegisterWrite(4, (uint16_t) error);
}
startMillis = currentMillis;
}
}
int start_continous_measurement() {
Wire.beginTransmission(ADDRESS);
Wire.write(0x36);
Wire.write(0x08);
return Wire.endTransmission();
}
int stop_continuous_measurement() {
// To stop the continuous measurement, first send 0x3FF9.
Wire.beginTransmission(ADDRESS);
Wire.write(0x3F);
Wire.write(0xF9);
return Wire.endTransmission();
}发布于 2022-11-25 07:43:03
日志显示,只接收到部分数据:Expected 15 bytes Received 11 bytes。这可能是由错误的字符间计时引起的,即字符之间的静默时间被错误地解释为消息的结束。通过指定client.strict = False,实现了Modbus规范的字符间定时.
还可以看到类似的故事:Pymodbus :响应中的错误字节计数和pymodbus 问题。
https://stackoverflow.com/questions/74555963
复制相似问题