Mit dem Raspberry Pi morsen / Tieferer Einstieg in Python

Mit der Schaltung mit LED und Buzzer, bei der wir das letzte Mal aufgehört habe, wollen wir jetzt weitermachen. Allerdings bleibt die Schaltung, wie sie ist. Dafür wollen wir dem rudimentären Python-Programm jetzt etwas mehr Funktionalität einhauchen und es dazu bringen, das es via LED und Buzzer morst und das bitte schön auch so, dass man (oder ein entsprechendes Analyse-Programm) es auch verstehen kann.

Dazu soll dem Programm ein beliebiger Text übergeben werden können, den dieses in Morsezeichen umsetzt und diese dann auch gleich morst.

Das soll dann so aussehen:





Dafür ist der folgende Code zuständig, der mit den Inline-Kommentaren schon gut dokumentiert ist. Auf ein paar Eigenarten gehen ich nach dem Listing ein.

# -*- encoding: utf-8 -*- # 2018 by Oliver Kuhlemann # Bei Verwendung freue ich mich über Namensnennung, # Quellenangabe und Verlinkung # Quelle: http://cool-web.de/raspberry/ import RPi.GPIO as GPIO # Funktionen für die GPIO-Ansteuerung laden import time # für sleep() (Pause) nötig import sys # um die übergebenen Parameter von der Shell abzufragen GPIO.setmode(GPIO.BCM) # die GPIO-Pins im BCM-Modus ansprechen led=14 # unsere LED hängt an BCM-Pin 14 GPIO.setup(led, GPIO.OUT) # in den LED-Pin wird "geschrieben" # Morsecode, siehe https://de.wikipedia.org/wiki/Morsezeichen einh=0.080 # Länge einer Einheit in Sekunden dit=einh*1 # Das Dit ist eine Einheit lang dah=einh*3 # Ein Dah ist dreimal so lang wie ein Dit. pSymbol=einh*1 # Die Pause zwischen zwei gesendeten Symbolen ist ein Dit lang. pBuchst=einh*2 # Zwischen Buchstaben in einem Wort wird eine Pause von der # Länge eines Dah (oder drei Dits) eingeschoben. pWort=einh*6 # Die Länge der Pause zwischen Wörtern entspricht sieben Dits. # bei der Pause zwischen Buchstaben und Wörtern wurde ja schon # eine Pause von Länge 1 Einh gesendet, darum abziehen # Nachfolgend sind alle Morse-Zeichen in einem sog. Dictionary definiert morseCode = {" ":"/","A":".-","B":"-...","C":"-.-.","D":"-..","E":".","F":"..-.", "G":"--.","H":"....","I":"..","J":".---","K":"-.-","L":".-..","M":"--","N":"-.", "O":"---","P":".--.","Q":"--.-","R":".-.","S":"...","T":"-","U":"..-","V":"...-", "W":".--","X":"-..-","Y":"-.--","Z":"--..","0":"-----","1":".----","2":"..---", "3":"...--","4":"....-","5":".....","6":"-....","7":"--...","8":"---..", "9":"----.","Ä":".--.-","Ö":"---.","Ü":"..--","ä":".--.-","ö":"---.","ü":"..--", "ß":"...--..",".":".-.-.-",",":"--..--",":":"---...",";":"-.-.-.","?":"..--..", "-":"-....-","(":"-.--.",")":"-.--.-","'":".----.","=":"-...-","+":".-.-.", "/":" ","@":".--.-."} def beep(t): # diese Zeile definiert eine Funktion GPIO.output(led, GPIO.HIGH) # LED-Pin auf High (+3.3V) setzen = einschalten time.sleep (t) # t Sekunden warten GPIO.output(led, GPIO.LOW) # LED-Pin auf Low (0V) setzen = ausschalten def morse(symbols): for sym in symbols: # For-Schleife: Gehe alle Zeichen in symbole durch if sym == ".": # Die Einzelzeichen finden sich dann jeweil in sym beep (dit) time.sleep (pSymbol) elif sym == "-": # elif ist das Else If in Python beep (dah) time.sleep (pSymbol) elif sym == " ": time.sleep (pBuchst) elif sym == "/": time.sleep (pWort) # ein EndIf gibt es in Python nicht. Ein Block endet, # wenn die Einrückung endet def textToMorse(text): code = "" for bst in text: code+= morseCode[bst]+" " # "code += ..." bedeutet das Gleiche wie "code = code + ..." return code # code ist unser Funktionsergebnis und Rückgabewert text = "Hallo Welt" # Unser Standardtext, wenn nichts übergeben wird argc=len(sys.argv) # Anzahl der übergebenen Wörter hinter "python morse.py ..." if argc > 1: # Falls etwas übergeben... 1. Arg ist immer der Programmname text = "" for nr in range (1,argc): # dann werden alle übergebene Werte ... text += sys.argv[nr] # zu unserem zu morsenden Text if nr < argc-1: text += " " else: print "Kein Text übergeben, benutze Standard-Text "+text text=text.upper() # Unser Dictionary kennt nur die Entsprechungen für die # Großbuchstaben. Darum machen wir den Text GROSS. # Für Umlaute funktioniert das nicht. Darum stehen die # kleinen Umlaute auch nochmals im Dictionary print "Morse den Text: " + text code=textToMorse (text) # Aufruf der Funktion, die den Text zu Morsezeichen # übersetzt print ("Das entspricht: " + code) morse (code) # Aufruf der Funktion, die das Pipen und Blinken # bewerkstelligt GPIO.cleanup() # Programm sauber verlassen und Resourcen wieder freigeben Was unser Programm grob macht: Wie man rechts sieht, schafft es der Morse Code Agent, eine Android App, den Morsecode sowohl akustisch als auch optisch (2. Bild) zu interpretieren und den ursprünglichen Text wieder auszugeben - das soll uns Beweis genug dafür sein, dass das Programm das richtige Timing beherrscht.

Aber noch einmal zum Code ansich: die erste Zeile ist wichtig und sorgt dafür, das Umlaute, die im Windows-Editor gespeichert wurden nicht gleich einen Syntax Fehler im Python Interpreter verursachen.

Nach ein paar Zeilen, die wir schon kennen, folgen dann Definitionen für das Timing mit ein paar Variablen, um die Sache lesbarer und änderbarer zu machen. Wenn man Einh z. B. auf 0.040 setzt, dann morst das Programm doppelt so schnell.

Dann folgt die Definition eines sogenanntes Dictionarys mit morseCode=... In geschweiften Klammern werden hier eine Reihe von Paaren, durch Komma getrennt, angegeben. Jedes Paar im Format "Suche":"Finde". Später kann man dann auf das Dictionary zugreifen und morseCode["A"] würde dann ".-" zurückliefern. Das ist doch sehr viel komfortabler als ewig viele If-Abfragen. Da wir nur Großbuchstaben im Dict haben, aber bei der Suche nach Groß-/Kleinschreibung unterschieden wird, müssen wir später unseren Text mit .upper() groß machen.

mit def beep(t): beginnt unsere erste Funktion, denn so wird eine Funktion in Python definiert. "def" ist das Schlüsselwort für eine Funktionsdefinition, "beep" der Funktionsname und das t in Klammern ist ein Übergabeparameter, den wir beim Aufruf übergeben müssen. In diesem Fall ist t die Zeit, die unser LED-Pin auf High geschaltet bleibt, was zum Leuchten der LED und dem Piepen des Buzzers führt (das kennen wir ja schon aus dem letzten Projekt).

Danach folgt ein Doppelpunkt. Ein Doppelpunkt leitet immer einen sogenannten Block ein, das sind Anweisungen, die zusammengehören, hier sind es alle Zeilen, die zur Funktion beep gehören. Das Ende erkennt Python am Ende der Einrückung. Die Einrückungen sind also nicht nur kosmetisch, sondern syntaktisch wichtig. Ich persönlich finde dieses "Feature" eher nervig. Denn mal benutze ich zum Einrücken die Tab-Taste für mehrere Zeilen und mal die Leertaste, um eine einzelne Zeile einzurücken. Da das in den letzten Jahrzehnten, in den ich schon programmiert habe, immer vollkommen egal war, habe ich mir das aus Bequemlichkeit irgendwie so angewöhnt. Jetzt nervt Python allerdings immer mit Syntax Errors und das Blöcke angeblich nicht geschlossen sind. Im Editor sieht alles ganz normal aus, denn Leerzeichen und Tab sehen gleich aus. Da hilft dann nur die "Alle Tabulatorzeichen zu Leerzeichen ersetzen" Funktion im Editor. Ich persönlich finde ein Endif wie in Basic oder eine geschweifte Klammer zu wie in C oder Java ja übersichtlicher. Aber sei's drum.

In der folgenden Funktion morse finden wir dann for ... in-Schleife. Das ist auch eine praktische Syntax in Python, um alle Elemente einer Sammlung, hier einer Zeichenkette, durchzugehen. In sym ist dann jeweils ein Zeichen (Punkt, Strich, Leerzeichen, oder "/") zu finden. An der Einrückung erkennt man, was die Schleife umfasst: alles bis zum nächsten def.

Pro Schleifen-Durchlauf wird je nach Symbol etwas anders getan. Das geschieht durch eine bedingte Verzweigung oder auch If-Abfrage genannt. Dabei wird eine Bedingung gestellt und geprüft, ob diese wahr ist. if sym == ".": wird z. B. ausgeführt, wenn das Symbol ein Punkt ist. Ein Punkt ist einer kurzer Ton, also wird beep mit der Zeitspanne dit, der kurzen aufgerufen. Auch if bildet einen Block. Es könnten also auch mehrere Zeilen eingerückt sein, die dann alle ausgeführt würden, wenn die Bedingung war ist.

elif ist Pythons Kürzel für "Else if" und heißt so viel "falls ansonsten aber folgendes zutrifft", bei elif sym == "-": also: "falls sym kein Punkt war, sym aber ein Strich ist". Dann gäbe es noch den Befehl else:, der hier nicht aufgeführt ist, aber für "falls das alles nicht zutrifft, dann aber...". Wollte man z. B. eine Fehlermeldung ausgeben, würde man "else: [Einrückung] print "Das Zeichen " + sym + " wird nicht unterstützt." [Einrückung Ende] schreiben.

In der Funktion textToMorse ist besonders die Zeile code += morseCode[bst] interessant, die aus dem oben definierten Dictionary für jeden Buchstaben des zu morsenden Textes die Morse-Code-Entsprechung holt und an den Morsecode anhängt.

Danach sind alle nötigen Funktionen definiert und das Hauptprogramm beginnt, so wie es ganz oben schon grob beschrieben wurde.