Getting started with Arduino: morse keyer


Close-up of the prototype keyer.

Using a paddle to operate in morse code is very convenient. But paddles don’t create dots and dashes on their own, so you need some electronics, called a “keyer”. You may use the build-in keyer of your radio, but most of them lack of functionality. You may buy a keyer at your local ham store, but these are rather expensive. So why not build your own keyer? It’s fun to do, and you learn new things. The Arduino prototyping board allows you to build the most advanced and personalized keyer that you have in mind! This article gives you a decent start for such a keyer, by implementing the basic functionality and learn a bit about the Arduino platform if you’re not familiar with this board yet.

About the Arduino


The Arduino prototype board, this is the “Uno” version. Throughout the years several versions have been released, but all are code and pin compatible.

The Arduino is one of the easiest prototyping platforms available nowadays. You can easily attach buttons, sensors, lights, displays, relays and motors and control them all with a simple program. You write the program using the Arduino IDE software (available for Linux, Windows and Mac) and transfer it to your Arduino board using a USB-cable. Arduino boards are sold at most electronics shops and will cost about 20-25 euros / 25-30 USD. You may consider buying a starter kit, containing an Arduino board, a breadboard, and handful of parts (LED’s, switches, sensors, motor, servo, etc), wires, and  a guide learning you the basics. Visit the Arduino website for more information.

About paddles

Paddles come in different shapes and sizes. The keyer discussed in this article is meant for “twin paddles” or “iambic paddles”. Such a paddle has 2 levers, one for sending dots and one for the dashes. The “iambic” means that, when squeezing both levers, the keyer will translate this to a repetitive series of dot-dash-dot-dash-dot-dash-etc…


Two examples of a paddle: the high-end Begali “Pearl” and the more common Bencher “BY-1”.

Actually, if you look at your paddle, it’s just a double switch. Ok, it’s probably the most expensive and specialized switch in your home, but still it’s a switch. Each contact is shortened to a common contact, normally connected to “ground”. The other ends of the switches are connected to the “dot” and “dash” inputs of your keyer.

The keyer hardware

It’s time to build the circuit. I suggest that you first build your prototype on a breadboard, at least I did for this article.


The complete circuit, less then 10 components!

The main component of the keyer is (of course) the Arduino board, and we will create the following inputs and outputs:

  • 2 Digital inputs for the paddle (dot and dash contact).
  • 1 Digital output to the key jack of your transmitter.
  • 1 Digital output to sound a sidetone buzzer.
  • 1 Analog input to read the speed from a potmeter.

Down here I’ll discuss these.

The paddle contacts have to be translated to a digital “1” or “0”. Most paddles pull connect the contacts to ground, so when a lever is pressed it is a digital “0”. So when not pressed, a “1” should appear at the Arduino’s input, so we add a 10k pull-up resistor to each contact, connecting it to +5V (operating voltage of the Arduino). The contacts can now be connected to the Arduino, using the digital I/O pins labelled “D2” and “D3”. The arduino also provides pins labelled “GND” (for the ground connection of the keyer) and “5V” (for the pull-up resistors).

The software processes the inputs from the keyer and produces the dots and dashes. Therefore it keys pin “D13”. When high (+5V), the key is pressed. We attach a transistor (almost any common NPN type transistor will do) to this pin. When ping D13 is high, the transistor pulls the key input of the connected transmitter to ground, as if a straight key was connected.

During key down, the software will generate a square wave on pin “D12”, allowing you to attach a small buzzer. You could also shape the signal a bit using an RC circuit and feed a LM386 based audio amplifier, allowing you to attach a small speaker or headphones, but I’ll leave that to you. For now, the buzzer will be fine.

You definitely want to be able to set the keying speed, so therefore we will attach a potmeter. The center pin will be connected to analog input “A0” of the Arduino, while the other 2 pins are connected to GND and 5V.

The hardware is finished now, so let’s go on to the software part.

The keyer software

Once installed the Arduino IDE software, you can simply start it and begin typing your code. You can download the keyer code to save you some time. If you have never programmed an Arduino before, you might first take a look at some examples, starting with this one: File → Examples → Basics → Blink. In the text down here I have added links to the online Arduino Language Reference.

Let’s start coding. The keyer software consists of 4 parts:

  • Declarations.
  • The “setup” function.
  • The main routine, called “loop”.
  • The custom “keyAndBeep” function.

We start with the declarations. We define constants for all pins used, and also introduce a variable (integer) to store the value of the potmeter, called “speed”:

#define P_DOT    2   // Connects to the dot lever of the paddle
#define P_DASH   3   // Connects to the dash lever of the paddle
#define P_AUDIO 12   // Audio output
#define P_CW    13   // Output of the keyer, connect to your radio
#define P_SPEED A0   // Attached to center pin of potmeter, allows you
                     // to set the keying speed.

int speed;

The next step is the setup routine. The Arduino runs this code only once, at start/boot time. We use this to define which pin is an input or an output, using the pinMode statement. We also ensure that there is no key output, by setting the output pin to LOW using the digitalWrite statement:

// Initializing the Arduino
void setup()
  pinMode(P_DOT, INPUT);
  pinMode(P_DASH, INPUT); 
  pinMode(P_AUDIO, OUTPUT);
  pinMode(P_CW, OUTPUT);
  digitalWrite(P_CW, LOW);      // Start with key up

Notice that we used the contants we just declared, not the pin numbers itself.

Let’s go on to the main routine, called loop. This one repeats itself continuously, so when the last statement is executed, it returns to the start of this loop again. The first statement reads the value of the potmeter using the analogRead statement, and stores it in our “speed” variable. Next it reads the input pin connected to the “dot” contact of the paddle using digitalRead. If the input is low, it calls a custom function called “keyAndBeep”, which we will discuss in a moment. Then there’s a short delay, since we want a bit of time after sending a dot. If we would skip this delay, there would be no separation between succeeding dots and/or dashes. Finally, we do the same for the “dash” contact, but now the key has to be down 3x longer, hence the “speed*3” argument. Now the routine returns to the first statement, reading the value of the potmeter again, checking the dot lever, etc…

// Main routine
void loop()
  speed = analogRead(P_SPEED)/2; // Read the keying speed from potmeter
  if(!digitalRead(P_DOT))        // If the dot lever is presssed..
    keyAndBeep(speed);           // ... send a dot at the given speed
    delay(speed);                //     and wait before sending next
  if(!digitalRead(P_DASH))       // If the dash lever is pressed...
    keyAndBeep(speed*3);         // ... send a dash at the given speed
    delay(speed);                //     and wait before sending next

The final part of the code does the actual keying and sounding. We call this function “keyAndBeep”. It starts with keying the output, setting the P_CW pin to “HIGH” (5V). This will drive the transistor to key the transmitter. Then a for loop runs for a while, all statements within this loop are repeated lots of times, depending on the value of the “speed” variable (so the setting of the potmeter). Each iteration the audio output is set to HIGH, then we wait 1 millisecond, and then set the output to LOW again and wait another millisecond. This results in a square wave of about 500 Hz (roughly). When the loop is ended, the key output is set to “LOW” again and the connected transmitter will stop.

// Key the transmitter and sound a beep
void keyAndBeep(int speed)
  digitalWrite(P_CW, HIGH);            // Key down
  for (int i=0; i < (speed/2); i++)    // Beep loop
    digitalWrite(P_AUDIO, HIGH);
    digitalWrite(P_AUDIO, LOW);
  digitalWrite(P_CW, LOW);             // Key up

In case you missed it… you can download the complete code here: pa3hcm_simple_iambic_keyer.txt


Connect the Arduino board to your PC using a standard USB A-B cable. Then upload the code using the Arduino IDE software.

Now connect the Arduino to your PC and upload the code. Once uploaded the keyer will come to live immediately, since it’s powered by USB now. You can simply remove the USB cable now and power the Arduino by attaching a 9V battery or a 9-15 volts DC adapter.

Next steps

Congratulations, You’ve just finished the prototype of your keyer! But this is just a very basic keyer. Here are some ideas to extend/personalize your keyer:

  • Add one ore more buttons (use pull-up resistors, like done with the paddle buttons) to transmit standard texts, like your callsign or a CQ message.
  • Add an LCD display showing the current speed. It’s easier than you think! For an example just go to the menu of the Arduino IDE software and click: File → Examples → LiquidCrystal → HelloWorld.
  • Create a memory keyer, the Arduino has an internal EEPROM where you can store your messages.
  • Replace the buzzer by a lowpass filter (to shape the square wave) and a LM386 based audio amplifier, so you can use a small speaker (sounds nicer) and/or headphones.
  • Place a small capacitor (10nF) between each input/output and ground to prevent RF interference.
  • Take a look at other Arduino-based keyers on the web and get inspired.
  • Add an Ethernet Shield to the Arduino and enable keying over your local network.
  • Add a Bluetooth Shield to the Arduino, and create an app to control the keyer using your mobile phone.
  • Build a very small keyer using the Arduino Mini, Micro or Nano.
  • Build your final version in a fancy enclosure!

As always… for any questions or comments regarding this article just place a comment down here, or use the contact form on this website to send me a private email.


Completed prototype of the Arduino morse code keyer.


Bill W7WEL built the keyer on copper clad, therefore he removed the microcontroller chip from the Arduino board, placed it in a socket, and added a clock circuit and power to get the chip running.


Chris ZS1CDG built a slightly different version, which is a touch paddle. He replaced both 10k resistors with 2M ones and uses his fingers as “switches”. He showed the result in this nice video.

This article was also published in:


13 Replies to “Getting started with Arduino: morse keyer”

  1. Robbert van Herksen / PA3BKL

    Hoi Ernest

    Zag je naam voorbij komen in een mailtje van Aeilko van der Wagen (de enige andere ham van Soesterberg, hi)
    Hij schreef dat jij hem geholpen had met de Arduino software voor zijn DDS protect.

    Natuurlijk even gegoogled op jouw call, en kwam op je website (gaaf!!!)
    Ik zag dat je een keyer gemaakt had met een Arduino.

    Ik dus ook, maar ik heb daar de bekende software van K3NG vor gebruikt, en in mijn Arduino Nano gezet (nanokeyer)

    Echt waanzinnige functionaliteit, ik wil er alleen nog een display aanhangen, maar datn past de code niet meer in het geheugen van de nano en zal ik moeten ‘verkassen’ naar een Arduino Mega (2850, die inmiddels een opvolger heeft: de 2860..heeft geen FTDI chip meer)

    Zoek even op nanokeyer, en je ziet vazelf de links naar de keyersoftware.

    73 de Robbert / PA3BKL

    • Ernest Post author

      Hello Robert,
      I know there are more Arduino based keyers available, and yes, K3NG did a really nice job. However, my article is not meant to build your own fancy / sophisticated / feature-rich keyer, but to be part of learning to write your own code for Arduino. Therefore its functionality is very limited, but most people should understand the code. Still, it might add some links to such projects for inspiration.
      Thanks for your comment, always appreciated!
      73, Ernest PA3HCM

  2. Jim Stafford

    Ernest, Very impressive it seems to me. I have been using the Arduino for less than 1 week. I could never set aside the time to learn the basics. Then a week ago, I did … by watching a youtube video. Then I discovered your keyer. It appeared so simple and yet when I loaded in your sketch, it almost worked the first time. I had left off the pull up resistor as I was not sure they were needed. Yes, they are! I almost understand your sketch. Now I’m going to play around with it until I understand it. I was intrigued as I am a very active CW enthusiast! Thank you for posting this project. jim/w4qo

  3. alan

    Thanks for the good job, I was looking for such a thing.
    It has been working at the first time
    73’s from F6FRI

  4. Antti OH3HMU

    The values one gets from analogRead are rather unusable as is even after scaling down by 2. To get a linear speed adjustment from 10 to 35 WPM, you can use e.g.

    speed = 60000 / (map(analogRead(P_SPEED), 0, 1023, 35, 10) * 44);

    to calculate the proper delay. I’ve posted the modified version on my GitHub account.

    • Ernest Post author

      Yes Antti, the speed adjustment is far from perfect, however I tried to keep the code as simple as possible, to allow starters to understand the code. Thanks for your input!

      More information on the map function can be found here:
      I’ve seen others solving this issue by adding series resistors on both sides of the potmeter, which is actually the “physical way” of implementing the map() function 😉
      Antti’s version of the code can be downloaded here:

  5. John Charlton

    Ernest, an excellent simple iambic keyer project and the code is easy to read. It worked first time. I modified the speed setting as per Antti above but modified it a little to read better by defining Maximum_speed_WPM, and Minimum_speed_WPM early on. I used this line;

    speed = 60000 / (map(analogRead(P_SPEED), 0, 1023, Maximum_speed_WPM, Minimum_speed_WPM) * 44)

    This now gives a reasonably linear speed adjustment.

    Thank you for posting this.

    John G3VRF

    • Osvaldo

      Hi John
      I am an enthusiast Arduino and CW. I have little knowledge to program but I like to experiment. You tell me where you put the line in the code created by Ernest?
      Thank you so much
      Osvaldo – LU5XP

  6. Ben Cheikh

    Hallo Ernest
    ik ben al een tijd opzoek naar een Arduino morse keyer zou u mij mischien willen en
    kunnen helpen hoe kom ik aan zo een keyer?
    en wat eventueel de prijs er van?
    ik zit op dit moment in het buitenland maar ik kom binnenkort naar nederland,
    met vriendelijke groet.


Leave a Reply

Your email address will not be published. Required fields are marked *