【问题标题】:Encode/decode data from RS232 serial port从 RS232 串行端口编码/解码数据
【发布时间】:2020-03-23 23:55:11
【问题描述】:

这是我第一次必须通过 RS232 串行连接到设备以读取/写入数据,我被困在编码/解码程序上。

我正在使用库“pyserial”在 Python 3 中做所有事情。这是我到目前为止所做的:

import serial

ser = serial.Serial()
ser.port = '/dev/ttyUSB0'
ser.baudrate = 115200
ser.bytesize = serial.EIGHTBITS
ser.parity = serial.PARITY_NONE
ser.stopbits = serial.STOPBITS_ONE
ser.timeout = 3

ser.open()

device_write = ser.write(bytearray.fromhex('AA 55 00 00 07 00 12 19 00'))

device_read = ser.read_until()

连接/通信似乎按预期工作。 device_read 的输出是

b'M1830130A2IMU v3.2.9.1 26.04.19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x0527641\x00\x00\x00IMHF R.1.0.0 10.28.2018 td:  6.500ms\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00'

这就是我卡住的地方。我不知道如何解释这个。附件是数据表中的一张图片,它解释了输出应该代表什么。

对于我拥有的设备,数据表显示“字节 98 到 164 中的字段为空”。有人可以帮助我了解将ser.read_until() 的输出转换为“人类可读”并表示图像中数据的形式需要做什么吗?我不需要有人为我编写代码,但我什至不知道从哪里开始。同样,这是我第一次这样做,所以我对发生的事情有点迷茫。

【问题讨论】:

  • 正在读取的数据看起来不正确,因此可能存在通信问题。
  • 啊……你说得对。当我只发送 1 个字节时,“命令”要求写入 9 个字节(我没有包括标题、校验和等字节)。我更新了输出
  • 很高兴听到 - 那么,您现在知道如何解码信息了吗?
  • 如果我这样做device_read[0:8].decode('ascii'),我会得到'M1830130'。如果我这样做device_read[8:48].decode('ascii'),我会得到'A2IMU v3.2.9.1 26.04.19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'。我看不出这是如何“解码”为“人类可读”的形式的?换句话说,为什么我的输出中还有\x00 之类的东西? device.read[48] 的输出也等于 (b'\x02'),它给出 2 而不是数据表中定义的 1 或 0。这让人相信要么我做错了什么,要么数据表不正确(不太可能)。
  • \x00 只是 0,这意味着 null,这是您不知道长度的字符串的标准结束标记。你可以像这样剥离它:``` ID_fw = device_read[8:48].decode('ascii') first_zero = ID_fw.find('\x00') if first_zero >= 0: ID_fw = ID_fw[0:first_zero] ```

标签: python python-3.x serial-port pyserial


【解决方案1】:

如果你想写一个十六进制值12(十进制18)的单字节,我相信你需要做的是ser.write(bytes([0x12])),相当于ser.write(bytes([18]))

看起来您的输出是 154 字节而不是 98 字节,其中大部分是非人类可读的。 但如果你确实有图表中描述的数据,你可以像这样分解它:

ID_sn = device_read[0:8].decode('ascii')
ID_fw = device_read[8:48].decode('ascii')
Press_Sens = device_read[48]

等等。

【讨论】:

  • 我输入了device_read[0:8],它返回了以下字节序列b'\xf3\x02\x00\x13\xda\xf1\xff\x00'。然后我将decode('ascii') 附加到命令中(例如device_read[0:8].decode('ascii'),我收到一个错误,上面写着UnicodeDecodeError: 'ascii' codec can't decode byte 0xf3 in position 0: ordinal not in range(128)。你能评论一下这是什么意思吗?
  • 一个字节为您提供 0 到 255 之间的值范围。有很多方法可以将这些值映射到含义(例如字符)。 ASCII 是其中一种方式,也是事实上的表示标准,每个字符使用一个且仅一个字节(这不是我们现在使用新系统的方式)。所以我猜RS232使用ASCII。 ASCII 仅使用从 0 到 127 的值(其余的留作自定义用途)。 0xf3 是 243 - 在该范围之外,因此不是有效的 ASCII(但可能是数字的一部分)。其他可能的编码是 UTF-8 和 UTF-16,但你会发现它们也不起作用。
  • 所以,我很确定你得到的不是表中描述的。否则,您实际上可以通过查看 b' 字符串来读取 98 中除两个字节之外的所有字节(并且您有更多字节)。您是否尝试过传入字节([0x12])而不是 0x12?
  • 编辑:不是 RS232,而是基于您发布的数据表 sn-p 的设备。
  • @ThatsRightJack -- 这两个字节基本上就像一个 16 位数字,只是数字只有 0-1 而不是 0-9。如果您有一个 16 位数字,并且一次读取 8 位数字,则将前 8 位数字部分(高阶部分)乘以 1 亿(1 和 8 个零),然后再将其添加到另一个一个,对吧?在二进制中,1 后面有 8 个零是数字 256(2 到 8 位)。数据手册告诉你的是,第二个字节是高位字节,所以你需要将第二个字节乘以 256,然后再将其添加到第一个字节。
【解决方案2】:

这不是答案,只是充实了@ozangds 的想法(可能会为您节省一些打字时间):

def decode_bytes(data, start, stop):
    return data[start:stop+1].decode('ascii').rstrip('\x00')


device_read = b'M1830130A2IMU v3.2.9.1 26.04.19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x0527641\x00\x00\x00IMHF R.1.0.0 10.28.2018 td:  6.500ms\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00'

ID_sn = decode_bytes(device_read, 0, 7)
ID_fw = decode_bytes(device_read, 8, 47)
Press_sens = device_read[48]
IMU_type = device_read[49]
IMU_sn = decode_bytes(device_read, 50, 57)
IMU_fw = decode_bytes(device_read, 58, 97)

label_fmt = '{:>10}: {!r}'
print(label_fmt.format('ID_sn', ID_sn))
print(label_fmt.format('ID_fw', ID_fw))
print(label_fmt.format('Press_sens', Press_sens))
print(label_fmt.format('IMU_type', IMU_type))
print(label_fmt.format('IMU_sn', IMU_sn))
print(label_fmt.format('IMU_fw', IMU_fw))

输出:

     ID_sn: 'M1830130'
     ID_fw: 'A2IMU v3.2.9.1 26.04.19'
Press_sens: 2
  IMU_type: 5
    IMU_sn: '27641'
    IMU_fw: 'IMHF R.1.0.0 10.28.2018 td:  6.500ms'

【讨论】:

    猜你喜欢
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    • 2018-08-06
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多