2009-07-07 3 views
15

Ich habe einen Arduino an meinem Computer angeschlossen, der eine Schleife ausführt und alle 100   ms einen Wert über die serielle Schnittstelle an den Computer sendet.pyserial - So lesen Sie die letzte von einem seriellen Gerät gesendete Zeile

Ich möchte ein Python-Skript, das nur alle paar Sekunden von der seriellen Schnittstelle liest, so dass ich möchte, dass es nur die letzte Sache von der Arduino gesendet sehen.

Wie machen Sie das in Pyserial?

Hier ist der Code, den ich versuchte, der nicht funktioniert. Es liest die Zeilen sequentiell.

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? 

Antwort

27

Vielleicht bin ich Missverständnis Ihre Frage, aber da es eine serielle Leitung ist, werden Sie von der Reihe nach lesen, um alles Arduino geschickt - es in der Arduino gepuffert werden werde, bis Sie lesen es.

Wenn Sie eine Statusanzeige haben möchten, die das neueste gesendet zeigt - verwenden Sie einen Thread, der den Code in Ihrer Frage enthält (minus den Schlaf), und halten Sie die letzte vollständige Zeile als die letzte Zeile aus dem Arduino lesen.

Update:mtasic ‚s Beispiel-Code ist recht gut, aber wenn der Arduino eine Teilleitung gesendet hat, wenn inWaiting() genannt wird, werden Sie eine abgeschnittene Linie bekommen. Stattdessen, was Sie tun möchten, ist die letzte komplette Linie in last_received, und halten Sie die Teillinie in buffer, so dass es an das nächste Mal um die Schleife angehängt werden kann. 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] 

In Bezug 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 bei der Verwendung von " Zeile lesen". Geben Sie ein Zeitlimit beim Öffnen der seriellen Schnittstelle an, andernfalls könnte für immer gesperrt werden, wenn kein Newline-Zeichen empfangen wird. Beachten Sie auch, dass "readlines()" nur mit einem Timeout funktioniert. Es hängt von einem Timeout ab und interpretiert das als EOF (Dateiende).

das scheint mir ziemlich sinnvoll!

10
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() 
+0

Nun, die in der Summe liest von dem, was in dem Puffer erhalten. Mein Eindruck ist, dass der Fragesteller das begrenzt, was das Arduino durch Zeilenumbrüche sendet, so dass es wahrscheinlich nicht mit der Empfangspuffergröße übereinstimmt. – JosefAssad

+0

Also last_received wird immer haben, was ich brauche? Gibt es eine Möglichkeit, dies mit Readline zu tun? – Greg

+0

das ist richtig, nach Ihrer Frage – mtasic85

2

Sie benötigen eine Schleife, um alle gesendeten Daten zu lesen. Der letzte Aufruf von readline() blockiert bis zum Timeout. Also:

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

leichte Modifikation & Vinay Sajip Code mtasic:

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

Ich entschied mich, das erste Element von der Spitze aufzuschneiden, aufzuzeichnen und dann die restlichen Elemente als den neuen Puffer wiederzuvereinigen und von dort fortzusetzen.

Mir ist klar, dass dies nicht was Greg für bat, aber ich dachte, dass es es 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) 
7

Diese Lösungen werden die CPU während des Wartens auf Zeichen beschneiden.

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 tun die gespaltene Sache wie zuvor.

2

Die Verwendung von .inWaiting() innerhalb einer Endlosschleife kann problematisch sein. Es kann die gesamte CPU abhängig von der Implementierung hog. Stattdessen würde ich empfehlen, eine bestimmte Größe der zu lesenden Daten zu verwenden. Also in diesem Fall sollte folgende zum Beispiel geschehen:

ser.read(1024) 
3

Diese Methode, die Sie separat erlaubt, die Timeout-Steuerung für die Erfassung aller Daten für jede Zeile, und eine andere Timeout für zusätzliche Linien 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 
+0

Danke dafür. Ich konnte keine andere Antwort finden, die tatsächlich alle Zeilen einer seriellen Antwort zurückgeben würde. – Timmay

2

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

Nachdem Sie die alten Daten gelöscht haben, können Sie ser.readline() verwenden, um die neuesten Daten vom seriellen Gerät abzurufen.

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

2

Zu viele Komplikationen

Was ist der Grund, das Bytes Objekt durch Newline oder durch andere Array Manipulationen zu spalten? Ich schreibe die einfachste Methode, die 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 = '' 
Verwandte Themen