Python ist sehr nützlich, wenn es um die Steuerung eines Mikrocontrollers oder die Visualisierung von Daten geht. In diesem Thread will ich ein paar sehr einfache und nützliche Templates für Python entwerfen, um eine Grundlage für einfache GUIs und eigene Anpassungen zu schaffen. Verbesserungsvorschläge und eigene Beiträge sind sehr willkommen. Als erstes ein Beispiel für einen Frequenzgenerator, der mit zwei Buttons gesteuert werden kann.
Christoph M. schrieb: > Verbesserungsvorschläge und eigene Beiträge > sind sehr willkommen. Wenn ich mir den Code so anschaue würde ich folgendes anders machen: * Es scheint keine Frame-Ende-Erkennung vorhanden zu sein. Wenn ich schnell genug auf den Button drücken könnte oder wenn später mal vom Programm eine Frequenz-Folge gesendet würde z.B. "100" + "101" für einen Sweep, dann könnte es meiner Meinung nach passieren dass der uC nicht "100" empfängt, sondern z.B. "100101". Das als int interpretiert ist blöd. Momentan verhindert das meiner Meinung nach nur, dass das Python Programm zu langsam ist (sleep) bzw. dass der uC schnell genug ist. Also würde ich mindestens ein '\n' einfügen, damit man immer weis wo ein Frame zuende ist. * Es gibt keine Sicherungsschicht. Ein Glitch auf der Leitung führt sofort zu einem falschen Ergebnis. Eine Blocksumme oder CRC wäre einfach zu implementieren. Bei der Frequenz vllt. nicht ganz so schlimm, sobald Du aber die Ausgangsspannung o.ä. einstellen möchtest und ein Übertragungsfehler zerhaut dir deine Schaltung weil anstatt 1V 10V anliegen... * Den Sleep würde ich komplett entfernen. Der ist unnötig wenn du Sende- und Empfangsrichtung auftrennst. Du weist was du von Python gesendet hast und erwartest eine Antwort. Also eher: Neuer Wert senden. Erwartete Antwort merken. Timer aufziehen bis wann die Antwort zurück sein muss. OK melden falls es innerhalb dieser Zeit zurückkommt. Ansonsten neu senden o.ä. Alles hier nur als Anmerkung zu verstehen, nicht als Kritik.
>Alles hier nur als Anmerkung zu verstehen, nicht als Kritik. Das passt schon. Ich bin ja froh, dass es sich überhaupt jemand anschaut und konstruktive Beiträge sind ja gut ;-) >* Es scheint keine Frame-Ende-Erkennung vorhanden zu sein. Wenn ich schnell genug auf den Button drücken könnte oder wenn später mal vom Programm eine Frequenz-Folge gesendet würde z.B. "100" + "101" für einen Sweep, dann könnte es meiner Meinung nach passieren dass der uC nicht "100" empfängt, sondern z.B. "100101". Das als int interpretiert ist blöd. Momentan verhindert das meiner Meinung nach nur, dass das Python Programm zu langsam ist (sleep) bzw. dass der uC schnell genug ist. Also würde ich mindestens ein '\n' einfügen, damit man immer weis wo ein Frame zuende ist. Ja, an der Stelle hast du sicherlich recht. Das kann zu Problemen führen, deshalb habe ich mal ein neues Beispiel gemacht. Es immer nur ein Byte als Kommando übertragen, damit kann es keine Falschinterpretation geben. Es gibt 3 Knöpfe: Zaehler hochzaehlen, Zaehler unter zaehlen und ADC lesen. Damit kann man sehr gut die Zuverlässigkeit der Datenübertragung prüfen. Besser wäre es noch, den Zaehlerknopf mit einem Event-Timer automatisch zu betätigen, dann kann man die Kommunikation über längere Zeit beobachten. Allerdings weiß ich noch nicht, wie man das in Python im Zusammenhang mit TkInter realisiert. Bis jetzt habe ich bei diesem Beispiel aber noch keine fehlerhafte Übertragung festgestellt. >* Es gibt keine Sicherungsschicht. Ein Glitch auf der Leitung führt sofort zu einem falschen Ergebnis. Eine Blocksumme oder CRC wäre einfach zu implementieren. Bei der Frequenz vllt. nicht ganz so schlimm, sobald Du aber die Ausgangsspannung o.ä. einstellen möchtest und ein Übertragungsfehler zerhaut dir deine Schaltung weil anstatt 1V 10V anliegen... Eine Überprüfung mit Checksumme könnte man sich überlegen. Allerdings ist meine Erfahrung, dass die serielle Übertragung bezüglich der Einzelbitfehler mittlerweile recht zuverlässig funktioniert. Wenn Probleme auftreten, sind das meistens Timing Probleme, was in die Richtung deiner ersten Anmerkung geht. >* Den Sleep würde ich komplett entfernen. Der ist unnötig wenn du Sende- und Empfangsrichtung auftrennst. Du weist was du von Python gesendet hast und erwartest eine Antwort. Also eher: Neuer Wert senden. Erwartete Antwort merken. Timer aufziehen bis wann die Antwort zurück sein muss. OK melden falls es innerhalb dieser Zeit zurückkommt. Ansonsten neu senden o.ä. Das läuft dann in Richtung Protokollimplemtierung wie TCP-IP und wird dann ziemlich aufwendig. Meiner Erfahrung nach ist das bei so einer direkten drahtgebundenen Kommunikation nicht erforderlich.
Christoph M. schrieb: > Das passt schon. Ich bin ja froh, dass es sich überhaupt jemand anschaut > und konstruktive Beiträge sind ja gut ;-) OK 👍 dann merke ich nochmal an. Christoph M. schrieb: > Ja, an der Stelle hast du sicherlich recht. Das kann zu Problemen > führen, deshalb habe ich mal ein neues Beispiel gemacht. > Es immer nur ein Byte als Kommando übertragen Das hilft dir zwar in der Interpretation weil es eindeutig ist, trotzdem bekommst du bei einer Störung bei der Anforderung eine falsche Aktion im Arduino. Wenn du dann einen falschen Wert zurück bekommst weißt du nicht Mal ob der Arduino sich wirklich verzählt hat oder ob es ein ADC Wert ist. Dass a,h und r mindestens 2 Bits auseinanderliegen hilft hier zwar ein bisschen, aber nicht wirklich. Christoph M. schrieb: > Damit kann man sehr gut die Zuverlässigkeit der Datenübertragung prüfen. Christoph M. schrieb: > Bis jetzt habe ich bei diesem Beispiel aber noch keine fehlerhafte > Übertragung festgestellt. Auf dem Labortisch daheim mag das funktionieren. Sobald aber ein paar Meter Kabel dazwischen sind, jemand ein Gerät in der Umgebung ein-/ausschaltet oder eine andere Kleinigkeit passiert geht es schief. Glaub mir. Alles schon gehabt. Schalt Mal den Staubsauger, Bohrmaschine oder ähnliches daneben ein und aus. Christoph M. schrieb: > Allerdings ist meine Erfahrung, dass die serielle Übertragung bezüglich > der Einzelbitfehler mittlerweile recht zuverlässig funktioniert. Weil die Chips mehrfach Abtasten (z.B. 3/5-Fach oder höher) und dann eine Mehrheitsentscheidung treffen. Hilft dir trotzdem nur bedingt was. Sobald der Störer bereitet als eine Bitzeit ist bringt das nichts mehr. Ich würde mindestens das Parity Bit einschalten auf beiden Seiten. Kostet dich nichts. Hilft aber einiges. Sozusagen die Sicherungsschicht für arme. Default ist nämlich auf beiden Seiten aus. Christoph M. schrieb: > Wenn Probleme auftreten, sind das meistens Timing Probleme, was in die > Richtung deiner ersten Anmerkung geht. Ich glaube du hast nur noch nicht eine verseuchte Umgebung oder Masseprobleme gesehen 😄 Man bekommt es selbst bei differentieller Übertragung zuverlässig hin dass die Daten Müll sind. Christoph M. schrieb: > Das läuft dann in Richtung Protokollimplemtierung wie TCP-IP Bei weitem nicht. Das ist eine ganz andere Nummer. Einfach ein ACK vom Arduino dass der letzte Befehl gültig war. Ob das ein festes Zeichen ist oder ob das einfach nochmal der empfangene Frame wiederholt ist spielt in dem Fall keine Rolle. Dadurch hat man aber zumindest die Möglichkeit zu sehen dass etwas erneut gesendet werden muss. Ähnlich wie bei deinem Zähler, nur dass es für alle Befehle funktionieren würde.
Fehlerkorrekturen kann man natürlich beliebig weit treiben. Innerhalb elektronischer Systeme wird nicht alles auf Fehler geprüft, weil man davon ausgeht, dass die Datenübertragung zwischen den Bauteilen zuverlässig ist z.B. I2C oder die meisten einfacheren Controllersystem bei denen es z.B. zwischen der CPU und dem Speicher keinen Fehlercheck oder Fehlerkorrektur gibt. So was fängt dann bei größeren Systemen wie PCs an. Vielleicht magst Du ein fehlertolerantes Protokoll in den Code einfügen. Für mich reicht die Zuverlässigkeit der Übertragung im aktuellen Aufbau. Das Bild zeigt den Zählerstand (8Bit) im MC über einige Minuten.
Christoph M. schrieb: > Innerhalb elektronischer Systeme wird nicht alles auf Fehler geprüft Stimmt, teilweise sagt man dass es egal ist und dass zum Beispiel ein falscher Wert nicht stört. Weil z.B. innerhalb von ein paar us/ms sowieso ein neuer Wert kommt. Bei nicht ganz so wichtigen Prozessdaten kann man das machen. Teilweise wird auch mehrfach gelesen. Wobei ich dann lieber ein Byte mehr reservieren würde. Christoph M. schrieb: > bei denen es z.B. zwischen der CPU und dem Speicher keinen Fehlercheck > oder Fehlerkorrektur gibt Das sind, wenn der HW Mensch alles richtig gemacht hat, auch wenige mm bis cm auf einer Platine innerhalb eines Gehäuses. Bei dir verbindest du 2 unterschiedliche Komponenten. Evtl über Meter hinweg. Ist ein anderer Fall. Außerdem wird jeder SW Mensch der bei Verstand ist nicht die Daten ohne Checksumme in den NV-Speicher schreiben. Also selbst wenn sie falsche drin stehen im Speicher, falsch rauslesen und verwenden tut man sie definitiv nicht. Christoph M. schrieb: > Vielleicht magst Du ein fehlertolerantes Protokoll in den Code einfügen. Warum etwas neues erfinden was es schon zu genüge gibt. In deinem Fall würde Modbus RTU passen. Hier ein Beispiel für Arduino wo man nur noch die Pins anpassen müsste: https://github.com/yaacov/ArduinoModbusSlave/blob/master/examples/simple/simple.ino PC seitige Tools gibt es da schon genügend. Einbindung in andere Systeme (Smart Home, Raspi, Visu,...) sind auch schon alle vorhanden. Christoph M. schrieb: > Für mich reicht die Zuverlässigkeit der Übertragung im aktuellen Aufbau. > Das Bild zeigt den Zählerstand (8Bit) im MC über einige Minuten. Ist ja in Ordnung. Wenn es dir reicht. Du postest hier aber unter der Kategorie Projekte & Code wo man normalerweise anderen etwas zur Verfügung stellt. Und dafür finde ich es einfach nicht gut genug ehrlich gesagt. Aber das ist Ansichtssache.
>Hier ein Beispiel für Arduino wo man nur noch die Pins anpassen müsste: >https://github.com/yaacov/ArduinoModbusSlave/blob/master/examples/simple/simple.ino >PC seitige Tools gibt es da schon genügend. Einbindung in andere Systeme >(Smart Home, Raspi, Visu,...) sind auch schon alle vorhanden. Ah, da liegt vielleicht das Missverständnis: In diesem Thread geht es mir speziell um Python. Es geht hier nicht um eine fertige Bedienoberfläche oder ein fertiges Programm, sondern um Python-Templates mit GUI, mit denen man schnell eigene Messaufgaben automatisieren kann. Man kann sie sich hier einfach kopieren und für eigene Probleme anpassen. Man mag sich fragen, ob man so etwas Sinn macht: Meiner Meinung nach ja, weil man schneller ist, als sich die Codefragmente lange im Internet zusammenzusuchen. Vielleicht hast du ja schon einen Treiber für Python und Modbus gefunden. Eigentlich wäre es konsequent, auch die Mikrocontrollerseite in Python zu programmieren. Die Beschränkung auf einen Bus wie z.B. auf Modbus ist auch ein wenig zu eng, weil man bei einer Messaufgabe z.B. auch ein Oszi über TCP-IP parallel zum MC steuern können soll.
Christoph M. schrieb: > Es geht hier nicht um eine fertige > Bedienoberfläche oder ein fertiges Programm, sondern um Python-Templates > mit GUI, mit denen man schnell eigene Messaufgaben automatisieren kann. Und wenn es da jetzt ein Script geben würde, dass so universell wäre dass es über die unterschiedlichen Applikation im uC skalierbar wäre...dann wäre das auch echt cool. So in etwa wie: Beitrag "Neu: SerialComMCU Daten von der seriellen Schnittstelle visualisieren" oder Beitrag "Projekt: Virtuelle Instrumente an serielle Schnittstelle" Christoph M. schrieb: > Vielleicht hast du ja schon einen Treiber für Python und Modbus > gefunden. Hm, z.B. den? https://minimalmodbus.readthedocs.io/en/stable/readme.html#features Christoph M. schrieb: > Eigentlich wäre es konsequent, auch die Mikrocontrollerseite in Python > zu programmieren. Muss meiner Meinung nach nicht sein. Wenn sich jeder an den Standard hält sollte das ja (theoretisch) alles miteinander funktionieren. Python auf uC schränkt halt sehr stark ein. Denke z.B. nicht dass uPython auf einem ATtiny oder ähnlichem läuft. Die C/CPP Implementierung wahrscheinlich schon. Christoph M. schrieb: > Die Beschränkung auf einen Bus wie z.B. auf Modbus ist auch ein wenig zu > eng, weil man bei einer Messaufgabe z.B. auch ein Oszi über TCP-IP > parallel zum MC steuern können soll. Das eine schließt das andere ja nicht aus. Du kannst dein Oszi ja über das gleiche Python-Script steuern wie dein uC der aber Modbus über UART/RS485 (RTU) oder Ethernet spricht.
Ich persönlich würde SCPI bevorzugen: Der Arduino ist dann ein SCPI-Server ähnlich wie ein Messgerät. Hierzu gibt es schon fertige Arduino-Bibliotheken. Siehe z.B. https://github.com/Vrekrer/Vrekrer_scpi_parser In Python gibt es die Bibliothek py-Visa (https://pyvisa.readthedocs.io/en/latest/), mit der man auf Messgeräte zugreifen kann, die dem VISA-Standard entsprechen. SCPI ist Teil des Visa-Standards. Es sollte dann also auch funktionieren, auf den SCPI-Arduino zuzugreifen, falls man die SCPI-Kommandos nicht zu Fuß an den Arduino senden möchte. Beim Red Pitaya (STEMLab) verwende ich übrigens diesen Weg, was dort prima funktioniert.
Hier noch ein Beispiel wie man mit py-Visa die Messdaten eines Oszilloskops visualisiert: https://nbviewer.org/github/StefanMack/PraktMesstVISA/blob/master/AutomMessenMitPython.ipynb
Ok, vielleicht habt ihr Recht und ich sollte es mal mit fertigen Kommunikationsprotokollen versuchen. Hier als im Anhang der Frequenzgenerator mit SCPI Steuerung. Der funktioniert jetzt zuverlässig inclusive Rückmeldung und Identifier. Irgendwie ist es doch angenehm, wenn man mit der GUI den Status des Gerätes abfragen kann. Eigentlich ist die Benutzung der Library relativ einfach. Aber bis es jetzt so funktioniert hat, war ich doch eine Stunde beschäftigt.
Christoph M. schrieb: > Hier als im Anhang der Frequenzgenerator mit SCPI Steuerung. Unter 40 Zeilen Code pro Seite. Das wäre es mir wert dafür eine saubere Übertragung zu bekommen 👍 Christoph M. schrieb: > Aber bis es jetzt so funktioniert hat, war ich doch eine Stunde > beschäftigt. Woran lag es? Referenzen? Oder die Bedienung der Libraries?
>Woran lag es? Referenzen? Oder die Bedienung der Libraries?
Das Problem bei der Kommunikation zweier Teilnehmer ist, dass auf beiden
Seiten Fehler passieren können. Meiner Erfahrung nach ist die
Fehlersuche bei der Entwicklung von Kommunikationsprotokollen immer
kompliziert.
In dem Fall habe ich mich auf der Senderseite vertippt, aber die
Fehlersuche war dann schwierig, weil ich nicht wusste, ob es an der
Library oder am Sender liegt.
Das allererste Beispiel hier im Thread ist extrem einfach aber zeigt
sporadische Fehler. Ich habe noch einmal versucht, herauszubekommen, wo
die Ursache liegt. Die Hardware funktioniert zuverlässig und der Fehler
scheint in den Sende- oder Empfangsroutinen zu liegen.
Ich konnte nicht herausfinden, was der "Delimiter" von ReadString ist
1 | frequency = Serial.readString().toInt(); |
ist es das 0x00 aus C oder ist es 0x10, 0x13 ? Die Verwendung der SCPI Bibliothek vereinfacht die Sache zwar, weil es out of the Library-Box funktioniert. Sie hat aber z.B. auch keine Fehlerkorrektur. Mir scheint das Problem mit der Verwendung von "readString" und der Pythonseitigen Abstraktion "übertüncht".
:
Bearbeitet durch User
Die neueste Version: Frequenzgenerator + Frequenzzähler Läuft bis ca. 20kHz auf Arduino Uno. PiPico geht mit dem selben Code.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.