40

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

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()

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

将 "while True"" 更改为 "while ser.readline():" 不会打印 "test",这很奇怪,因为它在 Python 2.7 中有效。有什么想法可能出错吗?

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

4

4 回答 4

57

完全没有必要使用单独的线程。只需为您的无限 while 循环执行此操作(在 Python 3.2.3 中测试)。我在我的eRCaGuy_PyTerm串行终端程序中使用了这种技术(在代码中搜索inWaiting()or in_waiting

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

ser = serial.Serial(port='COM4', baudrate=9600)

while (True):
    # Check if incoming bytes are waiting to be read from the serial input 
    # buffer.
    # NB: for PySerial v3.0 or later, use property `in_waiting` instead of
    # function `inWaiting()` below!
    if (ser.inWaiting() > 0):
        # read the bytes and convert from binary array to ASCII
        data_str = ser.read(ser.inWaiting()).decode('ascii') 
        # print the incoming string without putting a new-line
        # ('\n') automatically after every print()
        print(data_str, end='') 

    # Put the rest of your code you want here
    
    # Optional, but recommended: sleep 10 ms (0.01 sec) once per loop to let 
    # other threads on your PC run during this time. 
    time.sleep(0.01) 

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

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

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

更新:

  • 2018 年 12 月 27 日:添加了关于in_waitingvs的评论inWaiting()。感谢@FurkanTürkal 在下面的评论中指出这一点。请参阅此处的文档:https ://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial.in_waiting 。
  • 2018 年 10 月 27 日:添加睡眠以让其他线程运行。
  • 文档:https ://docs.python.org/3/library/time.html#time.sleep
  • 感谢@RufusV2 在评论中提出这一点。

多线程注意事项:

尽管如上所示读取串行数据不需要使用多个线程,但以非阻塞方式读取键盘输入却可以。因此,为了完成非阻塞键盘输入读取,我写了这个答案:如何读取键盘输入?.

参考:

  1. 官方 pySerialserial.Serial()类 API - https://pyserial.readthedocs.io/en/latest/pyserial_api.html
于 2016-08-04T04:59:03.713 回答
49

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

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

于 2013-07-10T07:15:57.863 回答
3

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

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

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

于 2019-06-19T15:19:18.923 回答
0

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

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
于 2018-04-25T06:36:05.553 回答