从两个串口异步读取

Posted

技术标签:

【中文标题】从两个串口异步读取【英文标题】:Read from two serial ports asynchronously 【发布时间】:2016-09-27 01:37:04 【问题描述】:

我想在 Linux 上的 python 中同时读取两个(或更多)串行端口(/dev/ttyUSB0 等)。我想从每个端口读取完整的行(以有数据的为准)并按收到的顺序处理结果(没有竞争条件)。作为一个简单的示例,可以将这些行写入单个合并文件。

我认为这样做的方法是基于 pyserial,但我无法完全弄清楚如何去做。 Pyserial 使用asyncio 和threads 进行非阻塞读取。 Asyncio 被标记为实验性的。如果在asyncio.Protocol.data_received() 中完成处理,我认为不会有任何竞争条件。在线程的情况下,处理可能必须由互斥体保护。

也许这也可以不在pyserial中完成。这两个串口可以作为文件打开,然后在数据可用时使用select()读取。

【问题讨论】:

创建两个线程,从串口读取数据并将要处理的数据放入队列中。查找“生产者消费者python”。 @AlexHall 听起来很容易解决这个问题。使用threads+queue+pyserial 发布答案以立即投票并接受:) 【参考方案1】:

考虑使用aioserial。

这是一个例子:

import asyncio
import concurrent.futures
import queue

import aioserial


async def readline_and_put_to_queue(
        aioserial_instance: aioserial.AioSerial,
        q: queue.Queue):
    while True:
        q.put(await aioserial_instance.readline_async())


async def process_queue(q: queue.Queue):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        while True:
            line: bytes = await asyncio.get_running_loop().run_in_executor(
                    executor, q.get)
            print(line.decode(errors='ignore'), end='', flush=True)
            q.task_done()


q: queue.Queue = queue.Queue()
aioserial_ttyUSB0: aioserial.AioSerial = \
        aioserial.AioSerial(port='/dev/ttyUSB0')
aioserial_ttyUSB1: aioserial.AioSerial = \
        aioserial.AioSerial(port='/dev/ttyUSB1', baudrate=115200)

asyncio.run(asyncio.wait([
    readline_and_put_to_queue(aioserial_ttyUSB0, q),
    readline_and_put_to_queue(aioserial_ttyUSB1, q),
    process_queue(q),
]))

【讨论】:

【参考方案2】:

正如@AlexHall 在评论中所建议的,这里有一个解决方案,每个串口使用一个线程和一个队列来同步访问:

import serial
import Queue
import threading

queue = Queue.Queue(1000)

def serial_read(s):
    while True:
        line = s.readline()
        queue.put(line)

serial0 = serial.Serial('/dev/ttyUSB0')
serial1 = serial.Serial('/dev/ttyUSB1')

thread1 = threading.Thread(target=serial_read, args=(serial0,),).start()
thread2 = threading.Thread(target=serial_read, args=(serial1,),).start()

while True:
    line = queue.get(True, 1)
    print line

也许可以写得更优雅,但它确实有效。

【讨论】:

【参考方案3】:

您可以尝试按顺序获取值并将其存储在变量中:

a = data1.read ()
b = data2.read ()

然后按顺序处理:

If len (a) != 0 or len (b ) != 0:
             Process a
             Process b

如果其中一个或两个值有数据,则使用此方法进行处理

【讨论】:

在您提出的第一个解决方案中,data2 可能有很多行可供读取,但 data1 没有,因此第一个读取块。这正是我想要避免的。在使用线程的第二种解决方案中,单个函数如何在没有竞争条件的情况下按顺序处理两个线程读取的行? 你只需要在你的代码中插入...线程是多个活动;同时。如果它有效,请通知我。 不,它不起作用。我需要“执行操作”按顺序发生,而不是同时发生,但使用您拥有的代码可以同时发生。 那么你想从2个串口接收数据并按顺序处理,即使你没有在一个端口接收? 是的,没错,按顺序处理。

以上是关于从两个串口异步读取的主要内容,如果未能解决你的问题,请参考以下文章

c#从串口读取数据怎样读一个就清空缓存

Python从串口读取

QT:从串口连续读取数据的问题

Java从串口读取数据

C#找不到文件(从串口读取)

从串口读取数据时出错