使用PySerial,能否等待数据?

27
我有一个Python程序,使用PySerial模块通过串口读取数据。我需要注意这两个条件:我不知道会有多少数据到达,也不知道何时期望数据。
基于此,我编写了以下代码片段:
#Code from main loop, spawning thread and waiting for data
s = serial.Serial(5, timeout=5)  # Open COM5, 5 second timeout
s.baudrate = 19200

#Code from thread reading serial data
while 1:
  tdata = s.read(500)    # Read 500 characters or 5 seconds

  if(tdata.__len__() > 0):        #If we got data
    if(self.flag_got_data is 0):  #If it's the first data we recieved, store it
      self.data = tdata        
    else:                         #if it's not the first, append the data
      self.data += tdata
      self.flag_got_data = 1

这段代码会一直循环从串口获取数据。我们将获取最多500个字符的数据,然后通过设置标志向主循环发出警报。如果没有数据存在,我们将返回睡眠并等待。

代码正在工作,但我不喜欢5秒的超时。我需要它是因为我不知道要期望多少数据,但我不喜欢它即使没有数据时也每隔5秒就会唤醒。

在进行read操作之前有没有任何方法检查何时可用数据?我在考虑像Linux中的select命令这样的东西。

注意:我找到了inWaiting()方法,但实际上那似乎只是将我的“睡眠”更改为轮询,所以这不是我想要的。我只想睡觉,直到有数据进来,然后去取它。

3个回答

26

好的,我已经准备好了一些内容。使用read()方法以及没有超时时间的inWaiting()方法的组合:

#Modified code from main loop: 
s = serial.Serial(5)

#Modified code from thread reading the serial port
while 1:
  tdata = s.read()           # Wait forever for anything
  time.sleep(1)              # Sleep (or inWaiting() doesn't give the correct value)
  data_left = s.inWaiting()  # Get the number of characters ready to be read
  tdata += s.read(data_left) # Do the read and combine it with the first character

  ... #Rest of the code

我觉得这样做可以得到我想要的结果,我猜在Python中没有单独的方法来实现这种功能。


3
“time.sleep(1)” 看起来像是一个很丑陋的hack,会在从数据中获取信息时添加大量的延迟。 - TJD
@TJD - 没有争议,似乎没有睡眠也可以工作,但是data_left变得非常不可靠(一个19个字符的字符串循环为3,然后10,然后6个“bank”进来)。所以我仍然对此不满意,但它更接近我所考虑的。等待数据,然后读取数据,然后再次休眠。 - Mike
我不认为这是“不可靠”的。这正是在您读取它们时字节到达的方式。您可能想要使用interCharTimeout,这样可以让您检测接收器何时停止看到任何新字节。 - TJD

14
您可以将timeout = None设置成空值,那么read调用会一直阻塞,直到请求的字节数达到。如果您想要等待数据到达,请使用超时None进行read(1)。如果您想要在不阻塞的情况下检查数据,请使用超时为零的read(1),并检查它是否返回任何数据。
(请参阅文档https://pyserial.readthedocs.io/en/latest/

1
但我认为使用5秒的超时并让它唤醒并立即返回读取也没有任何问题。这非常简单,不会有任何可测量的开销。 - TJD
感谢提供的信息,我确实看了文档,但无法弄清楚如何让它做我想要的事情。我同意在桌面机器上使用包含超时的循环不会非常昂贵,但(出自嵌入式背景),这使我感到不舒服,因为这样的轮询方式感觉是错误的。 - Mike
我认为这是更好的解决方案(至少对于我的需求来说)。谢谢分享。 - Marcel

2
def cmd(cmd,serial):
    out='';prev='101001011'
    serial.flushInput();serial.flushOutput()
    serial.write(cmd+'\r');
    while True:
        out+= str(serial.read(1))
        if prev == out: return out
        prev=out
    return out

这样调用:

cmd('ATZ',serial.Serial('/dev/ttyUSB0', timeout=1, baudrate=115000))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接