Mit dem Raspberry Pi morsen / Tieferer Einstieg in Python
Mit der Schaltung mit LED und Buzzer, bei der wir das letzte Mal aufgehört haben, wollen wir jetzt weitermachen. Allerdings bleibt die Schaltung auf dem Breadboard und unserem Raspberry Pi so, 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; da gibt es ein paar kostenlose für Android) 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:
# -*- 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:
- Die Angaben von der Kommandozeile als zu morsenden Text entgegennehmen
- Den Text in Morsecode mit Punkten und Strichen umwandeln
- Die einzelnen Symbole im Morsecode (.- etc.) interpretieren und die LED und den Buzzer entsprechend lang ansteuern und symbolabhängigen Pausen machen
Die Umsetzung von Buchstaben zu Morsezeichen kann mit dem Morse Code Enkoder/Dekoder auf Kryptografie.de überprüft werden.
Aber noch einmal zum Code ansich: die erste Zeile # -*- encoding: utf-8 -*- 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.
Wie oben gezeigt, kann die Android App Morse Code Agent nicht nur über das Mikrofon lauschen und Morse erkennen, sondern es kann auch Lichtsignale über die Kamera einfangen. Normalerweise soll das für eine Taschenlampe sein, aber unsere LED tut es genauso gut. Auch hier werden die Morsesignale an der LED erkannt und wiedergegeben (siehe Bild oben).
Unser Projekt gibt vielleicht ein ganz nützliches Tool für Amateurfunker ab, die mal etwas morsen müssen, obwohl ich glaube, dass die da noch ganz andere Programme haben. Obwohl: Der Raspberry Pi ist kleiner und mobiler als ein Notebook.