mikrocontroller.net

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


Autor: R. F. (inet_surfer88)
Datum:

Bewertung
0 lesenswert
nicht 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:
Schnittstelle öffnen
Adresse1 (1 Byte)
Werte für Adresse 1 (1Byte)
Adresse2 (1 Byte)
Werte für Adresse 2 (1Byte)
Adresse3 (1 Byte)
Werte für Adresse 3 (1Byte)
...
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.
ser = serial.Serial('/dev/ttyUSB0', 19200)
for i in range(112):
    ser.write(bytes([128|i]) + bytes([daten[i]]))
    ser.flush()
    time.sleep(0.001)
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.

Autor: StinkyWinky (Gast)
Datum:

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

Autor: R. F. (inet_surfer88)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: R. F. (inet_surfer88)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: imonbln (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

import struct
import time
import serial

with serial.Serial('/dev/ttyUSB0', 19200) as ser:
    for i,data in enumerate(daten):
        tmp = struct.pack('!2B', 0x80 | i, data)
        ser.write(tmp)
        ser.flush()
        time.sleep(0.001)

Autor: Christian Müller (Firma: magnetmotor.ch) (chregu) Benutzerseite
Datum:

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

Gruss Chregu

Autor: R. F. (inet_surfer88)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian Müller (Firma: magnetmotor.ch) (chregu) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Georg (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: R. F. (inet_surfer88)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: R. F. (inet_surfer88)
Datum:

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.