Morse code generator

Vroeger werd Morse gebruikt in de telegrafie om boodschappen te verzenden. Hoewel het gebruik overal ter wereld is gestopt zijn er nog veel liefhebbers die zelf boodschappen in Morse kunnen verzenden. Een Morse boodschap bestaat uit piepjes met een duur van 1 of 3 eenheden, waartussen pauzes zitten. Het is niet al te moeilijk om een programma te maken dat dit automatisch doet. Je hebt hiervoor naast een Arduino slechts twee draden en een luidspreker nodig en eventueel een weerstand van ca. 100 Ω. Ik gebruik een luidspreker uit een oude computerkast, maar met een passieve buzzer gaat het ook. Het programma werkt als volgt: in de Seriële monitor type je een zin in. Na verzending hoor je de zin in Morse en zie je dat ook weergegeven in de Seriële monitor. Sluit de luidspreker aan op GND en (via de weerstand) op een vrije pin, in mijn voorbeeld op digitale pin 2. De weerstand dient vooral om je Arduino te beschermen, en is alleen nodig als je luidspreker veel stroom kan trekken.


Opstelling met Arduino nano en luidspreker

In de sketch zie je dat ik de morsecodes in het array Code heb gezet. Morse maakt geen onderscheid tussen hoofd- en kleine letters, daarom zet ik de Zin eerst om in hoofdletters. Het array begint bij de komma en eindigt bij de Z. Dit heeft grote voordelen: de ASCII-code van de letter die je wilt coderen komt overeen met een plaats in het array, maar dan verschoven. De verschuiving zie je in de sketch terug in de instructie int index = Zin[l] - ',';. Hierin is Zin[l] een letter uit de Zin en ',' het eerste karakter waar in het array de Morse code voor is gegeven. In C++ mag je zoiets doen; in werkelijkheid worden de bijbehorende ASCII waarden van elkaar afgetrokken.
De codes zijn de officiële internationale Morse codes en ook de pauzes voldoen aan de standaard (tenzij ik me vergist heb). Symbolen waarvoor wel een code bestaat, maar die niet wordt aanbevolen in de standaard heb ik weggelaten (hieronder vallen o.a. het uitroepteken en de puntkomma). Er zijn enkele codes ik opzettelijk uit de codelijst heb gehouden, om niet teveel onnodige plaatsen te moeten reserveren. Die codes worden rechtstreeks verzonden. Er zijn nog een aantal codes die niet met een teken overeenkomen, zoals stuursymbolen en SOS. Je mag SOS - als het bedoeld is als alarm - namelijk niet maken met losse letters. Als je het verschil wilt horen type dan eerst alleen SOS in en daarna SOS voorafgegaan door een spatie.


De visuele uitvoer van het programma, met commentaar in rood

Van de stuursymbolen heb ik alleen "start transmissie" meegenomen. Je zou ook nog "einde transmissie" kunnen zenden, maar ik weet natuurlijk niet wanneer jij stopt met uitzenden...
Als je deze Morse generator ook wilt maken: onderstaand programma niet overtypen, maar selecteren, kopiëren en in de Arduino IDE plakken!
#define luid 2   // de pin waar de luidspreker op zit
#define duur 60  // 60 is zo ongeveer de minimum tijd
#define freq 700 // is niet voorgeschreven, kies tussen 500 en 1000

void setup() {
  pinMode(luid, OUTPUT);
  Serial.begin(9600); 
}

String Start = "-.-.-";
String Code[] = { // * = geen Morse code voor dit symbool
  /* , . / */ "--..--", "-....-", ".-.-.-", "-..-.",
  /* 0 t/m 9 */ "-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----.",
  /* : ; < = > ? @ */ "---...", "* ;", "* <", "* =", "* >", "..--..", ".--.-.",
  /* A t/m G */ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
  /* H t/m Q */ "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-",
  /* R t/m Z */ ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."
};

void dit() { // een punt, daarna 1 punt pauze
  // doe dit zo, omdat tone(UIT, freq, duur) niet goed werkt
  tone(luid, freq); delay(duur); noTone(luid); delay(duur);
}

void dah() { // een streep, daarna 1 punt pauze
  tone(luid, freq); delay(3 * duur); noTone(luid); delay(duur);
}

void spatie() { // stilte na elk woord (in totaal 7 x duur)
  delay(6 * duur);
}

void Zend(String Letter) {
  for (int l = 0; l < Letter.length(); l++)
    if (Letter[l] == '.') {
      dit();
      Serial.print('.');
    }
    else {
      dah();
      Serial.print('-');
    }
  delay(2 * duur); // stilte na elke letter (in totaal 3 x duur)
  Serial.print(' ');
}

bool EersteKeer = true;
void loop() {
  while (!Serial.available()); // Wacht tot er iets verzonden is ...
  String Zin = Serial.readString();
  while (Zin[Zin.length() - 1] == '\n' || Zin[Zin.length() - 1] == '\r') {
    Zin.remove(Zin.length() - 1); // Haal zo nodig LF en CR weg aan eind van de zin
  } 
  Zin.toUpperCase();
  if (EersteKeer) {
    Serial.print("<start> ");
    Zend(Start);
    spatie();
    Serial.println();
    EersteKeer = false;
  }
  Serial.println(Zin);
  if (Zin == "SOS") { // Dit wordt NIET als losse letter verzonden!
    dit(); dit(); dit(); dah(); dah(); dah(); dit(); dit(); dit(); spatie();
    return;
  }
  for (int l = 0; l < Zin.length(); l++) {
    if (Zin[l] == ' ') {
      spatie();
      Serial.print(' ');
      continue;
    }
    if (Zin[l] == '"') {
      Zend(".-..-.");
      continue;
    }
    if (Zin[l] == '\'') {
      Zend(".----.");
      continue;
    }
    int index = Zin[l] - ',';
    if ( (index < 0) || (index > 'Z' - ',') ) {
      Serial.println(); Serial.print("Geen code of ongeldig symbool: "); Serial.println(String(Zin[l]));
    }
    else if (Code[index].charAt(0) == '*') {
      Serial.println(); Serial.print("Geen code voor symbool: "); Serial.println(String(Zin[l]));
    }
    else Zend(Code[index]);
  }
  Serial.println();
}
Tip: als je het programma in de Arduino hebt geladen, kun je elk terminal programma gebruiken dat je tot je beschikking hebt. Dit kan ook op een andere computer zijn. Het maakt niet uit hoe je het einde van de regels instelt (niets, CR en/of LF). Hieronder zie je een voorbeeld.

De uitvoer van het programma Hype! Terminal