Forum: PC-Programmierung pySerial Probleme


von test (Gast)


Lesenswert?

Hallo,
ich möchte mit Phyton Sensordaten von einem uC auswerten. Dazu habe ich 
pyserial installiert. Leider bekomme ich Fehler die ich nicht verstehe?

System:

Win 7 64
Python 3.4.1
pyserial 2.7

Der Code ist eigentlich 1 zu 1 kopiert:
1
import time
2
import serial
3
4
5
6
ser = serial.Serial(
7
    port='\\.\COM3',
8
    baudrate=9600,
9
    parity=serial.PARITY_NONE,
10
    stopbits=serial.STOPBITS_ONE,
11
    bytesize=serial.EIGHTBITS
12
)
13
14
15
    
16
    
17
print("Bitte etwas eingeben")
18
a = input()
19
20
ser.write(a)
21
print("gesendet: " + a)
22
23
24
time.sleep(1)
25
26
27
b = ser.readline()
28
print("Antwort: " + b)
29
    
30
ser.close()

Ich bekomme folgende Fehlermeldung:

Traceback (most recent call last):
  File "C:\Python34\serial_test.py", line 20, in <module>
    ser.write(a)
  File "C:\Python34\lib\site-packages\serial\serialwin32.py", line 283, 
in write
    data = to_bytes(data)
  File "C:\Python34\lib\site-packages\serial\serialutil.py", line 76, in 
to_bytes
    b.append(item)  # this one handles int and str for our emulation and 
ints for Python 3.x
TypeError: an integer is required

von Dennis S. (eltio)


Lesenswert?

Sieht für mich so aus, als ob du "a" nach int casten musst.

Versuch mal (ungetestet):
1
ser.write( int(a) )

: Bearbeitet durch User
von Michael S. (Gast)


Lesenswert?

Hallo,

casten nach int - im Prinzip ja.
Das sieht dann so aus:


> print("Bitte etwas eingeben")
> a = input()
>

# a als bytes
a = bytes(a, encoding = "ascii")

> ser.write(a)

# zurück zu String
a = a.decode("ascii")

> print("gesendet: " + a)

von test (Gast)


Lesenswert?

Das erste hat ich auch schon ausprobiert, hat aber nicht gebracht. Aber 
der zweite Lösungsansatz läuft.

Sieht jetzt folgendermaßen aus:
1
import time
2
import serial
3
4
5
6
ser = serial.Serial(
7
    port='\\.\COM3',
8
    baudrate=9600,
9
    parity=serial.PARITY_NONE,
10
    stopbits=serial.STOPBITS_ONE,
11
    bytesize=serial.EIGHTBITS
12
)
13
14
15
    
16
    
17
print("Bitte etwas eingeben")
18
a = input()
19
try:
20
    a = bytes(a, encoding = "ascii")
21
    ser.write(a)
22
    a = a.decode("ascii")
23
    print("gesendet: " + a)
24
except:
25
    print("geht nich")
26
27
28
#time.sleep(1)
29
30
try:
31
    b = 0
32
    while b != '\n':
33
        b = ser.read()
34
        b = b.decode("ascii")
35
        print(b)
36
except:
37
    print("Empfang geht nich")
38
    
39
ser.close()

Allerdings funktioniert der readline() Befehl nicht?
Wenn ich den Teil nach dem time.sleep ersetzte durch:
1
b = ser.readline()
2
b = b.decode("ascii")
3
print(b)

passiert nichts bzw. das Programm hängt.

von Kaj (Gast)


Lesenswert?

test schrieb:
> Allerdings funktioniert der readline() Befehl nicht?
warum benutzt du readline?
Du weisst doch bestimt wie lang die antwort ist, also nimm read() und 
sag wie viele zeichen gelesen werden sollen.

Ansonsten hat die Klasse serial.Serial keine Funktion readline. (siehe 
Doku)
Die Klasse serial.FileLike hat aber diese Funktion.

http://pyserial.sourceforge.net/pyserial_api.html

von Michael S. (Gast)


Lesenswert?

Hallo,


readline() erwartet eine Eingabe, die mit '\n' abgeschlossen ist.

Einfacher kann es sein, zuerst zu prüfen, ob bzw. wieviele Bytes im 
Empfangspuffer sind.

Das macht die Methode inWainting()

Wenn mehr als 0 Byte warten, dann kann man diese Anzahl von Bytes 
einlesen

cnt = ser.inWaiting()
if cnt:
    in = ser.read(cnt)

von test (Gast)


Lesenswert?

Ersteinmal danke für die viele Hilfe! Ich habe das jetzt mit einer 
Schleife realisiert. Klappt auch bis auf einen Fall perfekt.

Ich sende über die seriele Schnittstelle Befehle an meinen uC. Das 
klappt auch perfekt. Ein Problem bekomme ich allerdings wenn ich mir 
Sensordaten vom uC schicken lasse und parallel Komandos an den uC 
schicke.
Diese werden zwar eigentlich immer ausgeführt allerdings kommen die 
Bestätigungsantworten nicht richtig durch bzw. gehen in den Sensordaten 
unter.
Hat jemand so etwas schon einal programmiert oder hat eine Idee?
Mir ist bis jetzt noch keine intelligente Lösung eingefallen.

Mein einzigster Ansatz wäre durchgehend die Schnittstelle auszulesen und 
eine Art RX Interrupt zu bauen, am besten ausgelagert in einen weiteren 
thread.

von test (Gast)


Lesenswert?

Gibt es gängige Fehlerquellen die man in Python/pySerial begehen kann, 
die dazu führen das Fehler beim senden auftreten? Teilweise müssen 
einzelne Zeichen beim senden verloren gehen. Da mit HTerm alles 
wunderbar funktioniert, gehe ich davon aus das der Fehler im Python Code 
liegt?

Was mich ein wenig wundert ist, dass ich beim Sednen Probleme habe, da 
es bei UART doch zwei Leitungen gibt. Also sollten Timing Fehler durch 
paralles Senden und Empfangen eigentlich nicht auftreten?

von test (Gast)


Lesenswert?

Ich hätte noch mal eine Frage zu pyserial. Und zwar habe ich ein Problem 
beim übertragen meines Stopbefehls an meinen uC. Über send_command wird 
dieser gesendet. Das Problem ist, dass die Antwort zwischen Messwerten 
verborgen ist. Deswegen die rx_filter Funktion. Vom Grunde her 
funktioniert auch alles. Allerdings wenn ich die Übertragung von 
Messwerten länger als ca. 4 sec laufen lasse und ich dann meinen 
Stopbefehl sende, hängt sich das Programm auf. Das gleiche passiert nach 
ca. 20sec bei Dauerübertragung von Messwerten. Ich taste mit 1kHz ab und 
schreibe den Inputbuffer in ein array. Baudrate ist 115200. Kann es zu 
irgendwelchen Überläufen kommen?
Vermutlich kommt es in send_command zu einer Endlosschleife. Aber ich 
versteh nicht wie sie entsteht. Ich lösche meinen Buffer, sende dann 
meinen Befehl, fülle meinen Buffer und lese ihn dann aus. Warum macht es 
einen Unterschied wie lange ich vorher in den Buffer geschrieben habe. 
Ich leere ihn doch. Sollte also immer gleich sein.
1
    def tx(self, data):
2
        try:
3
            data = bytes(data, encoding = "ascii")
4
            self.ser.write(data)
5
            data = data.decode("ascii")
6
        except:
7
            print("send failed")
8
9
    def rx(self):
10
        rx = ""
11
        
12
        try:
13
                rx = self.ser.read()
14
                rx = rx.decode("ascii")
15
        except:
16
            print("receive of Data failed")
17
        return rx
18
19
    
20
    def flushinput(self):
21
        self.ser.flushInput()
22
23
    def rx_buffer_read(self):
24
        rx = ""
25
        rx_buffer = ""
26
        
27
        if self.ser.inWaiting() != 0:
28
            try:
29
                
30
                while rx != "\n":
31
                    rx = self.rx()
32
                    rx_buffer += rx
33
 
34
                return rx_buffer
35
               
36
          
37
            except:
38
                print("rx_buffer fail")
39
40
        else:
41
            return "None"
42
43
44
    def rx_filter(self):
45
46
        rx = ""
47
        rx = self.rx_buffer_read()
48
        command = "command"
49
        data = "data"
50
51
        if "#" in rx:
52
            rx = rx[1:]
53
            return rx, command
54
55
        elif "$" in rx:
56
            rx = rx[1:]
57
            return rx, data
58
59
        else:
60
            return "None", "None"
61
62
63
    def send_command(self, data):
64
        self.flushinput()
65
        self.tx(data)          
66
        time.sleep(0.1)   
67
        signal = ""
68
        command = ""
69
        counter = 0
70
        
71
        while signal != "command":
72
            command, signal = self.rx_filter()
73
            counter += 1
74
            print(str(counter))
75
            print(command)
76
           
77
        return command

von test (Gast)


Lesenswert?

Kann es sein, dass der flushInput() Befehl nicht funktioniert? So länger 
das Interval zwischen Übertragungsstart und stop ist, so mehr Messwerte 
kommen vor der Antwort des uC am PC an. Die Anzahl müsste doch immer 
gleich sein wenn man davor den Buffer entlehrt?

von Michael S. (Gast)


Lesenswert?

test schrieb:
> Kann es sein, dass der flushInput() Befehl nicht funktioniert?

Den Verdacht hatte ich auch schon einmal.

Ersetze doch testhalber flushInput() durch:

while ser.inWaitung():
   ser.read(1)

oder

i = ser.inWaiting()
while i:
  ser.read(i)
  i = ser.inWaiting()

Bringt das Besserung ?

von test (Gast)


Lesenswert?

OK, ich habe noch einmal ein wenig experimentiert. Wenn ich den Buffer 
zwischendurch (bei mir waren es 30ms)leere habe ich keine Probleme. Ich 
habe jetzt meine Abtastfrequenz auf 800Hz gesenkt und frage den Buffer 
mit 1kHz ab. Wie kann das sein das der Buffer überläuft? Ist der QTimer 
zu ungenau oder ist meine Funktionskette aus rx_buffer_read und 
rx_filter zu langsam?

von Michael S. (Gast)


Lesenswert?

test schrieb:
> Vermutlich kommt es in send_command zu einer Endlosschleife.

Bau doch einfach print()'s in die Schleifen ein, dann siehts Du, wo das 
Programm hängt.

test schrieb:
> while rx != "\n":

Das könnte so eine Stelle sein, bist Du sicher, dass ein "\n" ankommt ?

von test (Gast)


Lesenswert?

Naja ansich läuft das Programm ja. Hängt sich halt ab einen gewissen 
Zeitraum auf. Lösche ich allerdings regelmäßig den InputBuffer passiert 
das nicht. Für mich bedeutet das, dass der Fehler irgendwo dort liegen 
muss? Gibt es sowas wie einen Buffer Überlauf bei pyserial? An der 
Hardware sollte es eigentlich nicht liegen, da ich über HTerm noch kein 
falsches Verhalten feststellen konnte.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.