Bouw een Morsecode-zender/ontvanger met Arduino UNO – Praktijk communicatieproject
Onderdelen en benodigdheden
| Arduino UNO | × | 2 |
| LED (generiek) | × | 2 |
| Fotoweerstand (LDR) | × | 1 |
| Buzzer | × | 1 |
Over dit project

Ik gaf een les over Communicatiesystemen en wilde dat mijn studenten leerden door te visualiseren en te ervaren. We deden een paar experimenten, begonnen bij de Steenijd, om de noodzaak van communicatie te begrijpen en wat bedoeld wordt met taal en grammatica, wat betrouwbare communicatie is, signalen, berichten, etc.
Terwijl we dit bespraken, gaven we aan dat we onze eigen taal voor visuele communicatie wilden definiëren, en daar bedachten we de Morsecode!
Het was duidelijk dat Morsecode leren een lastige klus zou zijn voor iedereen, dus we hadden een machine nodig. We besloten een klein programma te schrijven dat een gegeven string kan coderen naar equivalente Morsecode en een decoder die de string teruggeeft voor een gegeven Morsecode-stroom.
Voor de Morsecode van een karakter gebruikte ik de volgende tabel:

Om Morsecode te vertalen naar een equivalent karakter, kan het georganiseerd worden in een binaire boom. Met elke 'punt' naar de linkerkind en elke 'streep' naar het rechterkind.

Dit maakte het mogelijk om snel een string te coderen naar Morsecode en vice versa.
Eerst schreef ik een proof-of-concept in C#:


Uitvoer
Hello World .... . .-.. .-.. --- .-- --- .-. .-.. -..
Omdat communicatie tussen twee partijen moet plaatsvinden, wilde ik deze gecodeerde Morsecode naar een ander apparaat zenden en daar decoderen.
Ik gebruikte twee Arduino-borden om een zender en een ontvanger te bouwen.
Op het zenderbord sluitte ik een laserdiod van Keyes aan. Gemakkelijk verkrijgbaar en goedkoop. Voor de afstand die ik gebruik, was 3V voldoende, maar met 5V ziet de laser minstens 200m ver.

Mijn zender is verbonden met een computer via seriële interface (USB). Het leest tekens van de seriële poort, vindt de equivalente Morsecode en genereert een 'punt' en 'streep' lichtstroom.
Ik creëerde een 50ms puls, waarbij elke 'punt' een 50ms lichtstraal is en een 'streep' 150ms. Elke 'punt' en 'streep' worden gescheiden door 50ms stilte. Elk karakter wordt gevolgd door nog eens 100ms stilte.
Zendercode
const char* MorseTable[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
// spatie, !, ", #, $, %, &, '
NULL, "-.-.--", ".-..-.", NULL, NULL, NULL, NULL, ".----.", // ( ) * + , - . /
"-.--.", "-.--.-", NULL, ".-.-.", "--..--", "-....-", ".-.-.-", "-..-.", // 0-7
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", // 8-9 : ; < = > ?
"---..", "----.", "---...", "-.-.-.", NULL, "-...-", NULL, "..--..", // @ A-G
".--.-.", ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", // H-O
"....", "..", ".---", "-.-", ".-..", "--", "-.", "---", // P-W
".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", // X-Y-Z [\]^_
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, "..--.-", // ' a-g
NULL, ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", // h-o
"....", "..", ".---", "-.-", ".-..", "--", "-.", "---", // p-w
".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", // x-z {|}~ DEL
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, NULL
};
int dotLength = 50;
int dashLength = dotLength * 3;
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
}
void loop() {
char ch;
if (Serial.available()) {
ch = Serial.read();
flashDashDot(MorseTable[ch]);
delay(dotLength * 2);
}
}
void flashDashDot(const char * morseCode) {
int i = 0;
while (morseCode[i] != 0) {
if (morseCode[i] == '.') {
dot();
} else if (morseCode[i] == '-') {
dash();
}
i++;
}
}
void dot() {
digitalWrite(13, HIGH);
delay(dotLength);
digitalWrite(13, LOW);
delay(dotLength);
}
void dash() {
digitalWrite(13, HIGH);
delay(dashLength);
digitalWrite(13, LOW);
delay(dotLength);
}
Op het ontvangerbord sluitte ik een LDR, een piezo buzzer en een LED aan. LED en buzzer zijn redundant, ze geven alleen audio- en visuele feedback en het voelt leuk om te zien dat het werkt ;)

Mijn ontvanger is ook verbonden met een computer om de resulterende stringkarakters te lezen.
Op het apparaat, in een lus, lees ik of er licht valt op de LDR die verbonden is met analoog poort 4. Ik beschermde het tegen omgevingslicht door het in een lange buis te encapsuleren. Wanneer licht van mijn laserdiod de LDR raakt, gaat de waarde hoog en zet ik de speaker en LED aan. Wanneer het laag gaat, zet ik ze uit.
Ik bereken de lengte van de lichtstraal om te bepalen of het een 'punt' of 'streep' was. Rekening houdend met alle vertragingen, kwam dit op ~400 cycli voor een puls op mijn 16MHz Atmel-processor.
Ontvangercode
// Morsecode Binaire Boom
// Linkerkind op 2n+1
// Rechterkind op 2n+2
// <.|->
// .E -T
// .I -A .N -M
const char MorseTree[] = {
'\0','E','T','I','A','N','M','S','U','R','W','D','K','G','O',
'H','V','F','U','L','A','P','J','B','X','C','Y','Z','Q','\0','\0',
'5','4','\0','3','\0','\0','\0','2','\0','\0','+','\0','\0','\0','\0','1',
'6','=','/','\0','\0','\0','(','\0','7','\0','\0','\0','8','\0','9','0',
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','?','_','\0','\0','\0',
'\0','"','\0','\0','.','\0','\0','\0','\0','@','\0','\0','\0','\0','\0','\0',
'-','\0','\0','\0','\0','\0','\0','\0','\0',';','!','\0',')','\0','\0','\0',
'\0','\0',',','\0','\0','\0','\0',':','\0','\0','\0','\0','\0','\0','\0'
};
int val = 0; // Variabele om lichtwaarde van LDR op te slaan
int ctrHigh = 0;
int ctrLow = 0;
int codePtr = 0;
int dotLen = 400;
void setup() {
Serial.begin(9600);
Serial.print(codePtr);
}
void loop() {
val = analogRead(4);
if (val >= 10) {
ctrHigh++;
ctrLow = 0;
digitalWrite(13, HIGH);
tone(9, 1000);
} else {
ctrLow++;
if ((ctrHigh >= dotLen) && (ctrHigh < dotLen * 2)) {
Serial.print(".");
codePtr = (2 * codePtr) + 1;
} else if (ctrHigh >= dotLen * 2) {
Serial.print("-");
codePtr = (2 * codePtr) + 2;
} else {
if (ctrLow == dotLen * 2) {
Serial.print(MorseTree[codePtr]);
codePtr = 0;
}
}
ctrHigh = 0;
digitalWrite(13, LOW);
noTone(9);
}
}
Uitvoer bij ontvanger
....H.E.-..L.-..L---O.--W---O.-.R.-..L-..D-.-.--!
Code
Zender
const char* MorseTable[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
// spatie, !, ", #, $, %, &, '
NULL, "-.-.--", ".-..-.", NULL, NULL, NULL, NULL, ".----.", // ( ) * + , - . /
"-.--.", "-.--.-", NULL, ".-.-.", "--..--", "-....-", ".-.-.-", "-..-.", // 0-7
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", // 8-9 : ; < = > ?
"---..", "----.", "---...", "-.-.-.", NULL, "-...-", NULL, "..--..", // @ A-G
".--.-.", ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", // H-O
"....", "..", ".---", "-.-", ".-..", "--", "-.", "---", // P-W
".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", // X-Y-Z [\]^_
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, "..--.-", // ' a-g
NULL, ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", // h-o
"....", "..", ".---", "-.-", ".-..", "--", "-.", "---", // p-w
".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", // x-z {|}~ DEL
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, NULL
};
int dotLength = 50;
int dashLength = dotLength * 3;
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
}
void loop() {
char ch;
if (Serial.available()) {
ch = Serial.read();
flashDashDot(MorseTable[ch]);
delay(dotLength * 2);
}
}
void flashDashDot(const char * morseCode) {
int i = 0;
while (morseCode[i] != 0) {
if (morseCode[i] == '.') {
dot();
} else if (morseCode[i] == '-') {
dash();
}
i++;
}
}
void dot() {
digitalWrite(13, HIGH);
delay(dotLength);
digitalWrite(13, LOW);
delay(dotLength);
}
void dash() {
digitalWrite(13, HIGH);
delay(dashLength);
digitalWrite(13, LOW);
delay(dotLength);
}
Ontvanger
// Morsecode Binaire Boom
// Linkerkind op 2n+1
// Rechterkind op 2n+2
// <.|->
// .E -T
// .I -A .N -M
const char MorseTree[] = {
'\0','E','T','I','A','N','M','S','U','R','W','D','K','G','O',
'H','V','F','U','L','A','P','J','B','X','C','Y','Z','Q','\0','\0',
'5','4','\0','3','\0','\0','\0','2','\0','\0','+','\0','\0','\0','\0','1',
'6','=','/','\0','\0','\0','(','\0','7','\0','\0','\0','8','\0','9','0',
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','?','_','\0','\0','\0',
'\0','"','\0','\0','.','\0','\0','\0','\0','@','\0','\0','\0','\0','\0','\0',
'-','\0','\0','\0','\0','\0','\0','\0','\0',';','!','\0',')','\0','\0','\0',
'\0','\0',',','\0','\0','\0','\0',':','\0','\0','\0','\0','\0','\0','\0'
};
int val = 0;
int ctrHigh = 0;
int ctrLow = 0;
int codePtr = 0;
int dotLen = 400;
void setup() {
Serial.begin(9600);
Serial.print(codePtr);
}
void loop() {
val = analogRead(4);
if (val >= 10) {
ctrHigh++;
ctrLow = 0;
digitalWrite(13, HIGH);
tone(9, 1000);
} else {
ctrLow++;
if ((ctrHigh >= dotLen) && (ctrHigh < dotLen * 2)) {
Serial.print(".");
codePtr = (2 * codePtr) + 1;
} else if (ctrHigh >= dotLen * 2) {
Serial.print("-");
codePtr = (2 * codePtr) + 2;
} else {
if (ctrLow == dotLen * 2) {
Serial.print(MorseTree[codePtr]);
codePtr = 0;
}
}
ctrHigh = 0;
digitalWrite(13, LOW);
noTone(9);
}
}
Schema's
Morsecode Ontvanger Schakeling
Morsecode Zender Schakeling
Productieproces
- Streepjescodescanner: Technologie, Ontwerp en Toekomstige Trends
- Een DTMF-decoder bouwen met alleen een Arduino – Geen extra IC's nodig
- SERENA: Een Aangepast Arduino Mega 2560 Alarmsysteem Met TFT LCD Touchscreen
- Morsecode-communicatie met lasermodule – Tweezijdig Arduino-project
- Morse code en dubbele chantage: huidig landschap van geavanceerde malware-dreigingen
- Kiezen Tussen Data Matrix En QR Codes: Wat Is Best Voor Uw Bedrijf
- CNC G-codes uitgelegd: essentiële gids voor CNC programmeurs
- Complete Fanuc G-code Naslaggids – Alle Commando's Uitgelegd
- Uitgebreide gids voor CNC G-codes voor Fanuc-besturingen
- ADC Code Error Rates Beheersen: Wat U Moet Weten
- C++ comments: best practices voor leesbare, onderhoudbare code