fischertechnik – Python – Bit-Schubsen
4 Motoren, 8 digitale und 2 analoge Eingänge statt mit der originalen Windows-Software unter Python (und Linux) ansteuern. Das 30402 intelligent Interface bietet ein serielles Protokoll dafür.
Bits und Bytes, binär und hexadezimal
Nachdem klar war wie binäre Register 1001011 als Byte b’\x03‚ über die serielle Schnittstelle geschickt werden und wie ein Rückgabe-Byte interpretiert wird, war es spannend die Schnittstelle auszureizen.
Es gibt 3 Modi um die Schnittstelle ansprechen zu können. Dabei wird immer ein Byte für den Modus und ein Byte für den Motorzustand zum Interface geschickt. Je nach Modus antwortet das Interface dann mit einem oder mit drei Bytes.
Drei Bytes werden zurückgeliefert wenn ein Analogwert von EX oder EY abgefragt und mitgeteilt werden soll.
Ein wilder Mix
Es ist einfach, einen Motor einzuschalten und einen Taster abzufragen. Aufwendiger wird es, wenn mehrere Taster gleichzeitig und der Analogwert von EX/EY abgefragt werden sollen. Ein Byte setzt sich ja aus mehreren binären Registerwerten zusammen. So muss das Byte analysiert werden um festzustellen, ob ein Taster oder gar mehrere gedrückt sind.
Einen Motor anzusteuern ist einfach, man schickt ’00 00 00 10′ für den Motor M1 rüber. Mit ’00 00 00 01′ ändert man die Drehrichtung des M1. Wenn ich M4 einschalten will muss ich ’10 00 00 00′ schicken. Dann ist aber M1 auf ’00‘ gesetzt und ist aus. Also M1 und M4 ist ’10 00 00 10′ wenn beide in die gleiche Richtung gehen sollen.
Im Beispiel ist M4 eine Lampe – der ist die Richtung egal. Sie soll aber anbleiben, obwohl M1 die Richtung ändert. Dazu wurde noch M2 eingebaut, der über einen Taster eingeschaltet wird und nur läuft, solange der Taster gedrückt ist.
Das heisst, wird der Taster an E1 gedrückt, wird der Motor 2 eingeschaltet. Es muss also ein ’10‘ für den 2. Motor in das aktuelle Motor-Ansteuerungs-Register eingeschleust werden. Dazu werden einzelne Bits gezielt gesetzt.
Aus ’10 00 00 10′ wird dann kurzfristig ’10 00 10 10′ und beim Loslassen des Tasters an E1 wieder ’10 00 00 10′.
EX+EY als Analogeingänge
Das Interface bietet zwei Analogeingänge. Sollen diese abgefragt werden, reicht das eine Byte für die Digital-Signalwerte nicht mehr aus. Es werden 2 Bytes benötigt um Werte zwischen 0-1024 übermitteln zu können. Das sind dann Low-Byte und High-Byte. Keine Sorge, das macht eine kleine Funktion automatisch. Im Beispiel habe ich den Wert noch in eine Prozentzahl umgerechnet. Das erschien mit handlicher.
Dazu habe ich eine Photozelle benutzt. Diese liefert je nach Lichteinfall einen anderen Wert.
Zuviel Theorie, hier ein Video
Reset-Taster
ganz rechts sitzt der grössere Taster, der liegt an E8 und dient als Reset-Taster, der die Programmausführung stoppt.
Nach dem Start des Python-Programms mit F5 aus IDLE heraus läuft sofort der Propeller los und die Lampe M4 leuchtet. Das liegt am Photosensor, der von einer Lampe angestrahlt wird.
Wird der Photosensor verdunkelt erlischt M4 und der Propeller stoppt.
Wird der 2. Taster gedrückt, ändert der Propeller seine Laufrichtung.
Beim Druck auf den ganz linken Taster an E1 klettert M2 an der Zahnstange hoch und der Propeller ist davon unbeeinflusst. M2 läuft nur solange E1 gedrückt ist.
E1 und E2 können gleichzeitig betätigt werden und dann erfolgt Drehrichtungsänderung und Hochklettern gleichzeitig.
Der rechte Taster stoppt die Programmausführung.
Die Funktionen
Programm-Start
Es werden in Python 3.8 nur binascii und serial importiert.
Die serielle Schnittstelle wird angegeben und mit 9.600bd voreingestellt.
Eine Liste data mit 3 Elementen wird angelegt.
Festlegen, was vom Interface erwartet wird.
Sollen nur die digitalen Eingänge oder auch die analogen Eingänge abgefragt werden ?
Prüfen was vom Interface zurückkommt
Hier wird das zurückgelieferte byte binär mit 8 Stellen aufbereitet. Dann wird entsprechend dem angefragten Eingangssignal (im Beispiel die Taster) geprüft ob dieses Bit gesetzt ist. Dazu wird eine Bitmaske erzeugt, die an der Stelle eine 1 hat, die überprüft werden soll. Alle anderen Stellen sind mit 0 besetzt.
Wenn ich nur prüfen will, ob Eingang 5 gesetzt ist, sieht das so aus:
E8 | E7 | E6 | E5 | E4 | E3 | E2 | E1 | |
Register | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
UND Maske | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Ergebnis-Bool | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Das Ergebnis ist größer 0 und somit True.
Das Motor-Byte aufbereiten
Ähnlich zu oben werden auch die Motoren in einem Register verwaltet. Jeder Motor benötigt 2 bits. Es wird damit Rechtslauf, Linkslauf und Aus gesteuert. Wenn das Register zusammengesetzt ist, wird es an diese Funktion übergeben, die daraus das über die Schnittstelle zu sendene Byte macht.
Analog-Eingänge EX/EY auswerten
Wenn EX oder EY zusätzlich zu den digitalen Eingängen abgefragt werden soll, muss ein anderes Commando an das Interface geschickt werden. Das wird weiter oben in callInterface() definiert.
Bei meinen Versuchen mit dem Lichtsensor habe ich bemerkt, dass es etwas dauert, bis ein stabiles Signal anliegt. Darum habe ich mich entschieden den Wert 10mal hintereinander abzufragen. Das ist die for-Schleife. Um nicht zeitkritisch zu werden und die etwa 300ms zwischen jedem Absetzen eines Kommandos zu überschreiten, wird auch in dieser Funktion das Command seriell rausgeschrieben.
Werden Analog-Werte angefordert, werden 3 Bytes eingelesen. Das 1. Byte ist das Register mit den digitalen Eingängen und das 2. und 3. Byte ergeben zusammen einen Wert zwischen 0 und 1024. Aus dem erhaltenen Wert wir ein Prozentsatz errechnet.
Bei 3 Bytes nur digitale Zustände auswerten
Werden vom Interface nur die digitalen Eingänge erwartet, werden diese mit checkEingang() ausgewertet. Werden jedoch auch die Analogwerte erwartet, wird mit checkDigital_Drei() nur das erste Byte von dreien ausgelesen.
Motoren starten, stoppen, umkehren
Immer 2 bit repräsentieren einen Motor. Je nach Motornummer stehen die Bits im Register. Das Bitpaar ’01‘ steht für LINKS, ’10‘ steht für RECHTS und ’00‘ steht für AUS.
M4 | M3 | M2 | M1 | |||||
Motoren | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
Eine kleine Hilfsfunktion ermöglicht es einzelne Bits zu löschen. Dadurch wird sichergestellt, dass ein definierter Ausgangszustand für die boolsche Operation mit der Bitmaske besteht. Würde ein Motor mit ’11‘ besetzt werden, müsste er Links und Rechts gleichzeitig drehen und bleibt stehen. Das regelt das Interface so.
Über eine ODER-Verknüpfung wird der neue Motor in den aktuell bestehenden Motorzustand hineingeschrieben. Damit wird ein Motor gesetzt ohne die Anderen zu verändern.
M4 | M3 | M2 | M1 | |||||
Motoren ALT | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
Motor einzeln | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
Motoren NEU | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
Vorbelegungen
Das Hauptprogramm
In der While-Schleife ist der Programmablauf festgelegt.
Das command, das über die serielle Schnittstelle ans Interface geschickt wird, wird zusammengesetzt und ausgegeben.
Der Lichtsensor wird wie ein Potentiometer betrachtet und analog abgefragt. Der Potentiometerwert wird nur ausgegeben, wenn er sich ändert.
Es erfolgt die Abfrage ob Eingang E1 aktiv ist. Wenn das zutrifft, wird der Motor M2 mit Rechtslauf angefordert.
Wenn der Potentiometer einen Wert über 60% erreicht, wir der Propeller an M1 im Linkslauf gestartet. Gleichzeitig wird die Lampe an M4 eingeschaltet. Hier ist Rechts/Linkslauf unbedeutend.
Wenn aber der Potentiometer über 60% ist und gleichzeitig der Taster an E2 gedrückt wird, dann ändert sich die Drehrichtung von Motor M1. Die Lampe an M4 bleibt an.
Zum Schluss noch die Abfrage für den Reset-Taster. Wird der Taster an E8 gedrückt, wir die While-Schleife mit break abgebrochen und das Python-Script mit einer Mitteilung beendet.
Das war es schon, den Download-Link für das Python-Programm gibt’s hier.
Ich würde mich freuen, wenn ihr eine Nachricht schickt mit einem Bild oder einem kleinen Video von eurer Lösung. Falls es nicht geklappt hat oder ihr Verbesserungen habt, bitte schreibt mir einen Kommentar.
fischertechnik 30402 und Python
Schreibe einen Kommentar