Seite 1 von 1
serial Stream aufteilen
Verfasst: Dienstag 24. Januar 2017, 17:54
von Smasch
Hi Leute ich bin auf der Sucher nach einer Idee.
Ich lese über eine serielle Schnittstelle 200 Werte ein, die mit einem Komma getrennt sind.
Diese Werte möchte ich formatiert in einer Datei speichern. Sowas wie Variable1=Wert1, Variable2=Wert2,...
Mein Problem ist das die Werte unterschiedlich lang sind mal 4,5 oder 6 Zeichen lang.
Gibt es eine Funktion um leichter an meine Werte zu kommen? Eigentlich müssten meinen Werte doch in schönen Byte Blöcken vorhanden sein?!
Danke für eure Ideen.
Code: Alles auswählen
import serial
import time
port = serial.Serial("/dev/tty.usbserial-FTHEUS5P", 9600, timeout = None);
port.isOpen();
while True:
daten = port.readline();
f = open('spam.txt', 'a+')
f.write(daten.decode('utf-8'))
f.close()
print (daten);
print (len(daten));
print (daten[0:5])
Re: serial Stream aufteilen
Verfasst: Dienstag 24. Januar 2017, 20:09
von Sirius3
@Smasch: die Daten kommen schon in diesem Format von der Seriellen Schnittstelle? Wenn das Komma ein eindeutiges Trennzeichen ist, würde man die Schlüssel-Wert-Paare am Komma auftrennen.
Re: serial Stream aufteilen
Verfasst: Dienstag 24. Januar 2017, 22:23
von BlackJack
@Smasch: Anmerkungen zum Quelltext:
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
`time` wird importiert aber gar nicht verwendet.
Das Semikolon trennt in Python Anweisungen innerhalb einer Zeile. Das gehört also nicht ans Ende einer Zeile. Das geht zwar weil es auch die leere Anweisung gibt, aber es macht keinen Sinn.
Für Baudrate und `timeout` verwendest Du die Default-Werte, also braucht man die nicht angeben.
``port.isOpen()`` macht keinen Sinn → Weg damit.
`Serial`-Objekte sind iterierbar, und zwar genau wie bei Dateien iteriert man über die Zeilen. Also würde man statt der ``while``-Schleife eine ``for``-Schleife verwenden. Genau wie Dateiobjekte sind `Serial`-Objekte auch „context manager“, können also mit der ``with``-Anweisung verwendet werden.
Das '+' im Dateimodus ist nicht nötig und sollte auch nicht verwendet werden. Das macht selten Sinn, und wenn dann sowieso nur bei Binärdateien.
Zwischen Funktionsname und öffnender Klammer für den Aufruf gehört kein Leerzeichen.
Ich lande dann als Zwichenergebnis ungefähr hier:
Code: Alles auswählen
from serial import Serial
def main():
with Serial('/dev/tty.usbserial-FTHEUS5P') as port:
for daten in port:
with open('spam.txt', 'a', encoding='utf-8') as out_file:
out_file.write(daten.decode('utf-8'))
print(daten)
print(len(daten))
print(daten[:5])
if __name__ == '__main__':
main()
Zur Frage: Schau Dir mal in der Python-Dokumentation an was man mit Zeichenketten und `bytes` so alles machen kann.
Ansonsten wäre eine bessere Beschreibung der Daten und des gewünschten Ergebnisses sinnvoll. Es ist anscheinend nicht ganz klar ob das was Du gezeigt hast die Eingabe oder die gewünschte Ausgabe sein soll.
Re: serial Stream aufteilen
Verfasst: Mittwoch 25. Januar 2017, 02:22
von Smasch
Vielen Dank für diese ausführliche Antwort!
Ich werden mir das morgen früh genauer angucken und nachvollziehen.
Okay, ich versuche mein Vorhaben genauer zu beschreiben:
Ich habe einen Mikrocontroller der Messungen ausführt, dabei entspricht 1 Messung 200 Werten und es sollen 50/60 Messungen pro Sekunde gemacht werden. Die Standard Baudrate von 9600 hab ich nur für mich stehen lassen, um den Prozess zu verlangsamen und nachvollziehbarer zu machen. Am Ende sollen 921.600 oder vielleicht noch mehr erreicht werden. Die Messungen laufen auch über einen längeren Zeitraum (mehrere Sekunden oder Minuten) und sollen nicht nach 1 Sekunde zu Ende sein.
Mit diesem Projekt versuche ich mich auch in Python einzuarbeiten. Ich habe schon ein ähnliches Programm aber mit C geschrieben. Daher vielen Dank, dass Du mich auf diese Fehler aufmerksam gemacht hast.
Das Programm soll einmal eine Datei erstellen, in der die Daten einfach abgespeichert werden und eine zweite Datei (JSON) in der die 200 Werten Variablen zu geordnet werden und die 50x pro Sekunde geupdatet wird damit ich mit einer Webapp die Daten visualisieren kann (Graphen).
Der Code, den ich hier gepostet habe, ist nur mein Anfang den ich noch erweitern möchte.
Vom Mikrocontroller kommt so was wie: "8000,12000,15000,6500,...,\n"
Mit meiner Frage habe ich gehofft das es eine Möglichkeit gibt schon beim Lesen der Schnittstelle die Werte in eine Art Array zu packen.
Re: serial Stream aufteilen
Verfasst: Mittwoch 25. Januar 2017, 10:22
von BlackJack
@Smasch: Du kannst die Zeile lesen und an den Kommas in eine Liste aufteilen, dann hast Du eine Liste mit den Bytes die jeweils einen Wert ausmachen als ASCII-Darstellung ausmachen, die kann man dann mit der `int()`-Funktion und einer „list comprehension“ einfach zu einer Liste mit Zahlen umwandeln. Dann brauchst Du nur noch 200 Namen (wo kommen die her?) und kannst mit `zip()` und `dict()` ein Wörterbuch daraus machen, was mit Hilfe des `json`-Moduls zu einem JSON-Objekt serialisiert werden kann.
Re: serial Stream aufteilen
Verfasst: Montag 30. Januar 2017, 15:56
von Smasch
Vielen Dank für die Hilfe. Ich erstelle die Liste vom Seriellen Stream mit dem split Befehl.
Diese Liste füge ich dann mit einer zweiten Liste (wellen) zusammen. Damit habe ich mein gesuchtes Format (x,y) siehe Ausgabe "messliste".
In Zeile 28-30 versuche ich jetzt das Ganze mit matplotlib schön grafisch anzeigen zu lassen. Er zeigt es auch an updatet es leider noch nicht.
Code: Alles auswählen
from serial import Serial
import datetime
import matplotlib.pyplot as plt
def main():
try:
dateiname = "Messung"+'{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())+'.txt'
with Serial('/dev/tty.usbserial-FTHEUS5P', 9600) as port:
for daten in port:
liste = daten.decode('utf-8').split(",")
if(len(liste)==289):
i = 1
while i<=287:
liste[i]=int(liste[i])-6040
i=i+1
#Listen (x,y)
messliste = []
i=0
while i<=287:
messliste.append([])
messliste[i].append(wellen[i])
messliste[i].append(liste[i])
i=i+1
print(messliste)
zip(*messliste)
plt.plot(*zip(*messliste))
plt.show()
except KeyboardInterrupt:
out_file.close()
port.close()
print("Strg+c: Programm wurde beendet!")
if __name__ == '__main__':
main()
Ausgabe "messliste":
[[324, '8116'], [326, 2032], [329, 127], [332, 0], [335, 0], ... [891, 63], [892, 0]]
Re: serial Stream aufteilen
Verfasst: Montag 30. Januar 2017, 16:44
von snafu
Du solltest wohl noch etwas an deinen Python-Kenntnissen in Bezug auf Operationen auf Sequenzen arbeiten:
Code: Alles auswählen
liste = daten.decode('utf-8').split(",")
i = 1
while i<=287:
liste[i]=int(liste[i])-6040
i=i+1
# ...wird zu:
items = daten.decode('utf-8').split(",")[1:288]
liste = [int(item) - 6040 for item in items]
Wobei man einen besseren Namen als "liste" wählen sollte.
Re: serial Stream aufteilen
Verfasst: Montag 30. Januar 2017, 16:53
von snafu
Und auch hier kann man es deutlich kompakter lösen:
Code: Alles auswählen
messliste = []
i=0
while i<=287:
messliste.append([])
messliste[i].append(wellen[i])
messliste[i].append(liste[i])
i=i+1
# ...wird zu:
messliste = list(zip(wellen[:288], liste[:288]))
# Oder alternativ:
from itertools import islice
slices = [islice(wellen, 288), islice(liste, 288)]
messliste = list(zip(*slices))
Dies verhindert das Erzeugen von unnötigen Listen als Zwischenschritte.
Re: serial Stream aufteilen
Verfasst: Montag 6. Februar 2017, 15:43
von Smasch
Danke für eure Antworten!
Ich bin jetzt auf ein anderes Problem mit der seriellen Verbindung gestoßen und zwar bekomme ich ab und zu mal diese Fehler:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe0 in position 1: invalid continuation byte
oder
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb2 in position 7: invalid start byte
bzw. das Programm gibt mir gar keine Ausgabe beim einlesen der Schnittstelle
Gibt es Möglichkeiten den Port besser einzustellen um den Fehlern entgegenzuwirken?
Mit dem folgendem Code verrennt sich das Programm immer bzw schafft es einmal die Schnittstelle auszulesen.
Code: Alles auswählen
def schnittstelle_auslesen(dateiname):
global dia
global fig
fulltransition=0
with Serial('/dev/tty.usbserial-FTHEUS5P', 500000) as port:
while fulltransition != 289:
daten=port.readline()
liste=daten.decode('utf-8').split(",")
print(liste)
if(len(liste)==289):
fulltransition=289
i=0
while i<=287:
liste[i]=int(liste[i])-6040
i=i+1
with open(dateiname, 'a', encoding='utf-8') as out_file:
out_file.write('{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())+' || ')
out_file.write(daten.decode('utf-8'))
out_file.write('\n\n')
messliste = []
messliste = list(zip(wellen[:288], liste[:288]))
# if dia == None:
# plt.ion()
# #fig, ax = plt.subplots()
# plt.axis([300, 900, 0, 6000])
# dia, = plt.plot(*zip(*messliste))
# plt.title('Messung')
# plt.xlabel('Wellenlängen')
# plt.ylabel('Intensität')
# #löscht die Umrandung
# ax = plt.subplot(111)
# ax.spines["top"].set_visible(False)
# ax.spines["right"].set_visible(False)
# ##löscht die Umrandung
# plt.show()
# plt.pause(0.02)
# else:
# plt.pause(0.02)
# dia.set_ydata(liste[:288])
#fig.canvas.draw()
print(messliste)
def main():
newINTtime=INTtime=15
newDAC0=DAC0=0
newDAC1=DAC1=0
kalibrieren()
dateiname=logdatei_erstellen()
#Endlos auslesen
runningtoheaven=0
while runningtoheaven!=1:
schnittstelle_auslesen(dateiname)
if(INTtime!=newINTtime):
newINTtime=INTtime=sendnewINTtime()
if(DAC0!=newDAC0):
newDAC0=DAC0=sendnewDAC0()
if(DAC1!=newDAC1):
newDAC1=DAC1=sendnewDAC1
if __name__ == '__main__':
main()
Ich habe es nochmal mit einem kürzeren Programm versucht damit bekomm ich die Fehler seltener.
Liegt es an der Länge des Programms?
Code: Alles auswählen
from serial import Serial
import datetime
def main():
try:
dateiname = "Messung"+'{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())+'.txt'
with Serial('/dev/tty.usbserial-FTHEUS5P', 500000) as port:
for daten in port:
liste = daten.decode('utf-8').split(",")
if(len(liste)==289):
i = 0
while i<=287:
liste[i]=int(liste[i])-6040
i=i+1
with open(dateiname, 'a', encoding='utf-8') as out_file:
out_file.write('{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())+' || ')
out_file.write(daten.decode('utf-8'))
out_file.write('\n\n')
print(liste[:287])
except KeyboardInterrupt:
out_file.close()
port.close()
print("Strg+c: Programm wurde beendet!")
if __name__ == '__main__':
main()
Re: serial Stream aufteilen
Verfasst: Montag 6. Februar 2017, 17:24
von BlackJack
@Smasch: Was sind denn das für Daten die da gesendet werden? Offensichtlich kommen neben den Messwerten ja auch Zeilen die Dich nicht interessieren. Wie sind die kodiert? Wirklich UTF-8?
Re: serial Stream aufteilen
Verfasst: Dienstag 7. Februar 2017, 02:53
von Smasch
[codebox=c file=Unbenannt.c]unsigned int data[288];
void printdata(){
for (int i = 0; i < 288; i++){
printf("%d,",data);
}
printf("\n");
}[/code]
Das kommt vom Mikrocontroller.
Re: serial Stream aufteilen
Verfasst: Dienstag 7. Februar 2017, 10:06
von BlackJack
@Smasch: Dann können die Bytes die da angemeckert werden nicht kommen und ich verstehe auch nicht warum Du nach dem `split()` auf die Anzahl der Elemente prüfst. Es sei denn man muss mit korrupten Daten rechnen, dann ist dieser Anzahltest aber nicht robust genug, da würde man eine Prüfsumme verwenden. Ausserdem würde ich die Kodierung auf ASCII einschränken, denn UTF-8 wird hier ja gar nicht benötigt.
Re: serial Stream aufteilen
Verfasst: Dienstag 7. Februar 2017, 12:14
von Smasch
@BlackJack vielen Dank für deine Antworten!
Der uC schickt in einer Endlosschleife, wenn er eingeschaltet wird, die 288 Daten und das "\n".
Mit dem Programm auf dem PC fange ich manchmal mittendrin in einem Sendevorgang an zu lesen und ich möchte vermeiden, dass ich nur halbe Messungen bekomme. Ich möchte nur eine volle Messung mit 288 Werten zulassen.
Was wäre denn eine Möglichkeit meinen Test robuster zu gestalten? Bzw. wie verwende ich eine Prüfsumme?