Seite 1 von 1

Byte aus ser. Schnittstelle lesen und in String schreiben

Verfasst: Freitag 26. April 2013, 10:30
von dan.mue
Hallo,

zuerst möchte ich sagen das ich Python-Neuling bin.

Zyklisch wird an ein Gerät ein "set+0x0D" Befehl zu Status abfrage gesendet. Dieser antwortet mit einem unbestimmt langen String entsprechend dem Status.
Nun möchte ich einzelne Bytes aus der seriellen Schnittstelle lesen und in einen String speichern bis Carriage Return empfangen wird.
Dann soll der ganze String ausgegeben werden. Die einzelnen Bytes auslesen klappt, aber nicht das Speichern in den String.

Code: Alles auswählen

import serial
import time

ser = serial.Serial("/dev/ttyAMA0", 115200, timeout=10)
if ser.isOpen()== False:
    ser.open()
while 1:
    print"SET an BT senden"
    ser.write("set" + chr(0x0d))
    while ser.inWaiting() > 0:
        byte = ser.read(1)
        if byte != chr(0x0d):
            s = s + byte
        elif byte == chr(0x0d):
            print s
            #Hier sollte s wieder gelöscht werden
    time.sleep(9)

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 10:48
von Sirius3
Hallo dan.mue,

»serial.Serial« hat wie andere Filelike-Objekte die Methode »readline« um eine Zeile zu lesen, oder ganz einfach »next(ser)«.

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 10:59
von dan.mue
Danke schön, hat auf Anhieb Anhieb geklappt.

Wenn die readline-Methode aufgerufen wird, wartet dann das Programm bis eine Zeile empfangen wurde?

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 11:16
von BlackJack
@dan.mue: Noch ein paar Anmerkungen zum Quelltext:

Vergleiche mit literalen Wahrheitswerten sind überflüssig. `isOpen()` liefert ja schon `True` oder `False` und ein Vergleich mit `False` liefert auch wieder einen Wahrheitswert. Wenn man auf das Gegenteil des Wertes testen möchte, dann gibt es den ``not``-Operator, also ``if not ser.isOpen():``. Allerdings ist dieser Test überflüssig, denn an der Stelle ist der Rückgabewert von `isOpen()` *immer* `True`.

Da es einen eigenen Typ für Wahrheitswerte gibt, sollte man für eine Endlosschleife `True` statt 1 nehmen, denn man möchte dort ja einen Wahrheitswert und keine Zahl haben.

Statt ``chr(0x0d)`` könnte man das auch direkt in als Zeichenkette mit einer Escape-Sequenz schreiben: ``'\x0d'``.

Dein Code kann so offensichtlich nicht funktionieren weil `s` verwendet wird, bevor ein Wert an den Namen gebunden wurde.

Wenn man in einem ``elif`` genau die entgegengesetze Bedinung wie im ``if`` prüft, dann will man ein ``else`` stattdessen.

Letztlich würde ich aber sagen das Vorgehen ist nicht robust, da es nicht damit klar kommt, wenn der Sender aus irgend welchen Gründen mal nicht so schnell senden sollte wie der Empfänger die Daten verarbeitet und dadurch mitten in der Antwort `inWaiting()` 0 zurück gibt, obwohl die Antwort noch gar nicht komplett ist.

Die gesetzte Zeitschwelle von 10 Sekunden wird nirgends berücksichtigt oder verwendet. Bei `readline()` würde eine entsprechende Ausnahme ausgelöst wenn die Zeile innerhalb von 10 Sekunden nicht komplett empfangen wurde.

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 11:54
von dan.mue
Danke für dein Hilfe BlackJack,

wie Anfangs schon beschrieben wird zyklisch ein "set\x0d" Befehl an ein Gerät gesendet. Als Antwort sendet dieser mehrere Zeilen, wobei Anzahl der Zeilen sowie die Länge unbekannt sind. Die Zeilen werden lediglich mit "CR" beendet.

Beispiel:
"Kanal1: Sensor1<CR>Temperatur=35Grad<CR>"
oder
"Kanal1: Sensor1<CR>Temperatur=35Grad<CR>Kanal2: Taster Tuer<CR>Status=geschlossen<CR>

Es gibt aber kein kein Übertragungsende-Zeichen.

Code: Alles auswählen

while True:
    print"SET an BT senden"
    ser.write("set\x0D")
    time.sleep(1)
    while ser.inWaiting()>0:
        s = ser.readline()
        print s
Was mir jetzt nicht gefällt, dass die Ausgabe 1 Leerzeile schreibt. - Liegt wohl an print und <CR> im String

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 11:56
von dan.mue
OK

Code: Alles auswählen

pint s,
ist die Lösung

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 13:25
von BlackJack
@dan.mue: Das halte ich immer noch für nicht robust, immer noch aus dem selben Grund. Falls die andere Seite der seriellen Kommunikation nicht damit zurecht kommt wenn ein neuer 'set'-Befehl gesendet wird bevor alle Sensordaten empfangen wurden, dann wäre das ganze Protokoll noch ungünstiger. Ich würde nach das schreiben auch einen `flush()`-Aufruf setzen, damit man keine Probleme bekommt, wenn gepuffert wird.

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 13:36
von dan.mue
so besser:

Code: Alles auswählen

#!/usr/bin/python

import serial
import time

ser = serial.Serial("/dev/ttyAMA0", 115200, timeout=10)
scanperiode = 10
scantime = time.time()
ser.flushInput()
while True:
    #Alle 10 Sekunden wird der Status abgerufen
    if (time.time()-scanperiode) > scantime:
        print"SET an BT senden"
        ser.write("set\x0D")
        scantime=time.time()
    if ser.inWaiting()>0:
        s = ser.readline()
        #Hier werden zukuenftig alle Empfangenen Antworten und Events ausgewertet
        print s,
Denn das Gerät kann auch Events ohne Anfragen senden, wenn z.B. ein Alarm ausgelöst wird

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Freitag 26. April 2013, 14:21
von BlackJack
@dan.mue: Wenn das so komplett asynchron ist, würde ich einfach mit dem `threading`-Modul einen zweiten Thread starten und dort hin entweder das schreiben oder das lesen auslagern. Das `flush()`\en meinte ich beim Schreiben des 'set'-Befehls. Sonst sammeln die sich wenn man Pech hat in einem Puffer statt sofort zum seriell angeschlossenen Gerät geschickt zu werden. Ungetestet:

Code: Alles auswählen

import time
from threading import Thread
from serial import Serial


def send_set_commands(serial, interval):
    while True:
        serial.write('set\x0d')
        serial.flush()
        time.sleep(interval)


def main():
    serial = Serial('/dev/ttyAMA0', 115200, timeout=10)
    send_thread = Thread(target=send_set_commands, args=[serial, 10])
    send_thread.start()
    for line in serial:
        print line,


if __name__ == '__main__':
    main()
Falls lesen und schreiben auf der seriellen Schnittstelle nicht „thread safe” sein sollte, muss man etwas mehr tun, um den Zugriff sicher zu machen.

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Montag 29. April 2013, 08:21
von dan.mue
Hallo BlackJack,

danke für deine Hilfe, aber wie kann ich den Thread beenden, nachdem das Programm beendet wurde??

Re: Byte aus ser. Schnittstelle lesen und in String schreibe

Verfasst: Montag 29. April 2013, 11:16
von BlackJack
@dan.mue: Wenn man einen „daemon”-Thread daraus macht, dann ended er automatisch wenn der Hauptthread endet:

Code: Alles auswählen

    send_thread.setDaemon(True)