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 from my GitHub repository 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.

What others did

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.

Tommy NE5TH built it using a breadboard shield.

Maks UT3UEZ used it in a morse trainer/simulator, using an Arduino Mini. He uses a second Arduino Mini to decode the signal again and show the result on a display.

Yuri EW6BN put his version into a small plastic mints box.

Based on this article, Daniel VE7LCG built this very small keyer, including a tiny paddle!

Anthony DU1AU build this neat integrated keyer.

More builders:

This article was also published in:


  1. 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

    • 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. 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. 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. 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.

    • 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. 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

    • 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. 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.

  7. Hi, nice project!
    If you use pinMode(P_DOT, PULLUP_INPUT); you can remove the external pullup resistors and use the internal ones 🙂

    73 de Adrian YO3HJV

      • I applied external pullup resistors, so ‘INPUT’ is ok then. You can skip the external resistors and use the internal pullup resistors instead, but I found that less clear for educational purposes 😉

  8. Nice job!
    Just what I was looking for.
    Made mods to code as suggested above, added a a switch and a couple of lines of code to give iambic or semi auto.
    Simple sideswiper/cootie key gives same effect as vibroplex but without the $$$$ price.
    73 de Dave GM4HPK

  9. Nice job.
    Couldn’t afford the $$$$ price wanted for a vibroplex.
    Added a semi auto option and now able to use homebrew cootie to mimic.
    de Dave GM4HPK

  10. Hi Ernest,

    How to memorize dot after dash in your simple iambic keyer?

    Rubens PY2PVB
    Ilha Solteira SP Brazil

  11. Thanks for such a nice keyer, needed something for my HW9 was able to put a volume and switch control in a very small box. Makes a nice package. Thanks again worked right off.
    Rick WB8ZRQ

  12. Hi, you say you use the blue wire to key the radio. But the radio keyer input needs a complete circuit. So you must be connecting more than just the blue wire? Do you connect the key-input ground to the arduino ground also?

    • Yes, it’s the blue wire and ground for most radios. Some really old radios don’t key to ground, in that case check your manual.

  13. Hello Ernest, best 73.

    Tks a lot your easy arduino keyer project.
    I’m not an expert on arduinos, só I’m trying to update the project to have a simple 1602 LCD. Try severals parts os LCD programs (like Hello World) but nothing works.

    Can you update your project with LCD?

    Many thanks.
    Carlos, CT1GFQ

    • Hello Carlos,
      The goal of this article is to get you started with an Arduino, not to provide a full featured CW keyer. That said, maybe I can tell you how to start:
      In the Arduino software, go to the menu: File –> Examples –> LiquidCrystal. You will find a bunch of examples on how to control your LCD. If your LCD has 16 pins, it’s probably an HD44780 type, the HelloWorld example works for sure. If your LCD has only 4 pins or has a backpack, it’s probably an I2C type, in that case you could use the LiquidCrystal_PCF8574 library. You can install this library via the menu: Sketch –> Include Library –> Manage Libraries. Search for ‘LiquidCrystal_PCF8574’ and hit the ‘Install’ button. Now go back to your Examples, scroll all the way down and you’ll find a new menu ‘LiquidCrystal_PCF8574’, including an example called ‘LiquidCrystal_PCF8574_Test’. Good luck!
      73, Ernest PA3HCM

      • The term ‘reverse’ is normally used to swap levers. Iambic mode B has nothing to do with that, it has anything to do with what to do when both levers are pressed simultaneously. I must say I have never practiced mode B, but I know mode B alternates once more compared to mode A. This will require a bit more code, since then you have to determine if both levers were pressed. I don’t have code readily available to demonstrate this, sorry!

  14. Thanks for sharing this, I just built one. I appreciate how you made the code and circuitry very simple and easy to understand, for instance, you used external pull-up resistors to help visualize how the code and the circuit works!

    73, Anthony DU1AU (Philippines)

  15. Hi, you can delete this two resistors pull up, replace:

    pinMode(P_DOT, INPUT);
    pinMode(P_DASH, INPUT);

    pinMode(P_DOT, INPUT_PULLUP);
    pinMode(P_DASH, INPUT_PULLUP);


    • For educational purposes however I decided to use real pull-up resistors. Both solutions will work, choose whatever you like 😉

  16. Hi Ernest
    built the keyer using a Teensy LC mounted on a piece of printed circuit perf board. Downloaded and installed Arduino ide and Teensyduino. Downloaded the sketch and to my dismay it would not verify or upload. kept getting a “no usb ports found” error. then I discovered that my USB cable was a charge only cable! replaced that and at last,the sketch compiled and uploaded. my issue now is..the dots and dashes are not stable and vary in duration and time. I have no or at the very best limited experience in the world of “duinos” or sketches and am looking for help. Hopefully you or someone who reads this post can help me.

    • Sorry, I don’t have any experience with Teensy LC boards… code is tested on both official Arduino’s and clones from those popular chinese webshops. You might try an Arduino Nano, which has about the same form factor as the Teensy LC. Good luck!

      • the problem was me!
        the wiper of the speed control should be connected to A0. …I had connected it to A6 because thats what these 79 year old eyes saw (or was sure they saw) when I looked at the schematic. once I fixed that error, the keyer works perfectly….the unstable characteristic of the keyer went away..
        by the way. I am using a 3-1/3” 8 ohn loudspeaker with a 50k ohm pot in the hot side of the speaker…works perfectly as a volume for the sidetone.
        another mod I will be doing is adding a spst switch from the collector of the keying transistor to ground to use as a Tune switch.

Leave a Reply