使用pySerial(台达太阳能逆变器)读取RS485数据时丢失字节
Missing bytes when reading RS485 data using pySerial (Delta Solar Inverter)
一台太阳能逆变器(台达RPI M6A)有主从RS485总线。主站每秒几次要求逆变器发送响应数据集,然后逆变器执行。总通信集为 176 字节。
Raspberry Pi 3B 使用 FTDI FT232 USB 串行 (UART) I2C 转换器连接到此总线。
一个Python程序用于读取总线上传输的数据。仅当没有被解释为特殊字符的字节时(至少,我是这么认为的),这才能正常工作。
我首先一次读取输入 1 个字节。一旦找到正确的 'start of transmission' 序列(STX 后跟 ACK 后跟 inverterID=1),那么 160 字节的数据集加上一个结束序列是预期的。最后读取的字节应该是 ETX (= ascii 3).
预期的输出是:
Screenshot of expected output
我认为问题在于,如果某些字节相当于转义符或换行符等特殊字符,那么这些字节不是 ser.read() 命令的 'seen'。然后我会很快得到几个字节的ETX字节,说明中间某处的一些字节没有被捕获。
代码的相关部分是:
if bytes_to_read == 1:
raw_data = ser.read()
pos = pos + 1;
# print pos;
if ord(raw_data) == 2: # 2 = start of text character
pos = 1;
print str(pos) + ' ' + str(ord(raw_data))
elif pos == 2 and ord(raw_data) == 6: # 6 = acknowledge character
ack = True;
print str(pos) + ' ' + str(ord(raw_data))
elif pos == 2 and ord(raw_data) != 6: # 6 = acknowledge character
ack = False;
print str(pos) + ' ' + str(ord(raw_data)) + ' ack reset to False'
elif pos == 3 and ack and ord(raw_data) == 1:
bytes_to_read = 164;
print str(pos) + ' ' + str(ord(raw_data))
elif bytes_to_read == 164:
raw_data_byte = ser.read(164)
print len(raw_data_byte);
bytes_to_read = 1;
ack = False;
print 'got to read 164 bytes, first byte is ' + str(ord(raw_data_byte[0])) + ', last byte: ' + str(ord(raw_data_byte[163]));
if ord(raw_data_byte[0]) == 160 and ord(raw_data_byte[163]) == 3: # 160 = data bytes specified by sender
print ('ready to process ' + str(len(raw_data_byte)) + ' bytes')
supplied_power_byte1 = bin(ord(raw_data_byte[101]))[2:].zfill(8)
supplied_power_byte2 = bin(ord(raw_data_byte[102]))[2:].zfill(8)
稍后处理捕获的字节以允许写入数据库。
我一直在尝试 ser.read() 和 ser.readline(),结果相同。
如能给我指明正确方向的任何提示,我将不胜感激。
谢谢,
布拉姆
解决方案是将串行接口的所有特殊字符设置为'undefined',使用cli命令:
pi@raspberrypi:~ $ stty -F /dev/RS-485 intr ^-
其中“/dev/RS-485”是实际 USB 端口的符号链接,'intr' 是特殊字符名称的示例。为每个特殊字符完成此操作后(它们都可以添加到上面的 cli 命令中),端口设置如下所示:
pi@raspberrypi:~ $ stty -F /dev/RS-485 -g
1400:4:cbe:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
要以更人性化的格式查看设置,请使用命令
pi@raspberrypi:~ $ stty -F /dev/RS-485 -a
现在所有字节都按预期进入。
一台太阳能逆变器(台达RPI M6A)有主从RS485总线。主站每秒几次要求逆变器发送响应数据集,然后逆变器执行。总通信集为 176 字节。 Raspberry Pi 3B 使用 FTDI FT232 USB 串行 (UART) I2C 转换器连接到此总线。
一个Python程序用于读取总线上传输的数据。仅当没有被解释为特殊字符的字节时(至少,我是这么认为的),这才能正常工作。 我首先一次读取输入 1 个字节。一旦找到正确的 'start of transmission' 序列(STX 后跟 ACK 后跟 inverterID=1),那么 160 字节的数据集加上一个结束序列是预期的。最后读取的字节应该是 ETX (= ascii 3).
预期的输出是: Screenshot of expected output
我认为问题在于,如果某些字节相当于转义符或换行符等特殊字符,那么这些字节不是 ser.read() 命令的 'seen'。然后我会很快得到几个字节的ETX字节,说明中间某处的一些字节没有被捕获。
代码的相关部分是:
if bytes_to_read == 1:
raw_data = ser.read()
pos = pos + 1;
# print pos;
if ord(raw_data) == 2: # 2 = start of text character
pos = 1;
print str(pos) + ' ' + str(ord(raw_data))
elif pos == 2 and ord(raw_data) == 6: # 6 = acknowledge character
ack = True;
print str(pos) + ' ' + str(ord(raw_data))
elif pos == 2 and ord(raw_data) != 6: # 6 = acknowledge character
ack = False;
print str(pos) + ' ' + str(ord(raw_data)) + ' ack reset to False'
elif pos == 3 and ack and ord(raw_data) == 1:
bytes_to_read = 164;
print str(pos) + ' ' + str(ord(raw_data))
elif bytes_to_read == 164:
raw_data_byte = ser.read(164)
print len(raw_data_byte);
bytes_to_read = 1;
ack = False;
print 'got to read 164 bytes, first byte is ' + str(ord(raw_data_byte[0])) + ', last byte: ' + str(ord(raw_data_byte[163]));
if ord(raw_data_byte[0]) == 160 and ord(raw_data_byte[163]) == 3: # 160 = data bytes specified by sender
print ('ready to process ' + str(len(raw_data_byte)) + ' bytes')
supplied_power_byte1 = bin(ord(raw_data_byte[101]))[2:].zfill(8)
supplied_power_byte2 = bin(ord(raw_data_byte[102]))[2:].zfill(8)
稍后处理捕获的字节以允许写入数据库。
我一直在尝试 ser.read() 和 ser.readline(),结果相同。
如能给我指明正确方向的任何提示,我将不胜感激。
谢谢, 布拉姆
解决方案是将串行接口的所有特殊字符设置为'undefined',使用cli命令:
pi@raspberrypi:~ $ stty -F /dev/RS-485 intr ^-
其中“/dev/RS-485”是实际 USB 端口的符号链接,'intr' 是特殊字符名称的示例。为每个特殊字符完成此操作后(它们都可以添加到上面的 cli 命令中),端口设置如下所示:
pi@raspberrypi:~ $ stty -F /dev/RS-485 -g
1400:4:cbe:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
要以更人性化的格式查看设置,请使用命令
pi@raspberrypi:~ $ stty -F /dev/RS-485 -a
现在所有字节都按预期进入。