【问题标题】:python - How to read data fully from serial port?python - 如何从串口完全读取数据?
【发布时间】:2018-12-13 00:13:18
【问题描述】:

我正在尝试使用基于PySerial 库的以下代码读取写入串行端口的 JSON 字符串:

while True:
    if serial_port.in_waiting > 0:
        buffer = serial_port.readline()
        print('buffer=', buffer)
        ascii = buffer.decode('ascii')
        print('ascii=', ascii)

我试图嗅探端口上的数据,并确保数据被完整写入而没有任何分散:

jpnevulator --ascii --tty "/dev/ttyACM1" --read
7B 22 30 22 3A 31 7D                            {"0":1}
7B 22 30 22 3A 32 7D                            {"0":2}
7B 22 30 22 3A 33 7D                            {"0":3}
7B 22 30 22 3A 34 7D                            {"0":4}
7B 22 30 22 3A 35 7D                            {"0":5}
7B 22 30 22 3A 36 7D                            {"0":6}

但是使用的代码导致读取的数据分散,因此显示如下结果:

buffer= b'{"0'
ascii= {"0
buffer= b'":1}'
ascii= ":1}
buffer= b'{"'
ascii= {"
buffer= b'0":2'
ascii= 0":2
buffer= b'}'
ascii= }

此外,当我使用 read() 而不是 readline() 时,我会得到相同的行为:

buffer= b'{'
data_str= {
buffer= b'"'
data_str= "
buffer= b'3'
data_str= 3
buffer= b'"'
data_str= "
buffer= b':'
data_str= :
buffer= b'1'
data_str= 1
buffer= b'}'
data_str= }

我什至尝试使用使用相同库的another code,但遇到了同样的问题。

我不确定我为什么会遇到这种行为。

【问题讨论】:

  • Readline() 可能不是您所追求的,因为您的数据似乎没有以换行符“\n”终止,因此请尝试使用 read() 代替,同样您可能也是不耐烦,当串口只接收到第一个字符时,它会说那里有数据,所以你的 buffer.decode 在只输入 1 或 2 个字符时可能会表现得很奇怪
  • @Reroute 正如我在编辑中指出的那样,使用 read() 而不是解决问题。
  • 正如@Reroute 在评论末尾所建议的那样。一旦有任何可用输入 (.in_waiting > 0),您就会立即采取行动。你有什么办法可以告诉你有一个完整的“消息”然后才读?或者......您仍然可以逐个阅读,但您必须自己组装所需的内容......即收集到右括号,然后开始新的ascii。你输入的大小是固定的吗?那会让你的生活更轻松。 ;)

标签: python string io serial-port pyserial


【解决方案1】:

我会尝试解决这个问题。 :) 您的循环等待任何输入变为可用serial_port.in_waiting > 0。因此,您看到的行为。一旦可以获取任何内容,就会开始读取。 PySerial 似乎没有任何额外的抽象可以让您知道最后准备好的字节将是一个 ASCII 花括号字符(我真的只是浏览了文档)。您始终可以在 Python 脚本中应用通用的 read as stuff 解决方案。

不过先问一个问题。您的输入示例表明您将处理同样大小的字符串/JSON?我们真的应该那么幸运吗?如果是这样,您可以等到该或更多字节可用,然后将所需大小读入您的buffer

否则您的代码会发生变化:

buffer = bytes()  # .read() returns bytes right?
while True:
    if serial_port.in_waiting > 0:
        buffer += serial_port.read(serial_port.in_waiting)
        try:
            complete = buffer[:buffer.index(b'}')+1]  # get up to '}'
            buffer = buffer[buffer.index(b'}')+1:]  # leave the rest in buffer
        except ValueError:
            continue  # Go back and keep reading
        print('buffer=', complete)
        ascii = buffer.decode('ascii')
        print('ascii=', ascii)

注意 1:我认为 serial_port.in_waiting 理论上可以在 if 和 read 之间改变,但我也认为未读字节只是留在缓冲区中,我们很好。

注意2:这种方法有点幼稚,并且没有考虑到您可能还阅读了两段 JSON 代码。

注意 3:如果是这种情况,它也不考虑 JSON 中的嵌套映射。

希望它仍然有用。底线。除非处理固定大小的输入或以任何其他方式让 pySerial 为您提供所需的内容分块,否则您必须在脚本中读取内容并对其进行处理。


更新:反映 cmets 中的讨论。

您的问题实际上是您只是在查看串行端口上的(流)字节。在那个级别上,对传递的数据没有任何有用的理解。您需要更高级别(应用程序或介于两者之间的层)来理解传入的内容。换句话说,解析封装传输数据的协议。

事实上,如果我们知道代表 JSON 的字符串(字节串)是传递的内容(服务器作为协议,封装/表示数据(结构)的方式),那可以工作,但重组需要发生在原始串行通信之上。我们的应用程序(或库/模块)可以读取原始串行数据,从中理解并将它们提供给更高级别。

【讨论】:

  • 感谢您的回答。输入的大小不是固定的,需要立即检索。不幸的是,当不同的写入发生在短时间内(重叠来自不同缓冲区的分散读取)时,这种连接方法将失败。
  • 在这种情况下,您需要一个不同的触发器来执行读取,然后只需 serial_port.in_waiting 变为非零。您不仅需要知道任何/第一个字节可用,还需要知道完整的数据“块”已准备好。
  • 现在每次读取显示单个字符 - 这是我为解决您的意思而编写的代码:gist.github.com/kaisbe/281b5eef1edffa82d1427b0d7b913e6c
  • 我刚刚浏览了它的文档,我没有看到任何暗示任何其他信号设施的东西。但这确实是有道理的。如果你只看串行接口,你会得到(流)字节。如果没有额外的信息,你不能做任何其他事情,封装你的内容的一些协议说,我从这里开始,我在这里完成。这将是特定于应用程序并在我假设的应用程序中处理。例如,打开和关闭大括号可以用于此目的。但我不知道你的设置,可能还有更多内容,而不是我可以根据需要读取和解析的流。
  • 换句话说。照原样,是的,没有关于设置的更多细节和一点创意。恐怕你不能只用.read() 就得到一个代表字符串的JSON。
【解决方案2】:

这是数据通信中常见的问题:什么时候收到完整的帧?

如果您可以控制双方,我建议您在协议中添加额外的框架。否则,如果您收到完整的 JSON 对象,则需要逐字符检查。

一个非常简单的协议是ndjson,它只需要你在帧之间添加'\n',并且避免在你的payload中插入换行符。如果您在 JSON 字符串中有实际的换行符,那么如果您使用的是 JSON 库,它们将被自动转义。

示例作者:

# Writing ndjson frames
frames = [
    {"0": "1"},
    {"0": "2"},
]
for frame in frames:
    port.write(json.dumps(frame, separators=(',', ':')))  # No extra whitespace or newlines
    port.write('\n')  # ndjson protocol separator

可以使用 pyserial.readline() 读取 ndjson 帧流。

请注意,您需要指定超时才能退出程序,否则readline() 将永远阻塞。

示例阅读器假设 port 以超时打开:

while True:
    data = port.readline()  # Blocks until a complete frame is received or timeout
    if data:
        d = json.loads(data)
        print("Received object: %r" % d)
    else:
        if should_exit:
            break

【讨论】:

    猜你喜欢
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 2013-07-25
    相关资源
    最近更新 更多