Python PySerial 非阻塞读取循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17553543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 08:30:24  来源:igfitidea点击:

PySerial non-blocking read loop

pythonpython-3.xnonblockingpyserial

提问by DominicM

I am reading serial data like this:

我正在读取这样的串行数据:

connected = False
port = 'COM4'
baud = 9600

ser = serial.Serial(port, baud, timeout=0)

while not connected:
    #serin = ser.read()
    connected = True

    while True:
        print("test")
        reading = ser.readline().decode()

The problem is that it prevents anything else from executing including bottle py web framework. Adding sleep()won't help.

问题是它阻止了其他任何东西的执行,包括 Bottle py Web 框架。添加sleep()不会有帮助。

Changing "while True"" to "while ser.readline():" doesn't print "test", which is strange since it worked in Python 2.7. Any ideas what could be wrong?

将 "while True"" 更改为 "while ser.readline():" 不会打印 "test",这很奇怪,因为它在 Python 2.7 中工作。任何想法可能是错误的?

Ideally I should be able to read serial data only when it's available. Data is being sent every 1,000 ms.

理想情况下,我应该只能在可用时读取串行数据。每 1,000 毫秒发送一次数据。

采纳答案by Fredrik H??rd

Put it in a separate thread, for example:

把它放在一个单独的线程中,例如:

import threading
import serial

connected = False
port = 'COM4'
baud = 9600

serial_port = serial.Serial(port, baud, timeout=0)

def handle_data(data):
    print(data)

def read_from_port(ser):
    while not connected:
        #serin = ser.read()
        connected = True

        while True:
           print("test")
           reading = ser.readline().decode()
           handle_data(reading)

thread = threading.Thread(target=read_from_port, args=(serial_port,))
thread.start()

http://docs.python.org/3/library/threading

http://docs.python.org/3/library/threading

回答by Gabriel Staples

Using a separate thread is totally unnecessary. Just do this for your infinite while loop instead (Tested in Python 3.2.3):

使用单独的线程是完全没有必要的。只需为您的无限 while 循环执行此操作(在 Python 3.2.3 中测试):

import serial
import time # Optional (if using time.sleep() below)

while (True):
    # NB: for PySerial v3.0 or later, use property `in_waiting` instead of function `inWaiting()` below!
    if (ser.inWaiting()>0): #if incoming bytes are waiting to be read from the serial input buffer
        data_str = ser.read(ser.inWaiting()).decode('ascii') #read the bytes and convert from binary array to ASCII
        print(data_str, end='') #print the incoming string without putting a new-line ('\n') automatically after every print()
    #Put the rest of your code you want here
    time.sleep(0.01) # Optional: sleep 10 ms (0.01 sec) once per loop to let other threads on your PC run during this time. 

This way you only read and print if something is there. You said, "Ideally I should be able to read serial data only when it's available." This is exactly what the code above does. If nothing is available to read, it skips on to the rest of your code in the while loop. Totally non-blocking.

这样你只能在有东西的时候阅读和打印。你说,“理想情况下,我应该能够仅在可用时读取串行数据。” 这正是上面代码所做的。如果没有可供读取的内容,它会跳到 while 循环中的其余代码。完全无阻塞。

(This answer originally posted & debugged here: Python 3 non-blocking read with pySerial (Cannot get pySerial's "in_waiting" property to work))

(这个答案最初发布并在这里调试:Python 3 non-blocking read with pySerial (Cannot get pySerial's "in_waiting" property to work)

pySerial documentation: http://pyserial.readthedocs.io/en/latest/pyserial_api.html

pySerial 文档:http://pyserial.readthedocs.io/en/latest/pyserial_api.html

UPDATE:

更新:

Note on multi-threading:

多线程注意事项:

Even though reading serial data, as shown above, does notrequire using multiple threads, reading keyboard input in a non-blocking manner does. Therefore, to accomplish non-blocking keyboard input reading, I've written this answer: How to read keyboard-input?.

即使读串行数据,如上所示,也没有要求使用多个线程,读取键盘输入在非阻塞方式确实。因此,为了完成非阻塞键盘输入读取,我写了这个答案:如何读取键盘输入?.

回答by William Kuipers

Use a timer driven event to test and read the serial port. Untested example:

使用定时器驱动事件来测试和读取串行端口。未经测试的示例:

import threading
class serialreading():
    def __init__(self):
        self.active = True
        self.test() 
    def test(self):
        n_in =comport.in_waiting()
        if n_in> 0:
            self.data = self.data + comport.read(size=n_in)
    if len(self.data) > 0: 
        print(self.data)
        self.data=""
    if self.active:
        threading.Timer(1, test).start()  # start new timer of 1 second
    def stop(self):
        self.active = False

回答by Mohammad Azim

I would warn against using blocking IO in a thread. Remember Python has a GILand at one time only one thread can execute. Now please note that pyserial module is a wrapper over an OS implementation of accessing the serial port. That means it calls code external to the Python. If that code blocks, then the interpreter also get blocked and nothing will execute in the Python program, even the main thread.

我会警告不要在线程中使用阻塞 IO。记住 Python 有一个GIL,并且一次只能执行一个线程。现在请注意,pyserial 模块是访问串行端口的操作系统实现的包装器。这意味着它调用 Python 外部的代码。如果该代码阻塞,那么解释器也会被阻塞,Python 程序甚至主线程都不会执行任何操作。

This can even happen when using non-blocking IO or timeout based polling if the underlying device driver does not implement timeout well.

如果底层设备驱动程序没有很好地实现超时,则在使用非阻塞 IO 或基于超时的轮询时,甚至会发生这种情况。

A more robust approach is to use multiprocessingmodule with a queue. Run serial read code in a separate process. This will make sure main and other threads don't block and the program can exit in clean way.

更健壮的方法是使用带有队列的多处理模块。在单独的进程中运行串行读取代码。这将确保主线程和其他线程不会阻塞,并且程序可以以干净的方式退出。