Forum: PC-Programmierung Sendepause bei RS232 mit pyserial (python)


von R. F. (inet_surfer88)


Lesenswert?

Hallo,

ich beschäftige mich seit einigen Wochen mit Python, bin also neu in dem 
Thema.

Ich will Werte an eine Zentrale senden. Die Zentrale hat 112 Adressen. 
Jede Adresse bekommt einen Wert von einem Byte gesendet.
Es wird immer ein Byte mit der Adresse gefolgt von einem Byte mit den 
entsprechenden Werten gesendet.

Prinzipieller Ablauf:
1
Schnittstelle öffnen
2
Adresse1 (1 Byte)
3
Werte für Adresse 1 (1Byte)
4
Adresse2 (1 Byte)
5
Werte für Adresse 2 (1Byte)
6
Adresse3 (1 Byte)
7
Werte für Adresse 3 (1Byte)
8
...
9
Schnittstelle schließen

Die Adressen und die Werte können direkt nacheinander ohne Pause 
gesendet werden. Das Problem ist hierbei aber folgendes. Wenn genau ein 
Byte aufgrund eines Übertragungsfehlers verloren geht, verschiebt sich 
die ganze Zuordnung und es kommt nur noch "Müll" heraus.
Deswegen soll zwischen den einzelnen Paaren bestehend aus Adresse und 
Daten eine Pause sein. Die Pause soll mindestens die Länge von 10 Bit 
haben. Die Pause dient der Zentrale zur Synchronisierung.

Ich habe mir folgendes Programm überlegt. Da ich mir bei der Funktion 
von ser.flush() nicht ganz sicher bin, wäre meine Frage ob ich da so 
alles richtig verstanden habe.
1
ser = serial.Serial('/dev/ttyUSB0', 19200)
2
for i in range(112):
3
    ser.write(bytes([128|i]) + bytes([daten[i]]))
4
    ser.flush()
5
    time.sleep(0.001)
6
ser.close()

Wenn ich das richtig verstanden habe wird mit der Zeile ser.write.... 
die beiden Bytes Adresse und Wert gesendet.
Danach wartet ser.flush() darauf, daß die beiden Bytes vollständig 
gesendet wurden.
Erst nach dem vollständigen Senden der beiden Bytes startet jetzt meine 
Wartezeit von 1ms.
Nach der Wartezeit wird das nächste Paar gesendet.

Ist das so alles so korrekt, oder habe ich etwas falsch verstanden?

Die Funktion von dem Programm ist gegeben, läuft alles. Allerdings habe 
ich keine Möglichkeit, die Pause zu Überprüfen. Hier bräuchte ich ein 
vernünftiges Speicheroszi. Und solange kein Byte verloren geht, 
funktioniert das ganze auch ohne die Pause.

von StinkyWinky (Gast)


Lesenswert?

Ich vermute, du benutzt Windows mit einem USB->Seriell Adapter. Daher 
befürchte ich, dass von deinem Timing nicht viel übrig bleibt...

von R. F. (inet_surfer88)


Lesenswert?

Mist, ich habe die ganzen Infos vergessen.
Das ganze läuft während der "Bastelphase" unter Debian 9.2 mit LXDE.
Python-Version und pyserial-Version hab ich gerade nicht im Kopf, bin 
zur Zeit nicht an dem Rechner. Ist auf jeden Fall Python 3.
RS232-Hardware ist ein USB-zu-RS232-Wandler (FTDI-Chip).

Wenn das ganze fertig programmiert ist und alles läuft kommt es auf 
einem RaspberryPi3 zum Einsatz, ebenfalls über den Wandler.
OS wird dann das jeweils aktuellste Rasbian sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

StinkyWinky schrieb:
> Ich vermute, du benutzt Windows

Seit wann hat Windows denn ein /dev/ttyUSB0?

Allerdings bleibt das Argument, dass durch den zwischengeschalteten
USB-Stack keinerlei Timing-Garantie mehr da ist.  Der FTDI hat einen
ziemlich großen FIFO in beiden Richtungen.

Bei einer Hardware-UART (der RPi hat sowas ja) wäre das was anderes.

: Bearbeitet durch Moderator
von R. F. (inet_surfer88)


Lesenswert?

Das bedeudet also das ich keine Chance habe, mit einem Wandler auf das 
Timing Einfluss zunehmen, auch nicht mit flush() ?
Dann wäre es wohl besser, die Anwendung später mit der Hardware-RS232 
vom Raspberry laufen zu lassen. Die wäre noch frei. Ich wollte mir das 
Geld für den Pegelwandler sparen, der USB-Adapter ist vorhanden und 
würde mich somit nichts kosten. Aber in dem Falle scheint ein 
Pegelwandler dann wohl gut angelegtes Geld zu sein.

von imonbln (Gast)


Lesenswert?

Für das Programm Fragment, hätte ich ein paar Python spezifische 
Anmerkungen, die lösen zwar dein Serial Timing Problem nicht aber 
sollten dir als Anfänger helfen besser die Programmiersprache Python zu 
verstehen und vielleicht auch etwas robusteren Code zu generieren.


1.) in Python gibt es Contextmanager die sparen Resourcen (so die 
Theorie) und machen das close überflüssig. Der Contextmanager beginnt 
mit ein with und das abräumen der resourcen (z.b. Close) erfolgt wenn 
der with block verlassen wird automatisch.


2.) Das iterieren über die range, entspricht zwar vielen Hochsprachen, 
aber ein Python Entwickler würde wahrscheinlich das buildin enumerate 
nutzen und über daten iterieren, das ermöglicht es auf die Magic nummer 
112 zu verzichten und macht den Code robuster (wieder verwendbar) auch 
wenn sich daten änderen sollten)


3.) für Hardware nahe Sachen und Netzwerk spezifische Dinge gibt es das 
module struct welches hier zwar noch nicht nötig ist aber 
perspektivisch, das Wandeln der lokalen Daten in Netzwerk oder Serial 
typische Codierungen vereinfacht.

1
import struct
2
import time
3
import serial
4
5
with serial.Serial('/dev/ttyUSB0', 19200) as ser:
6
    for i,data in enumerate(daten):
7
        tmp = struct.pack('!2B', 0x80 | i, data)
8
        ser.write(tmp)
9
        ser.flush()
10
        time.sleep(0.001)

von Christian M. (Gast)


Lesenswert?

Bekommst Du von dieser "Zentrale" nichts zurück, auf das Du warten 
könntest?

Gruss Chregu

von R. F. (inet_surfer88)


Lesenswert?

Hallo,

den Vorschlag mit dem Contextmanager und der with-Geschichte werde ich 
mir bei Gelegenheit anschauen. Ich denke mal, das man mit der 
Suchmaschine seines Vertrauens da etwas finden dürfte. Zeitlich muss für 
solche Sachen der nächste langweilige Hotelabend während eines 
Außendiensteinsatzes herhalten. Das könnte dann die Programmversion 2.0 
werden.

Die Zentrale sendet bei meiner konkreten Anwendung nichts zurück. Die 
Adresse besteht aus 7 Bit und das oberste 8. Bit ist die Kennung für 
Senden (High) oder empfangen (Low). Solange ich hier immer eine 1 sende 
um Daten an die Zentrale zu übermitteln, kommt nichts zurück. Deswegen 
auch die Oder-Verknüpfung mit 128 in dem Codeschnipsel oben.

von Christian M. (Gast)


Lesenswert?

R. F. schrieb:
> Die Pause dient der Zentrale zur Synchronisierung.

So was Schwachsinniges hatte doch nur eine alte ESP-Firmware oder war es 
ein anderes China-Modul...?

Und wenn Du einfach zwischen jedem Datagramm die Schnittstelle schliesst 
und wieder öffnest!?

Gruss Chregu

von Wolfgang (Gast)


Lesenswert?

R. F. schrieb:
> Ich will Werte an eine Zentrale senden. Die Zentrale hat 112 Adressen.
> Jede Adresse bekommt einen Wert von einem Byte gesendet.

Heißt das, dass die Adressen von 0 bis 111 in dieser Reihenfolge 
angesprochen werden?
Welchen Wertebereich belegen die zu übertragenden Datenbytes?

von Georg (Gast)


Lesenswert?

R. F. schrieb:
> Es wird immer ein Byte mit der Adresse gefolgt von einem Byte mit den
> entsprechenden Werten gesendet.

Kommt 0xFF bei den Werten vor? Wenn nicht könntest du statt einer Pause 
ein Byte 0xFF senden und das zur Resynchronisation benutzen.

Georg

von R. F. (inet_surfer88)


Lesenswert?

Hallo,

Christian M. schrieb:
> So was Schwachsinniges hatte doch nur eine alte ESP-Firmware oder war es
> ein anderes China-Modul...?
Das Teil ist Baujahr 1995 und funktioniert noch tadellos. Das dürfte 
gerade so der Anfang von USB gewesen sein. Wandler von USB auf seriell 
wahrscheinlich noch Fehlanzeige.

Das mit dem schließen/öffnen werde ich testen. Das hatte ich ganz am 
Anfang mal drin, ich glaube da war die Laufzeit von dem Programm zu 
hoch. Habe es jetzt aber nicht mehr 100%ig im Kopf, teste ich nochmals. 
Der gesamte Sendevorgang darf 1 Sekunde nicht überschreiten (meine 
Anforderung, der Zentrale wärs egal)

Die Adressen werden in der Reihenfolge 0...111 in dieser Reihenfolge 
übertragen, da ich die Adresse mit dem Schleifenzähler erzeuge und den 
dazugehörigen Datensatz aus der Liste nehme. Auch für die Liste nehme 
ich einfach den Schleifenzähler. Der Zentrale ist die Reihenfolge egal. 
Ich kann auch nur einzelne Werte senden.
Das Datenbyte stellt den Schaltzustand von jeweils 8 Digitalen Kanälen 
dar. Pro Bit ein digitaler Kanal. Der Wert kann also jeden möglichen 
Wert annehmen.

von R. F. (inet_surfer88)


Lesenswert?

Der Wert 0xFF kann vorkommen, wenn die 8 zugeordneten digitalen Kanäle 
alle logisch 1 sind.
(kommt auch häufiger vor)

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.