python-如何从串行端口完全读取数据?

时间:2018-07-04 14:06:03

标签: python string io serial-port pyserial

我正在尝试使用基于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,但是遇到了同样的问题。

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

2 个答案:

答案 0 :(得分:1)

我将尝试解决这个问题。 :)您的循环等待任何输入变为serial_port.in_waiting > 0。因此,您所看到的行为。一旦可以获取任何内容,便开始读取。 PySerial似乎没有任何其他可用的抽象来让您知道诸如最后准备好的字节之类的东西是ASCII大括号字符(我实际上只是在文档中进行过扫描)。您总是可以应用随书随用的通用解决方案,并且可以在Python脚本中从中受益。

第一个问题。您的输入示例建议您要处理大小相等的字符串/ 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根据需要将内容分块,否则您必须阅读内容并在脚本中进行处理。


更新:在评论中反映讨论。

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

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

答案 1 :(得分:1)

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

如果您对双方都有控制权,建议您为协议添加额外的框架。否则,如果您已收到完整的JSON对象,则需要逐字符检查。

一个非常简单的协议是ndjson,它仅要求您在帧之间添加# 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 ,并且避免在有效载荷中插入换行符。如果您在JSON字符串中包含实际的换行符,那么在使用JSON库时,它们将自动转义。

示例作家:

readline()

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

请注意,您需要指定一个超时时间才能退出程序,因为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 超时打开的示例阅读器:

{{1}}