Before radio could carry voices, ships and soldiers talked with flashes of light and beeps, using a code invented by Samuel Morse. Every letter is a short pattern of dots and dashes: S is three dots, O is three dashes, so SOS is · · · – – – · · ·. In this lesson the UNO becomes a signal lamp. Type a message in the Serial Monitor and it flashes and beeps it in Morse code. Then try it the other way round: tap a message on the button, and the UNO works out the letters and prints them. Can a friend read your message from the flashes?
What you'll learn
- How Morse code turns letters into dots and dashes
- How to read text that you type in the Serial Monitor
- How to store a code table in an array of strings
- How to measure how long a button is held with millis()
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- White LED and 1 resistor, 220 Ω (UNO R3 Starter Kit)
- Active buzzer (UNO R3 Starter Kit)
- 1 push button (UNO R3 Starter Kit)
- The printed parts from the 3D printing box below (signal lamp), or the cardboard version described there
- Breadboard and jumper wires (UNO R3 Starter Kit)
Wiring

| From | To |
|---|---|
| UNO pin 9 | 220 Ω resistor, then the LED's long leg |
| LED short leg | GND |
| Active buzzer + (long leg) | UNO pin 8 |
| Active buzzer − | GND |
| Push button, one leg | UNO pin 2 |
| Push button, the diagonally opposite leg | GND |
The sketch
const int LED_PIN = 9, BUZZER_PIN = 8, KEY_PIN = 2;
const int UNIT = 150; // one dot in ms when the UNO sends
const int KEY_UNIT = 200; // one dot in ms when you tap (a bit slower)
// A to Z, then 0 to 9
const char* CODES[36] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----."};
void signal(bool on) {
digitalWrite(LED_PIN, on);
digitalWrite(BUZZER_PIN, on);
}
const char* codeFor(char c) {
c = toupper(c);
if (c >= 'A' && c <= 'Z') return CODES[c - 'A'];
if (c >= '0' && c <= '9') return CODES[26 + c - '0'];
return nullptr; // no code for this character
}
void sendText(String text) {
for (unsigned int i = 0; i < text.length(); i++) {
if (text[i] == ' ') { // space between words: 7 units in total
delay(UNIT * 4);
Serial.print("/ ");
continue;
}
const char* morse = codeFor(text[i]);
if (morse == nullptr) continue;
Serial.print(morse);
Serial.print(' ');
for (int k = 0; morse[k] != 0; k++) {
signal(true);
delay(morse[k] == '.' ? UNIT : UNIT * 3); // a dash is 3 dots long
signal(false);
delay(UNIT); // gap inside a letter
}
delay(UNIT * 2); // gap between letters: 3 units in total
}
Serial.println();
}
char decode(String morse) {
for (int i = 0; i < 36; i++) {
if (morse == CODES[i]) return i < 26 ? 'A' + i : '0' + (i - 26);
}
return '?';
}
String tapped = ""; // dots and dashes of the letter you are tapping
unsigned long pressedAt = 0, releasedAt = 0;
bool wasDown = false, spaceDone = true;
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(KEY_PIN, INPUT_PULLUP);
Serial.println("Type a message and press Enter, or tap on the button.");
}
void loop() {
if (Serial.available()) { // a message from the Serial Monitor
String text = Serial.readStringUntil('\n');
text.trim();
if (text.length() > 0) {
Serial.print(text + ": ");
sendText(text);
}
}
bool down = digitalRead(KEY_PIN) == LOW;
unsigned long now = millis();
if (down && !wasDown) { // just pressed
pressedAt = now;
signal(true);
}
if (!down && wasDown) { // just let go: was it a dot or a dash?
signal(false);
unsigned long held = now - pressedAt;
if (held > 20) { // ignore contact bounce
tapped += held < KEY_UNIT * 2 ? '.' : '-';
releasedAt = now;
spaceDone = false;
}
}
wasDown = down;
if (!down && tapped.length() > 0 && now - releasedAt > KEY_UNIT * 3) { // a pause ends the letter
Serial.print(decode(tapped));
tapped = "";
}
if (!down && !spaceDone && now - releasedAt > KEY_UNIT * 7) { // a long pause ends the word
Serial.print(' ');
spaceDone = true;
}
delay(5);
}
Try it
- Open the Serial Monitor at 9600 baud and set the line ending to Newline.
- Type
SOSand press Enter. The lamp flashes and beeps · · · – – – · · ·, and the Serial Monitor shows the code. - Now tap on the button: a quick tap is a dot, a longer press (about half a second) is a dash. Tap · · · · then · and pause: the Serial Monitor prints HE.
- Send a secret message to a friend across the room. They write down the dots and dashes and use the table below to read it.
The Morse alphabet
| Letter | Code | Letter | Code | Letter | Code |
|---|---|---|---|---|---|
| A | · – | J | · – – – | S | · · · |
| B | – · · · | K | – · – | T | – |
| C | – · – · | L | · – · · | U | · · – |
| D | – · · | M | – – | V | · · · – |
| E | · | N | – · | W | · – – |
| F | · · – · | O | – – – | X | – · · – |
| G | – – · | P | · – – · | Y | – · – – |
| H | · · · · | Q | – – · – | Z | – – · · |
| I | · · | R | · – · | 0–9 | five signs each |
How it works
The whole Morse alphabet lives in one array of 36 strings, from A to Z and then 0 to 9. To find the code for a letter, the sketch subtracts 'A' from it: B minus A is 1, so B is CODES[1]. Computers store letters as numbers, which makes that little trick work.
Morse code is all about timing. A dash lasts three dots, the gap inside a letter lasts one dot, the gap between letters three dots and the gap between words seven. Change UNIT to send faster or slower.
Decoding works the other way round. millis() remembers when the button was pressed and let go. A short press adds a dot to tapped, a long one a dash. When you pause for three units, the letter is finished: the sketch looks it up in the same array and prints it.
Try this next
- Add a second LED in another colour that only lights for dashes.
- Make the UNO send a reply: when you tap a question mark (· · – – · ·), it answers OK.
- Use the passive buzzer and
tone()for a nicer beep.
Troubleshooting
- Nothing happens when you press Enter: set the Serial Monitor's line ending to Newline and the speed to 9600 baud.
-
Every tap is a dash: tap more quickly, or make
KEY_UNITbigger. - Letters come out as ?: leave a clear pause between letters, and keep the gaps inside a letter short.
- A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.