我有一个Arduino开发板,每秒输出一个类似“ 287、612、109、1134”的字符串。我的代码尝试读取此字符串并将其转换为风速和风向。最初它工作正常,但最终在其中一个readine中,它仅读取字符串中导致错误的第一个数字。如何让代码获取完整的字符串,而不仅仅是第一个数字?

import serial, time, csv, decimal

#for Mac
port = '/dev/cu.usbserial-A401316V'

ser = serial.Serial(port , baudrate=57600, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=2)

while True:

    time.sleep(2)

    data = ser.readline( eol="\r\n").strip()

    data = data.split(',')

    if len(data) > 0:

        if data[0] != '2501':

            s = float( 1000 / float(data[0]) )

            print  'wind speed %.1f' %(round(float((2.5 * s)), 1))
            print 'wind direction', round((int(data[1]) - (50))  * .39, 0)
            print 'software version', data[2], '\n'
        else:
            print 'wind speed is zero'
            print 'wind direction', round((int(data[1]) - (50))  * .39, 0)
            print 'software version', data[2]

ser.close()

最佳答案

在没有看到来自串行端口的更多错误数据的情况下,我可以说,最常见的原因是您在行(甚至是空行)上看到单个数字是由于Arduino程序向端口发送数据和您的程序尝试读取该数据。如果您在data之后打印data=ser.readline()并注释掉其余处理,我会打赌,您会在输出中找到类似以下内容的行:

252, 236, 218, 1136
251, 202, 215
2, 1353
199, 303, 200, 1000
259, 231, 245, 993
28
4, 144, 142, 1112
245, 199, 143, 1403

251, 19
2, 187, 1639
246, 235, 356, 1323


数据分成几行甚至是空行的原因是该程序试图在写入期间或写入之间从串行连接读取数据。发生这种情况时,readline()获得可用的内容(即使是部分/未写入的内容),并在找到的内容的末尾抛出\n

解决此问题的方法是确保Arduino在我们读取之前已经完成了数据发送,并且有供我们读取的数据。处理此问题的一个好方法是使用生成器,该生成器仅在完成主循环时才产生行(例如,它们以\r\n结尾,表示对端口的写入结束)。

def get_data():
    ibuffer = ""  # Buffer for raw data waiting to be processed
    while True:
        time.sleep(.1)  # Best between .1 and 1
        data = ser.read(4096)  # May need to adjust read size
        ibuffer += data  # Concat data sets that were possibly split during reading
        if '\r\n' in ibuffer:  # If complete data set
            line, ibuffer = ibuffer.split('\r\n', 1)  # Split off first completed set
            yield line.strip('\r\n')  # Sanitize and Yield data


yield使此函数成为生成器,您可以调用该生成器以获取下一个完整的数据集,同时将由read()拆分的任何内容保留在缓冲区中,以等待下一个read()数据集可用于的下一个被串联。将yield视为return,但不传递值并离开函数/循环,它传递值并等待next()被调用,它将在从下一个地方开始的地方进行下一次传递通过循环。使用此功能,程序的其余部分将如下所示:

import serial, time, csv, decimal

port = '/dev/cu.usbserial-A401316V'
ser = serial.Serial(port , baudrate=57600, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=2)

def get_data():
    """
    The above function here
    """

ser_data = get_data()
while True:
    data = ser_data.next().replace(' ','').split(',')

    if len(data) > 0:
        """
        data processing down here

        """
ser.close()


根据您的设置,例如,如果丢失了串行连接,或者要在get_data()声明周围使用try/except,则可能需要在data中抛出一个条件中断。

值得注意的是,除了上述内容外,我所做的另一件事是将您的ser.readline(eol)更改为字节大小的ser.read(4096)。 PySerial的readline()eol实际上是最新版本Python的deprecated

希望这会有所帮助;或者,如果您还有其他问题,希望它至少能给您一些思路,以使他们步入正轨。

关于python - 从Arduino读取字符串,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23498853/

10-14 17:57
查看更多