Morse code is a fun way to communicate if you’re into secret messages. However, the nature of Morse code makes it excellent for writing and generating messages using the Arduino. In this guide, we’ll show you how to create a device that can turn the text you type into audible Morse code messages automatically.
Morse code was developed in the 19th century to send messages over long distances using very simple sequences of long and short pulses, commonly referred to as dots and dashes. By encoding letters of the alphabet into combinations of dots and dashes, messages can be conveyed with only a single electric or audible signal.
To show how this works, we’ll use a simple buzzer to convert text into an audible Morse code signal. By using the serial monitor in the Arduino IDE, you’ll be able to type a message on your computer, and the Arduino will translate it into tones in Morse code. Notably, with very little modification, this same method could be used to convey the message through an LED, since both devices can be rapidly turned on and off.
You won’t need much to make this project work in terms of hardware. However, if you’re brand new to coding, you might want to take this project in chunks. It’s not very complex code, but you’ll need to understand how arrays and switch cases work. Keep that in mind going forward.
As for the physical components, here’s what you’ll need:
Arduino Uno | ![]() |
A breadboard | ![]() |
Arduino IDE | ![]() |
A buzzer | ![]() |
You’ll also need a few standard wires!
Most starter electronics kits come with all the pieces we’ve listed above, so if you have a kit, take a look through what you already have when starting this project.
The wiring for this project couldn’t be simpler. Most of the magic in this project comes from the code, so we’ll get the wiring out of the way first. To hook up this project, connect the power wire from the buzzer to pin 8. Then connect the ground wire from the buzzer to GND.
That’s it! This one’s simple. Now, let’s move on to the code.
The code for this project may be longer than you’re used to seeing, owing mostly to encoding the Morse code pulses. Don’t be too worried, though. The code is extremely basic once you break it down to its component parts. First, here’s the full code that you can upload directly to your Arduino.
int buzzer = 8; // Assign buzzer to pin 8
int note = 1000; // Set the pitch for the buzzer tone
int timeUnit = 100; // This variable will be used to measure dots, dashes, breaks, and pauses
char input; // Variable to save the input to
void setup () {
Serial.begin(9600);//for the connect with the boared
}
void loop () {
if (Serial.available()) {
input = Serial.read();//read the input
if (input == ‘a’ || input == ‘A’) {lA();}//if the input is a or A go to function lA
if (input == ‘b’ || input == ‘B’) {lB();}//same but with b letter
if (input == ‘c’ || input == ‘C’) {lC();}
if (input == ‘d’ || input == ‘D’) {lD();}
if (input == ‘e’ || input == ‘E’) {lE();}
if (input == ‘f’ || input == ‘F’) {lF();}
if (input == ‘g’ || input == ‘G’) {lG();}
if (input == ‘h’ || input == ‘H’) {lH();}
if (input == ‘i’ || input == ‘I’) {lI();}
if (input == ‘j’ || input == ‘J’) {lJ();}
if (input == ‘k’ || input == ‘K’) {lK();}
if (input == ‘l’ || input == ‘L’) {lL();}
if (input == ‘m’ || input == ‘M’) {lM();}
if (input == ‘n’ || input == ‘N’) {lN();}
if (input == ‘o’ || input == ‘O’) {lO();}
if (input == ‘p’ || input == ‘P’) {lP();}
if (input == ‘q’ || input == ‘Q’) {lQ();}
if (input == ‘r’ || input == ‘R’) {lR();}
if (input == ‘s’ || input == ‘S’) {lS();}
if (input == ‘t’ || input == ‘T’) {lT();}
if (input == ‘u’ || input == ‘U’) {lU();}
if (input == ‘v’ || input == ‘V’) {lV();}
if (input == ‘w’ || input == ‘W’) {lW();}
if (input == ‘x’ || input == ‘X’) {lX();}
if (input == ‘y’ || input == ‘Y’) {lY();}
if (input == ‘z’ || input == ‘Z’) {lZ();}
if (input == ‘ ‘) {wordPause();}
Serial.println (input);
}
}
//Letter functions
void lA () {dot();dash();letterPause();}//letter A in morse code!
void lB () {dash();dot();dot();dot();letterPause();}//same for B
void lC () {dash();dot();dash();dot();letterPause();}
void lD () {dash();dot();dot();letterPause();}
void lE () {dot();letterPause();}
void lF () {dot();dot();dash();dot();letterPause();}
void lG () {dash();dash();dot();letterPause();}
void lH () {dot();dot();dot();dot();letterPause();}
void lI () {dot();dot();letterPause();}
void lJ () {dot();dash();dash();dash();letterPause();}
void lK () {dash();dot();dash();letterPause();}
void lL () {dot();dash();dot();dot();letterPause();}
void lM () {dash();dash();letterPause();}
void lN () {dash();dot();letterPause();}
void lO () {dash();dash();dash();letterPause();}
void lP () {dot();dash();dash();dot();letterPause();}
void lQ () {dash();dash();dot();dash();letterPause();}
void lR () {dot();dash();dot();letterPause();}
void lS () {dot();dot();dot();letterPause();}
void lT () {dash();letterPause();}
void lU () {dot();dot();dash();letterPause();}
void lV () {dot();dot();dot();dash();letterPause();}
void lW () {dot();dash();dash();letterPause();}
void lX () {dash();dot();dot();dash();letterPause();}
void lY () {dash();dot();dash();dash();letterPause();}
void lZ () {dash();dash();dot();dot();letterPause();}
void dot() //Emit sound for 100 milliseconds
{
tone(buzzer, note, timeUnit);
delay(timeUnit * 2);
}
void dash() //Emit sound for 300 milliseconds
{
tone(buzzer, note, timeUnit * 3);
delay(timeUnit * 4);
}
void letterPause() //Delay between letters for 300 milliseconds
{
delay(timeUnit * 3);
}
void wordPause()
{
delay (timeUnit * 7);
}
Now, let’s go through the important parts of this code, starting with some basic setup.
int buzzer = 8; // Assign buzzer to pin 8
int note = 1000; // Set the pitch for the buzzer tone
int timeUnit = 100; // This variable will be used to measure dots, dashes, breaks, and pauses
char input; // Variable to save the input to
void setup () {
Serial.begin(9600);//for the connect with the boared
}
In this section, we’ll initialize the buzzer on pin 8, and identify a tone for the buzzer to make in the variable note. Next, we’ll identify a duration in the timeUnit variable of 100 milliseconds. This will be used to determine all future durations. For example, a dot is one time unit, while a dash is three time units. We’ll also use this to set durations for breaks between each symbol of a letter (one unit), each letter as a whole (three units), and each word (seven units).
In the setup() section, we’ll also initialize the serial monitor, which will allow you to type messages that will be sent to the Arduino. This will require your project to remain connected via USB to your computer.
void loop () {
if (Serial.available()) {
input = Serial.read();//read the input
if (input == ‘a’ || input == ‘A’) {lA();}//if the input is a or A go to function lA
if (input == ‘b’ || input == ‘B’) {lB();}//same but with b letter
if (input == ‘c’ || input == ‘C’) {lC();}
if (input == ‘d’ || input == ‘D’) {lD();}
if (input == ‘e’ || input == ‘E’) {lE();}
if (input == ‘f’ || input == ‘F’) {lF();}
if (input == ‘g’ || input == ‘G’) {lG();}
if (input == ‘h’ || input == ‘H’) {lH();}
if (input == ‘i’ || input == ‘I’) {lI();}
if (input == ‘j’ || input == ‘J’) {lJ();}
if (input == ‘k’ || input == ‘K’) {lK();}
if (input == ‘l’ || input == ‘L’) {lL();}
if (input == ‘m’ || input == ‘M’) {lM();}
if (input == ‘n’ || input == ‘N’) {lN();}
if (input == ‘o’ || input == ‘O’) {lO();}
if (input == ‘p’ || input == ‘P’) {lP();}
if (input == ‘q’ || input == ‘Q’) {lQ();}
if (input == ‘r’ || input == ‘R’) {lR();}
if (input == ‘s’ || input == ‘S’) {lS();}
if (input == ‘t’ || input == ‘T’) {lT();}
if (input == ‘u’ || input == ‘U’) {lU();}
if (input == ‘v’ || input == ‘V’) {lV();}
if (input == ‘w’ || input == ‘W’) {lW();}
if (input == ‘x’ || input == ‘X’) {lX();}
if (input == ‘y’ || input == ‘Y’) {lY();}
if (input == ‘z’ || input == ‘Z’) {lZ();}
if (input == ‘ ‘) {wordPause();}//the space
Serial.println (input);//print the latter saved in the input var
}
}
In this part of the code, we’ll read the letters coming into the serial monitor one at a time and immediately run the corresponding function down below. Each line checks to see if the input letter is either the capital or lowercase version of a letter (there is no difference in case in Morse code), or if there is a blank space in the line. Depending on which letter it receives, the code will then call the appropriate function for that letter.
//Letter functions
void lA () {dot();dash();letterPause();}//letter A in morse code!
void lB () {dash();dot();dot();dot();letterPause();}//same for B
void lC () {dash();dot();dash();dot();letterPause();}
void lD () {dash();dot();dot();letterPause();}
void lE () {dot();letterPause();}
void lF () {dot();dot();dash();dot();letterPause();}
void lG () {dash();dash();dot();letterPause();}
void lH () {dot();dot();dot();dot();letterPause();}
void lI () {dot();dot();letterPause();}
void lJ () {dot();dash();dash();dash();letterPause();}
void lK () {dash();dot();dash();letterPause();}
void lL () {dot();dash();dot();dot();letterPause();}
void lM () {dash();dash();letterPause();}
void lN () {dash();dot();letterPause();}
void lO () {dash();dash();dash();letterPause();}
void lP () {dot();dash();dash();dot();letterPause();}
void lQ () {dash();dash();dot();dash();letterPause();}
void lR () {dot();dash();dot();letterPause();}
void lS () {dot();dot();dot();letterPause();}
void lT () {dash();letterPause();}
void lU () {dot();dot();dash();letterPause();}
void lV () {dot();dot();dot();dash();letterPause();}
void lW () {dot();dash();dash();letterPause();}
void lX () {dash();dot();dot();dash();letterPause();}
void lY () {dash();dot();dash();dash();letterPause();}
void lZ () {dash();dash();dot();dot();letterPause();}
Outside of the main loop() section, we’ll define the functions for each letter. Here, we’ll need to add the pattern for each letter, according to the International Morse Code standard. To make things simple, we’ll use the dot(), dash(), and letterPause() functions to create the pattern, and define what those functions do a bit later on. Each command is separated by a semicolon, but we’re keeping each function on a single line for clarity’s sake.
void dot() //Emit sound for 100 milliseconds
{
tone(buzzer, note, timeUnit);
delay(timeUnit * 2);
}
void dash() //Emit sound for 300 milliseconds
{
tone(buzzer, note, timeUnit * 3);
delay(timeUnit * 4);
}
void letterPause() //Delay between letters for 300 milliseconds
{
delay(timeUnit * 3);
}
void wordPause()
{
delay (timeUnit * 7);
}
Next, we’ll define the functions we used in the previous set. First, dot() will play the tone we specified up top for one time unit. Even though the tone() command will play the tone for one time unit, it can get preempted if it’s called again too soon, so we’ll add a delay() command that will prevent the sketch from moving to the next line for two time units. Why two time units? This way it will allow the tone() command to finish, and then wait for a second time unit as a pause between elements.
For example, in the letter ‘E’, which consists of one dot, you should hear the tone for 100 milliseconds, then silence for 100 milliseconds. In the letter ‘S’, which consists of three dots, you’ll hear tone, silence, tone, silence, tone, and silence, for 100 milliseconds each.
We do a similar thing in the dash() command, but this time we play the tone for three time units (or 300 milliseconds) and then pause the sketch for four time units. Three units to allow the tone to finish playing, and a fourth unit of silence before moving on to the next element.
We’ll also define the letterPause() and wordPause() functions. The former consists entirely of a three time unit delay, which should place 300 milliseconds of silence between each letter of your message, allowing you to distinguish one letter from another. The latter is the longest delay, of seven time units or 700 milliseconds. This will play any time the monitor reads a space, indicating that a new word has started.
Once your sketch is uploaded and your Arduino is running, open the Serial Monitor under the Tools section of your IDE. Type some words and press enter and you should hear the corresponding Morse code tones come out of the buzzer. If you want to tweak the sketch yourself, try adding new functions for numbers.