PySerial liest serielle Daten vom uC "meistens"

Gast #5072561
Lesenswert?

Hallo,

ich habe etwas Probleme damit, Daten vom uC (Arduino UNO über USB-RS232) 
mit Pyserial (mit Python3) zu lesen.

Daten sind 4 Temperaturwerte, Format:
"23.45,24.65,18.00,12.36\r\n"

Der Arduino sendet die Daten vernünftig, das habe ich im Terminal 
überprüfen können.

Python-Code:
(Daten sollen eingelesen werden und werden dann mit APscheduler getaktet 
in eine SQL Datenbank geschrieben)
1
import time
2
import serial
3
import pymysql.cursors
4
import pymysql
5
from apscheduler.schedulers.background import BackgroundScheduler
6

7
sched=BackgroundScheduler()
8

9

10

11
ser = serial.Serial(
12
           port='/dev/ttyACM0',
13
           baudrate = 9600,
14
           parity=serial.PARITY_NONE,
15
           stopbits=serial.STOPBITS_ONE,
16
           bytesize=serial.EIGHTBITS,
17
           timeout=5)
18
connection = pymysql.connect(host='localhost',
19
                             user='db_user',
20
                             password='1234',
21
                             db='wetter',
22
                             charset='utf8mb4',
23
                             cursorclass=pymysql.cursors.DictCursor)
24

25
ser.flush()
26
ser.flushInput()
27

28
#######################################################################
29
def get_temp():
30
            with connection.cursor() as cursor:
31

32
                x=ser.readline()
33
                print(x)
34
                line=x.decode()
35
                line = ''.join(line.split()) #whitespaces abtrennen
36
                arr=line.split(',') #am komma trennen
37
                if len(arr)==4:
38
                    print('temp0: ',arr[0], 'temp1: ',arr[1], 'temp2: ',arr[2], 'temp3: ',arr[3])
39
                    sql = "INSERT INTO `data` (`temp0`, `temp1`, `temp2`, `temp3`) VALUES (%s, %s, %s, %s)"
40
                    cursor.execute(sql, (arr[0], arr[1], arr[2], arr[3], ))
41
                    connection.commit()
42
                else:
43
                    print('serial data error!')
44

45

46
#######################################################################
47

48

49
sched.add_job(get_temp, 'interval', seconds=60)
50
sched.start()

Eine beispielhafte Ausgabe:
b'25.50,25.00,27.00,24.44\r\n'
temp0:  25.50 temp1:  25.00 temp2:  27.00 temp3:  24.44
b'25.50,25.00,27.00,24.44\r\n'
temp0:  25.50 temp1:  25.00 temp2:  27.00 temp3:  24.44
b'25.50,25.00,27.00,24.44\r\n'
temp0:  25.50 temp1:  25.00 temp2:  27.00 temp3:  24.44
b'25.50,25.00,27.00,24.44\r\n'
temp0:  25.50 temp1:  25.00 temp2:  27.00 temp3:  24.44
b'25.50,25.00,27.00,24.44\r\n'
temp0:  25.50 temp1:  25.00 temp2:  27.00 temp3:  24.44
b'25.50,25\n'
serial data error!
b'25.50,24.50,26.69,24.12\r\n'
temp0:  25.50 temp1:  24.50 temp2:  26.69 temp3:  24.12
b'25.50,24.50,26.69,24.12\r\n'
temp0:  25.50 temp1:  24.50 temp2:  26.69 temp3:  24.12
b'2\r\n'
serial data error!
b'25.50,24.00,26.69,24.12\r\n'
temp0:  25.50 temp1:  24.00 temp2:  26.69 temp3:  24.12
b'25.50,24.50,26.69,24.12\r\n'
temp0:  25.50 temp1:  24.50 temp2:  26.69 temp3:  24.12
b'25.50,24.50,26.69,24.12\r\n'
temp0:  25.50 temp1:  24.50 temp2:  26.69 temp3:  24.12
b'25.50,24.50,26.69,24.12\r\n'

Ich denke, man kann das Problem ganz gut erkennen. Eine gute Lösung habe 
ich noch nicht gefunden.

Ich habe schon versucht vor dem Lesevorgang mit ser.flushInput() den 
Eingangspuffer zu leeren. Das hat die Fehlerrate aber eher 
verschlechtert.

Danke im Vorraus.
Marvin
#5072577
Lesenswert?

In welchen Intervallen werden die Daten gesendet? Könnte mir vorstellen, 
dass der Buffer zwischendurch vollläuft. Benutzt du den Standard Windows 
CDC Treiber oder einen von einem bestimmten Hersteller? Je nach Treiber 
gibt es keinen zirkulären Buffer für die seriellen Daten, sondern er 
läuft einfach voll, wenn du nicht schnell genug ausliest.
#5072622
Lesenswert?

Hast du mal probiert, deine gettemp funktion in kürzeren Intervallen 
aufzurufen? In 60 Sekunden können 60 x 25 = 1500 byte auflaufen, das 
könnte auf einem embedded System schon die größe des buffers sein. 
Versuch einfach mal testweise ein Intervall von 1 Sekunde.
Gast #5072714
Lesenswert?

Ich hab jetzt mal testweise den Scheduler auf eine Sekunde gestellt. 
Bisher gab es damit noch keine Auffälligkeiten. Ich werde es so mal 
etwas laufen lassen.

Jim M. schrieb:
> Wieso wird da überhaupt der Scheduler verwendet? Die
> ser.readline()
> Funktion müsste doch von sich aus blockieren?

Ja, das tut sie, aber ich bekäme dann Messwerte im Takt von ETWA einer 
Sekunde. Ich will die Daten aber im Takt von genau 60 Sekunden in eine 
Datenbak schreiben.

Ich gehe mal davon aus, dass sich der Puffer an einer sinnvollen Stelle 
leeren lässt, damit ich das Messintervall wieder vergrößern kann.
#5072723
Lesenswert?

Marvin schrieb:
> Ja, das tut sie, aber ich bekäme dann Messwerte im Takt von ETWA einer
> Sekunde. Ich will die Daten aber im Takt von genau 60 Sekunden in eine
> Datenbak schreiben.
So wie es jetzt ist, ist es aber unvorhersehbar, welchen Messwert du 
kriegst. Du weißt nämlich nicht, wann den Puffer vollgelaufen ist bzw 
wie alt der Messwert, den du liest, ist. Wenn du einen aktuellen Wert 
alle 60 Sekunden willst musst du entweder die Werte in einer while 
Schleife so lesen wie sie reinkommen und dann bei jeder Iteration prüfen 
ob 60 Sekunden vergangen sind und dann den Wert in die DB schreiben. 
Oder du schedulest alle 60 sekunden einen Flush des input buffers und 
wartest dann auf die nächste line.
Moderator Persönliche Seite #5073250
Lesenswert?

Felix U. schrieb:
> Wenn du einen aktuellen Wert alle 60 Sekunden willst musst du entweder
> die Werte in einer while Schleife so lesen wie sie reinkommen und dann
> bei jeder Iteration prüfen ob 60 Sekunden vergangen sind und dann den
> Wert in die DB schreiben.

Damit schreibst Du nur jeden 60sten Wert weg und 59 andere verwirfst Du. 
Wenn man schon jede Sekunde einen Wert bekommt, aber nur jede Minute 
einen Wert protokollieren will, würde ich die Messwerte immer für eine 
Minute mitteln und dann den Mittelwert in die DB schreiben. Dann haben 
die 60 erhaltenen Werte wenigstens einen Sinn.
Gast #5099679
Lesenswert?

Darf ich mal fragen, ob Du einen original Arduino Uno mit FTDI benutzt 
oder einen CH340?

Letzterer macht mit mit Chinaclones vermehrt Probleme, da der 
Linuxtreiber (auch auf dem Raspberry) nicht 100% kompatibel mit dem 
Chipsatz ist.

Probiere einfach mal Dein Pythonskript auf einem MAC oder Windows PC 
aus. Wenn Du dort Deine Daten korrekt eingelesen bekommst, dann hast Du 
den Übeltäter. Dann hast Du die Wahl zwischen Treiber oder Arduino mit 
FTDI

Grüße
Tony

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren