webentwicklung-frage-antwort-db.com.de

Lesen von seriellen Daten in Echtzeit in Python

Ich verwende ein Skript in Python, um Daten von einem PIC-Mikrocontroller über die serielle Schnittstelle mit 2 Mbit/s zu erfassen.

Der PIC arbeitet mit perfektem Timing bei 2 Mbit/s, auch der serielle FTDI-USB-Anschluss funktioniert mit 2 Mbit/s hervorragend (beide mit einem Oszilloskop überprüft).

Ich sende Nachrichten (Größe von ca. 15 Zeichen) ca. 100-150x pro Sekunde und die Anzahl erhöht sich (um zu überprüfen, ob ich Nachrichten verloren habe und so weiter)

Auf meinem Laptop läuft Xubuntu als virtuelle Maschine, ich kann die serielle Schnittstelle über PuTTY und über mein Skript (Python 2.7 und pySerial) lesen

Das Problem:

  • Wenn ich die serielle Schnittstelle über PuTTY öffne, sehe ich alle Nachrichten (der Zähler in der Nachricht erhöht sich 1 um 1). Perfekt!
  • Wenn ich die serielle Schnittstelle über pySerial öffne, sehe ich alle Nachrichten, aber anstatt 100-150x pro Sekunde zu empfangen, erhalte ich sie mit ungefähr 5 pro Sekunde (die Nachricht wird immer noch 1 mal 1 erhöht), aber sie werden wahrscheinlich in einem gewissen Puffer gespeichert, als wenn ich mich ausschalte Der PIC, ich kann in die Küche gehen und zurückkommen und ich erhalte immer noch Nachrichten.

Hier ist der Code (ich habe den größten Teil des Codes weggelassen, aber die Schleife ist dieselbe):

ser = serial.Serial('/dev/ttyUSB0', 2000000, timeout=2, xonxoff=False, rtscts=False, dsrdtr=False) #Tried with and without the last 3 parameters, and also at 1Mbps, same happens.
ser.flushInput()
ser.flushOutput()
While True:
  data_raw = ser.readline()
  print(data_raw)

Weiß jemand, warum pySerial so viel Zeit braucht, um von der seriellen Schnittstelle bis zum Ende der Leitung zu lesen? Irgendeine Hilfe?

Ich möchte das in Echtzeit haben.

Vielen Dank

32
Vasco Baptista

Sie können inWaiting() verwenden, um die Anzahl der in der Eingabewarteschlange verfügbaren Bytes abzurufen.

Dann können Sie read() verwenden, um die Bytes zu lesen.

While True:
    bytesToRead = ser.inWaiting()
    ser.read(bytesToRead)

Warum nicht readline()in diesem Fall von Docs verwenden:

Read a line which is terminated with end-of-line (eol) character (\n by default) or until timeout.

Sie warten bei jeder Messung auf das Timeout, da es auf eol wartet. der serielle Eingang Q bleibt gleich, es ist nur eine Menge Zeit, um zum "Ende" des Puffers zu gelangen.

34
Kobi K

Sie müssen das Timeout auf "Keine" setzen, wenn Sie die serielle Schnittstelle öffnen:

ser = serial.Serial(**bco_port**, timeout=None, baudrate=115000, xonxoff=False, rtscts=False, dsrdtr=False) 

Dies ist ein blockierender Befehl. Sie warten also, bis Sie Daten erhalten, die am Ende eine neue Zeile (\ n oder\r\n) enthalten: line = ser.readline ()

Sobald Sie die Daten haben, wird es so schnell wie möglich zurück.

4
Fabian Meier

Von das Handbuch :

Mögliche Werte für den Parameter Timeout:… x Timeout auf x Sekunden einstellen

und

readlines (sizehint = None, eol = '\ n') Lies eine Liste von Zeilen bis zum Timeout. sizehint wird ignoriert und ist nur aus Gründen der API-Kompatibilität mit integrierten Dateiobjekten vorhanden.

Beachten Sie, dass diese Funktion nur bei einer Zeitüberschreitung zurückgegeben wird.

Ihr readlines wird also höchstens alle 2 Sekunden zurückkehren. Verwenden Sie read() wie von Tim vorgeschlagen.

2
msw

Eine sehr gute Lösung hierfür findet sich hier :

Hier ist eine Klasse, die als Wrapper für ein pyseriales Objekt dient. Sie können Zeilen ohne 100% CPU lesen. Es enthält keine Timeout-Logik. Wenn eine Zeitüberschreitung auftritt, gibt self.s.read(i) eine leere Zeichenfolge zurück, und Sie möchten möglicherweise eine Ausnahme auslösen, um die Zeitüberschreitung anzuzeigen.

Es soll laut Autor auch schnell sein:

Der folgende Code gibt mir 790 kB/Sek., Während das Ersetzen des Codes durch die Readline-Methode von Pyserial nur 170 kB/Sek. Ergibt.

class ReadLine:
    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)

ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)

while True:

    print(rl.readline())
0
Joe