10.04.2025 Microcontroller am Beispiel von Morsecode

1. Einleitung

1.1 Motivation

Das Ziel dieses fachlichen Versuchs ist es, die Grundlagen der Mikrocontroller-Programmierung anhand des Arduino-Systems zu erlernen und anzuwenden. Dabei wird ein Arduino genutzt, um Morsecode-Signale zu erzeugen und zu dekodieren. Dies ermöglicht ein tieferes Verständnis für digitale Signalverarbeitung, Timing-Steuerung und serielle Kommunikation.

Durch den Versuch sollen folgende Kompetenzen erworben werden:

  • Grundlegende Programmierung und Steuerung eines Mikrocontrollers (Arduino)

  • Umsetzung eines Kommunikationssystems mit Morsecode

  • Anwendung von LED zur Erzeugung von Morsezeichen

  • Grundlagen der Signalverarbeitung und Decodierung von Zeichen

Dieser Versuch verbindet praktische Mikrocontroller-Anwendungen mit einem klassischen Kommunikationsverfahren.

1.2 Aufgabenstellung

Im Rahmen dieses Projekts sollte ein Arduino-Mikrocontroller so programmiert werden, dass er Morsecode sowohl ausgeben als auch empfangen kann. Dabei lag der Fokus auf zwei Teilaufgaben:

  1. Ausgabe: Ein über die serielle Konsole eingegebener Text sollte in Morsecode übersetzt und über verschiedene Ausgabemedien (Konsole, LED, LCD-Display) dargestellt werden. Dazu sollte die Morsecode-Logik mithilfe einer Bibliothek umgesetzt und auf verschiedene Hardware-Komponenten übertragen werden.

  2. Eingabe: Über ein Mikrofonmodul sollten Tonimpulse (kurz/lang) erkannt und interpretiert werden. Die Tonfolgen sollten gespeichert und auf der Konsole ausgegeben werden. Ziel war es, zwischen kurzen und langen Tönen zu unterscheiden und diese als Morsezeichen zu erfassen.

1.2.1 Aufgabenverteilung

Franka Schmid und Max Grauvogl kümmerten sich um die Ausgabe des Morse Codes, erst auf die Console, dann auf ein LCD-Display. Dabei war Franka hauptsächlich für das Programmieren zuständig und Max für die Anbindung der Hardware.

Verena Feike und Chieme Hangen waren für die Morse Code Eingabe zuständig. Dabei waren beide bei allen Arbeitschritt dabei.

2. Grundlagen

2.1 Arduino

2.1.1 Was ist Arduino?

Arduino ist eine Open-source Plattform für die Entwicklung von Microcontroller Projekten. Hierfür wird ein Arduino Board (Hardware) und die Arduino IDE(Software) benötigt.

2.1.2 Aufbau eines Arduino Boards (Hardware)

Es gibt unterschiedliche Boards, welche unterschiedliche Größen, Leistungsstufen oder verschiedene Anzahl von In- und Outputpins haben. Hierzu gehören beispielsweise

  • Arduino Uno

  • Arduino Mega

  • Arduino Nano

2.1.3 Wichtigste Komponenten:

Ein Arduino Board enthält normalerweise folgende Hauptkomponenten: alt text

  1. Microcontroller- führt die Programme aus

  2. USB-Port- Ermöglicht die Verbindung des Arduino-Boards zum Computer

  3. USB to Serial Chip- Übersetzt Daten vom Computer für den Microcontroller; macht die Programmierung vom Computer aus möglich

  4. Digital pins (0,1)- Ein- und Ausgang für digitale Signale, z.B. zum An- und Auschalten von LEDs

  5. Analog pins- Zum Lesen von analogen Werten (0-1023)

  6. 5V /3.3V pins- Für externe Komponenten

  7. GND- „Ground“, „Negative“, „-“; Masseanschluss für den Stromkreis

  8. VIN- (Voltage In) für externe Spannungsquellen

2.2 Arduino IDE (Software)

Die IDE wird zum Schreiben, Kompilieren und Hochladen von Programmen genutzt. Diese kann unter ww.arduino.cc heruntergeladen und dann installiert werden.

2.2.1 Aufbau eines Arduino-Programms

Jedes Programm hat folgende Funktionen:

void setup() {
  pinMode(13, OUTPUT); // Pin 13 als Ausgang setzen
}


void loop() {
  digitalWrite(13, HIGH); // LED an
  delay(1000); // 1 Sekunde warten
  digitalWrite(13, LOW); // LED aus
  delay(1000);
}
  • void setup() wird einmalig zu Beginn des Programms ausgeführt. Setzen von wichtigen Ein- und Ausgängen.

  • void loop() wird immer wieder in einer Schleife ausgeführt

2.2.2 Elektronik & Zubehör

LEDs

Bei einer LEDs muss ein Vorwiderstand genutzt werden. Dieser verhindert, dass zu viel Strom durch die LED fließt und diese dadurch beschädigt wird.

Sensoren /Aktoren

Ein Arduino kann mit Sensoren und Aktoren verbunden werden, z.B. Temperatursensoren, Lichtsensoren, Geräuschsensoren. Aktoren wären beispielsweise Servomoteren.

Arduino Boards

Für unsere Projekte reicht ein Arduino Uno. Dieser ist besonders für Anfänger geeignet. Ein Vergleich der wichtigsten Boards:

Board

Arduino Uno

Arduino Mega

Arduino Micro

PINS, digital

14

54

20

PINS, digital mit PWM

6

15

7

PINS, analog

6

16

12

Taktrate

16 MHz

16MHz

16MHz

Flashspeicher

32kB

256 kB

32 kB

SRAM

2

8

2,5

Processor

ATmega328P

ATmega 2560

ATmega32U4

Auf dem Arduino Mega können somit deutlich längere Programme hochgeladen werden. Alle Boards sind gleich schnell, da sie alle die gleiche Taktrate haben. Je größer der SRAM desto mehr Variablen kann der Arduino bereitstellen und verarbeiten.

3. Versuchsdurchführung

Zwei seperate Versuche wurden durchgeführt: Ein Versuch zur Ausgabe und ein Versuch zur eingabe des Morse Codes.

3.1 Ausgabe von Morsecode

Um mithilfe eines Arduinos via Morsecode zu kommunizieren, ist eine geeignete Ausgabe des generierten Morsecodes erforderlich. Dies kann beispielsweise über die serielle Konsole der Arduino-IDE, eine LED oder ein externes LCD-Display erfolgen.

Nutzereingabe über die serielle Konsole

Zunächst muss es möglich sein Text in der Konsole der Arduino IDE einzugeben.

In der Methode setup() fügen wir dafür folgenden Code hinzu: Serial.begin(9600); Dies wird benötigt um die serielle Kommunikation zwischen Arduinoboard und dem Computer/ der Arduino IDE zu realisieren.

Die loop()-Funktion wird kontinuierlich vom Arduino aufgerufen. Hier ergänzen wir den Code zur Verarbeitung der Benutzereingabe:

if (Serial.available() > 0) {
  String input = Serial.readStringUntil('\n');
  Serial.print("Du hast eingegeben: ");
  Serial.println(input);
}

Nun wird der auf der Konsole eingegebene Text auf der Konsole ausgegeben.

Übersetzung in Morsecode

Zur Übersetzung des eingegebenen Textes in Morsecode verwenden wir die Bibliothek Morse.h, welche die eigentliche Konvertierung übernimmt.

Innerhalb einer Funktion receiver() definieren wir, wie mit dem übersetzten Zeichen umgegangen werden soll – in diesem Fall wird es einfach auf der Konsole ausgegeben:

#include "Morse.h"

Morse morse;

void receiver(char e) {
  Serial.print(e);
}

Mit dem Aufruf morse.println(input); kann der Input übersetzt und behandelt werden. Diese Zeile wird der loop()-Methode hinzugefügt.

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    Serial.print("Du hast eingegeben: ");

    Serial.println(input);
    morse.println(input);
  }
}
Morsecode-Logik in der transmiter()-Funktion

In einer separaten Funktion transmiter() wird definiert, welches Symbol wie dargestellt werden soll:

void transmiter(uint8_t e) {
  if (e == MORSE_CHAR) {
    Serial.print("/");
  }
  else if (e == MORSE_SPACE){
    Serial.print(" ");
  }
  else if (e == MORSE_EOL){
    Serial.println(" EOL ");
  }
  else if (e == MORSE_DI || e == MORSE_DIT){
    Serial.print(".");
  }
  else if (e == MORSE_DAH){
    Serial.print("-");
  }
}

Die Bibliothek übersetzt die Buchstaben der Benutzereingabe in Integer-Werte, die mit bestimmten Konstanten (Enums) verknüpft sind:

  • MORSE_CHAR: Ein Buchstabe ist abgeschlossen

  • MORSE_SPACE: Ein Leerzeichen

  • MORSE_EOL: Zeilenende

  • MORSE_DI bzw. MORSE_DIT: Kurzer Ton (Punkt)

  • MORSE_DAH: Langer Ton (Strich)

Nun wird überprüft, welchem dieser Varianten der Typ entspricht und das ensprechende Zeichen wird auf der Konsole ausgegeben.

Diese beiden Methoden müssen jetzt nur noch dem Morse-Modul innerhalb der setup() Methode übergeben werden: morse.begin(receiver, transmiter);

Der Code soll nun so erweitert werden, dass auch eine LED und ein LCD Display den Morsecode anzeigen.

Anzeigen mithilfe einer LED

Für die LED muss zunächst die richtige LED angesteuert werden. Dafür ergänzen wir die Zeile pinMode(LED_BUILTIN, OUTPUT); vor morse.begin(receiver, transmiter); in der setup()-Methode.

Die Transmitter Methode passen wir nun so an, dass im Falle eines MORSE_DI, MORSE_DIT oder MORSE_DAH die Lampe für einen gewissen Zeitraum leuchtet:

void transmiter(uint8_t e) {
  if (e == MORSE_GAP){
    delay(DIT_DURATION);
  }
  if (e == MORSE_CHAR){
    Serial.print("/");
    delay(DAH_DURATION);
  }
  else if (e == MORSE_SPACE){
    Serial.print(" ");
    delay(7 \* DIT_DURATION);
  }
  else if (e == MORSE_EOL) Serial.println(" EOL ");
  else {
    digitalWrite(LED_BUILTIN, HIGH);
  if (e == MORSE_DI || e == MORSE_DIT){
    Serial.print(".");
    delay(DIT_DURATION);
  }
  else if (e == MORSE_DAH){
    Serial.print("-");
    delay(DAH_DURATION);
  }
  digitalWrite(LED_BUILTIN, LOW);
  delay(DIT_DURATION);
  }
}

Die Funktion delay() wird verwendet um Pausen zwischen den Zeichen einzufügen und um die LED für die gewünschte Zeit leuchten zu lassen. Mit digitalWrite(LED_BUILTIN, HIGH); wird die LED angeschalten. Je nach dem ob das Zeichen ein kurzer oder ein langer Laut ist, leuchtet die LED nun für einen gewissen Zeitraum. digitalWrite(LED_BUILTIN, LOW); lässt die LED erlöschen.

Anzeigen mithilfe eines LCD Displays

Analog zur Anzeige auf der Konsole soll der Text nun auch auf einem externen Display angezeigt werden. Hierfür müssen in der Setup Methode die Ansteuerungen für die einzelnen Pins definiert werden:

#include <LiquidCrystal.h>
#include "Morse.h"

Morse morse;

const int DIT_DURATION = 200;
const int DAH_DURATION = 3 * DIT_DURATION;

const int rs = A3, en = A5, d4 = A9, d5 = A10, d6 = A11, d7 = A12;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  Serial.begin(9600);
   pinMode(A14,OUTPUT);
 pinMode(A13,OUTPUT);
  pinMode(A4,OUTPUT);
  pinMode(A0,OUTPUT);
  pinMode(A2,OUTPUT);
  pinMode(A1,OUTPUT);
 digitalWrite(A14,LOW);
 digitalWrite(A13,HIGH);
  digitalWrite(A4,LOW);
  digitalWrite(A0,LOW);
  digitalWrite(A2,LOW);
  digitalWrite(A1,HIGH);
  pinMode(LED_BUILTIN, OUTPUT);
  morse.begin(receiver, transmiter);
}

In der transmitter() Methode wird abschließend noch unter jedem Serial.print(c); Aufruf ein analoger lcd.print(c); Aufruf eingefügt.

3.2 Eingabe von Morse Codes

3.2.1 Ziel

Über das Toneingabegerät soll eine Folge aus langen und kurzen Tönen eingegeben werden können. Diese soll anschließend auf der Kommandozeile ausgegeben werden.

3.2.2 Versuchsaufbau

Als Toneingabegerät wurde das KY-038 LM393 Sound Detection Module verwendet. Die digitale Ausgabe dieses Moduls kam dabei zum Einsatz. Zusätzlich wurde eine LED eingebaut, die aufleuchtet, sobald ein Ton erkannt wird.

int micPin = 2;
int ledPin = 13;
int micValue = 0;
int ledState = 0;
int index = 0;
int arrayMaxLength = 10;
bool* text = new bool[arrayMaxLength];  // Dynamically allocated array

Im ersten Codeabschnitt werden die benötigten Variablen deklariert und initialisiert. Der Mikrofoneingang ist an Pin 2 angeschlossen, die LED an Pin 13. Das Array text dient zur Speicherung der erkannten Tonfolge, wobei jeder Eintrag einen kurzen (false) oder langen Ton (true) repräsentiert. Der index markiert die stelle des Arrays, die zuletzt beschrieben wurde.

void setup() {
  for (int i = 0; i < arrayMaxLength; i++) {
    text[i] = false;  // Initialize all values to false
  }

  // Variables for holding the mic value and led state
  pinMode(micPin, INPUT);   // Configures the sound sensor pin as input
  pinMode(ledPin, OUTPUT);  // Configures the LED pin as output
  Serial.begin(9600);
}

In der setup() methode werden alle Werte im text-array mit false initialisiert. Außerdem werde der Pinmode für das Microphon und die LED gesetzt. Zum Schluss wird die serielle Schnittstelle mit einer Baudrate (Bit pro Sekunde) von 9600 zur Ausgabe auf der Konsole gestartet.

void loop() {
  // Read the sound sensor value
  micValue = digitalRead(micPin);

  // Check if the sound sensor has detected noise
  if (micValue == LOW) {
    digitalWrite(ledPin, LOW);
  } else {
    digitalWrite(ledPin, HIGH);  // Toggles the LED state
    digitalWrite(micPin, 0);
    delay(300);  // Pauses for 300ms seconds
    micValue = digitalRead(micPin);
    if (micValue == HIGH) {
      text[index] = true;
      Serial.println("lang");
    } else {
      text[index] = false;
      Serial.println("kurz");
    }
    index++;

Zu Beginn der loop()-Methode wird zunächst der aktuelle Wert des Mikrofonsensors ausgelesen. Wird ein Ton erkannt, leuchtet die LED auf, und das Programm pausiert für 300 Millisekunden. Anschließend wird der Sensorwert erneut ausgelesen, um zu entscheiden, ob es sich um einen langen oder kurzen Ton handelt. Ein langer Ton wird durch ein erneut erkanntes Signal (HIGH) nach der Pause bestimmt und im Array als true gespeichert. Ein kurzer Ton hingegen wird als false abgespeichert. Zusätzlich wird der erkannte Ton („lang“ oder „kurz“) über die serielle Schnittstelle ausgegeben.

    // wait till sound is gone to start next input
    do {
      digitalWrite(micPin, 0);
      delay(100);
      micValue = digitalRead(micPin);
    } while (micValue == HIGH);
    delay(300);
    Serial.print("next input:");
    digitalWrite(ledPin, LOW);
  }

Danach wird gewartet bis der Ton vorbei ist, damit nicht ein sehr langer Ton fälschlicherweise als mehrere kurze Töne erkannt wird. Dazu wird im Abstand von 100 Millisekunden überprüft, ob weiterhin ein Signal anliegt. Sobald kein vorhanden ist, wird ein letzes Mal 300ms gewartet. Danach wird die LED ausgeschaltet und über die serielle Schnittstelle die Meldung „next input:“ ausgegeben – dies signalisiert, dass nun der nächste Ton eingegeben werden kann.

  // extend input array, if neccessary
  if (index == arrayMaxLength) {
    // Create a new larger array
    bool* temp = new bool[arrayMaxLength * 2];

    // Copy old data to new array
    for (int i = 0; i < arrayMaxLength; i++) {
      if (text[i]) {
        Serial.print("-");
      } else {
        Serial.print(".");
      }
      temp[i] = text[i];
    }
    Serial.println("end line");

    // Free the old array and point text to the new one
    delete[] text;
    text = temp;

    // Update arrayMaxLength
    arrayMaxLength *= 2;
  }
}

Wenn die maximale Länge des Arrays erreicht ist, wird das Array dynamisch vergrößert. Dazu wird ein neues Array mit der doppelten Größe erstellt, und alle bisherigen Einträge werden in das neue Array kopiert. Währenddessen wird zur Kontrolle die bisher gespeicherte Tonfolge in Form von Punkten (für kurze Töne) und Strichen (für lange Töne) auf der Konsole ausgegeben. Anschließend wird das alte Array freigegeben und der Zeiger text auf das neue, größere Array gesetzt. Die Variable arrayMaxLength wird entsprechend angepasst.

Der ganze Code nochmal zusammengesetzt:

int micPin = 2;
int ledPin = 13;
int micValue = 0;
int ledState = 0;
int index = 0;
int arrayMaxLength = 10;
bool* text = new bool[arrayMaxLength];  // Dynamically allocated array

void setup() {
  for (int i = 0; i < arrayMaxLength; i++) {
    text[i] = false;  // Initialize all values to false
  }

  // Variables for holding the mic value and led state
  pinMode(micPin, INPUT);   // Configures the sound sensor pin as input
  pinMode(ledPin, OUTPUT);  // Configures the LED pin as output
  Serial.begin(9600);
}

void loop() {
  // Read the sound sensor value
  micValue = digitalRead(micPin);

  // Check if the sound sensor has detected noise
  if (micValue == LOW) {
    digitalWrite(ledPin, LOW);
  } else {
    digitalWrite(ledPin, HIGH);  // Toggles the LED state
    digitalWrite(micPin, 0);
    delay(300);  // Pauses for 300ms seconds
    micValue = digitalRead(micPin);
    if (micValue == HIGH) {
      text[index] = true;
      Serial.println("lang");
    } else {
      text[index] = false;
      Serial.println("kurz");
    }
    index++;
        // wait till sound is gone to start next input
    do {
      digitalWrite(micPin, 0);
      delay(100);
      micValue = digitalRead(micPin);
    } while (micValue == HIGH);
    delay(300);
    Serial.print("next input:");
    digitalWrite(ledPin, LOW);
  }
    
  // extend input array, if neccessary
  if (index == arrayMaxLength) {
    // Create a new larger array
    bool* temp = new bool[arrayMaxLength * 2];

    // Copy old data to new array
    for (int i = 0; i < arrayMaxLength; i++) {
      if (text[i]) {
        Serial.print("-");
      } else {
        Serial.print(".");
      }
      temp[i] = text[i];
    }
    Serial.println("end line");

    // Free the old array and point text to the new one
    delete[] text;
    text = temp;

    // Update arrayMaxLength
    arrayMaxLength *= 2;
  }
}

3.3.3 Ergebnisse

Kurze Töne werden zuverlässig erkannt. Leider hat das Programm Schwierigkeiten, lange Töne korrekt zu erfassen – diese werden manchmal fälschlicherweise als mehrere kurze Töne abgespeichert. Die Ursache dafür könnte entweder in einer ungenauen Implementierung oder in den Einschränkungen des verwendeten Moduls liegen.

4. Zusammenfassung

In diesem Versuch wurde die Funktionsweise eines Mikrocontrollers am Beispiel der Morsecode-Kommunikation praktisch erprobt. Dabei wurde ein Arduino verwendet, um Texteingaben in Morsecode zu übersetzen und über Konsole, LED und LCD-Display auszugeben. Die Eingabe von Morsecode erfolgte über ein Mikrofonmodul, wobei kurze und lange Töne unterschieden und in einem Array gespeichert wurden.

Probleme

Die Ausgabe funktionierte zuverlässig über alle Medien hinweg. Die Eingabe hingegen zeigte Schwächen bei der Erkennung langer Töne, was auf die begrenzte Genauigkeit des Sensors oder Optimierungsbedarf im Code zurückzuführen sein könnte. Insgesamt konnte durch den Versuch ein tieferes Verständnis für Mikrocontroller-Programmierung, digitale Signalverarbeitung und einfache Kommunikationsprotokolle gewonnen werden.

5. Literaturverzeichnis

Söderby, Karl (2024): Getting Started with Arduino, [online] https://docs.arduino.cc/learn/starting-guide/getting-started-arduino/?_gl=1cseioa_upMQ.._gaOTE5MTU0NDEwLjE3NDQwMjY4NjA._ga_NEXN8H46L5*MTc0NDAyNjg1OS4xLjAuMTc0NDAyNjg1OS4wLjAuNzI2OTA2MTYz [abgerufen am 07.04.2025]

Gudino, Miguel (2017): Arduino Uno, Mega und Micro im Vergleich, [online] https://www.arrow.de/research-and-events/articles/arduino-uno-vs-mega-vs-micro erstellt am 22.12.2017 [abgerufen am 07.04.2025]

arunav1 (2022): Easiest way to connect LCD screen to Arduino mega!, [online] https://projecthub.arduino.cc/arunav1/easiest-way-to-connect-lcd-screen-to-arduino-mega-0fc95b, erstellt am May 11, 2022 [abgerufen am 07.04.2025]

Weis, Thomas (2018): Arduino Sound Sensor Modul – Tutorial, [online] https://www.makerblog.at/2018/11/arduino-sound-sensor-modul-tutorial/ [abgerufen am 09.04.2025]