Frage

Ich habe ein Arduino mit meinem Computer verbunden, um eine Schleife ausgeführt wird, einen Wert über die serielle Schnittstelle an den Computer zurück sendet alle 100 ms.

Ich mag einen Python-Skript machen, die alle paar Sekunden von der seriellen Schnittstelle gelesen werden nur, so will ich es nur das letzte, was aus dem Arduino geschickt sehen.

Wie tun Sie dies in pyserial?

Hier ist der Code Ich habe versucht, die Arbeit does. Es liest die Zeilen der Reihe nach.

import serial
import time

ser = serial.Serial('com4',9600,timeout=1)
while 1:
    time.sleep(10)
    print ser.readline() #How do I get the most recent line sent from the device?
War es hilfreich?

Lösung

Vielleicht ich Ihre Frage bin Missverständnis, aber da es eine serielle Leitung ist, werden Sie alles aus dem Arduino nacheinander gesendet lesen müssen -. In der Arduino gepuffert werden werde, bis Sie sie lesen

Wenn Sie eine Statusanzeige haben, die die neueste Sache zeigt geschickt -. Verwenden, um einen Thread, der den Code in Ihrer Frage enthält (minus den Schlaf), und halten Sie die letzte vollständige Zeile gelesen als die neueste Linie von der Arduino

Update: mtasic der Beispielcode ist recht gut, aber wenn der Arduino eine Teilleitung gesendet hat, wenn inWaiting() genannt wird, werden Sie eine abgeschnittene Linie zu bekommen. Statt dem, was Sie tun möchten, ist die letzte vollständig Linie in last_received zu setzen, und halten Sie die Teillinie in buffer, so dass es auf das nächste Mal in der Schleife angehängt werden kann. So etwas wie folgt aus:

def receiving(ser):
    global last_received

    buffer_string = ''
    while True:
        buffer_string = buffer_string + ser.read(ser.inWaiting())
        if '\n' in buffer_string:
            lines = buffer_string.split('\n') # Guaranteed to have at least 2 entries
            last_received = lines[-2]
            #If the Arduino sends lots of empty lines, you'll lose the
            #last filled line, so you could make the above statement conditional
            #like so: if lines[-2]: last_received = lines[-2]
            buffer_string = lines[-1]

Im Hinblick auf die Verwendung von readline(): Hier ist, was die pyserial Dokumentation (leicht bearbeitet für Klarheit und mit einem Hinweis auf readlines ()) zu sagen hat:

  

Seien Sie vorsichtig, wenn „Readline-“ verwenden. Machen   angeben, einen Timeout, wenn die Öffnung   serielle Schnittstelle, sonst könnte es blockieren   für immer, wenn kein Newline-Zeichen ist   empfangen. Beachten Sie auch, dass „readlines ()“   funktioniert nur mit einem Timeout. Es   hängt von einer Zeitüberschreitung aufweist und   interpretiert, dass als EOF (Ende der Datei).

, die ich ganz vernünftig erscheint!

Andere Tipps

from serial import *
from threading import Thread

last_received = ''

def receiving(ser):
    global last_received
    buffer = ''

    while True:
        # last_received = ser.readline()
        buffer += ser.read(ser.inWaiting())
        if '\n' in buffer:
            last_received, buffer = buffer.split('\n')[-2:]

if __name__ ==  '__main__':
    ser = Serial(
        port=None,
        baudrate=9600,
        bytesize=EIGHTBITS,
        parity=PARITY_NONE,
        stopbits=STOPBITS_ONE,
        timeout=0.1,
        xonxoff=0,
        rtscts=0,
        interCharTimeout=None
    )

    Thread(target=receiving, args=(ser,)).start()

Diese Lösungen werden die CPU hog während für Zeichen warten.

Sie sollten mindestens einen blockierenden Aufruf tun lesen (1)

while True:
    if '\n' in buffer: 
        pass # skip if a line already in buffer
    else:
        buffer += ser.read(1)  # this will block until one more char or timeout
    buffer += ser.read(ser.inWaiting()) # get remaining buffered chars

... und tut die gespaltene Sache wie zuvor.

Sie können ser.flushInput() verwenden, um alle seriellen Daten zu spülen, die derzeit im Puffer ist.

Nachdem die alten Daten zu löschen, können Sie Benutzer ser.readline (), um die neuesten Daten aus dem seriellen Gerät zu erhalten.

Ich denke, es ist ein wenig einfacher als die anderen vorgeschlagenen Lösungen hier. Arbeitete für mich, hoffe, es für Sie geeignet ist.

Mit dieser Methode können Sie separat die Timeout-Steuerung für für jede Zeile alle Daten zu sammeln, und eine andere Timeout auf zusätzliche Leitungen zu warten.

# get the last line from serial port
lines = serial_com()
lines[-1]              

def serial_com():
    '''Serial communications: get a response'''

    # open serial port
    try:
        serial_port = serial.Serial(com_port, baudrate=115200, timeout=1)
    except serial.SerialException as e:
        print("could not open serial port '{}': {}".format(com_port, e))

    # read response from serial port
    lines = []
    while True:
        line = serial_port.readline()
        lines.append(line.decode('utf-8').rstrip())

        # wait for new data after each line
        timeout = time.time() + 0.1
        while not serial_port.inWaiting() and timeout > time.time():
            pass
        if not serial_port.inWaiting():
            break 

    #close the serial port
    serial_port.close()   
    return lines

Sie erhalten eine Schleife müssen alles geschickt, mit dem letzten Aufruf zu lesen, bis das Timeout auf readline () blockiert. Also:

def readLastLine(ser):
    last_data=''
    while True:
        data=ser.readline()
        if data!='':
            last_data=data
        else:
            return last_data

Leichte Änderung mtasic & Vinay Sajip Code:

Während ich diesen Code sehr hilfreich für mich für eine ähnliche Anwendung gefunden, die ich brauchte alle die Linien zurück von einem seriellen Gerät kommen, die Informationen in regelmäßigen Abständen senden würde.

ich das erste Element aus der Spitze Pop entschieden haben, aufzeichnen, und dann die übrigen Elemente wie den neuen anzuschließen und von dort weiter.

Ich weiß, dass dies nicht , was Greg für bittet, aber ich dachte, dass es sich wert als eine Randnotiz war.

def receiving(ser):
    global last_received

    buffer = ''
    while True:
        buffer = buffer + ser.read(ser.inWaiting())
        if '\n' in buffer:
            lines = buffer.split('\n')
            last_received = lines.pop(0)

            buffer = '\n'.join(lines)

Mit .inWaiting() innerhalb einer Endlosschleife kann problematisch sein. Es kann die gesamte CPU je nach Implementierung Schwein auf. Stattdessen würde ich mit einer bestimmten Größe der Daten empfiehlt gelesen werden. Also in diesem Fall sollte folgende zum Beispiel durchgeführt werden:

ser.read(1024)

Zu viel Komplikationen

Was ist der Grund, das Bytes Objekt durch Newline oder durch andere Array Manipulationen zu spalten? Ich schreibe die einfachste Methode, das wird Ihr Problem lösen:

import serial
s = serial.Serial(31)
s.write(bytes("ATI\r\n", "utf-8"));
while True:
    last = ''
    for byte in s.read(s.inWaiting()): last += chr(byte)
    if len(last) > 0:
        # Do whatever you want with last
        print (bytes(last, "utf-8"))
        last = ''

Hier ist ein Beispiel eines Wrapper verwenden, die Sie über die neueste Linie ohne 100% CPU

lesen kann
class ReadLine:
    """
    pyserial object wrapper for reading line
    source: https://github.com/pyserial/pyserial/issues/216
    """
    def __init__(self, s):
        self.buf = bytearray()
        self.s = s

    def readline(self):
        i = self.buf.find(b"\n")
        if i >= 0:
            r = self.buf[:i + 1]
            self.buf = self.buf[i + 1:]
            return r
        while True:
            i = max(1, min(2048, self.s.in_waiting))
            data = self.s.read(i)
            i = data.find(b"\n")
            if i >= 0:
                r = self.buf + data[:i + 1]
                self.buf[0:] = data[i + 1:]
                return r
            else:
                self.buf.extend(data)

s = serial.Serial('/dev/ttyS0')
device = ReadLine(s)
while True:
    print(device.readline())
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top