← Projects
electronics beginner 49 min ago ◯ 6 min read

Build a Morse Code (CW) Practice Oscillator and Trainer with Arduino

Build time: 1 weekend
Tools needed: Soldering iron, wire strippers, small screwdriver, multimeter, 3D printer or project box (optional, for enclosure)
Parts List
arduinomorse codecwham radiopractice oscillatorkeyerpiezolearning cw

Learning Morse code (CW, in ham radio shorthand) is still one of the more rewarding skills a new licensee can pick up — CW gets through weak-signal conditions that defeat voice, needs far less bandwidth and power, and remains a living, actively-used mode on the ham bands rather than a historical curiosity. It's also a skill that only comes from repetition: hearing characters at speed until they're recognized instantly rather than decoded letter by letter. A dedicated practice oscillator with a built-in trainer mode is the classic tool for this, and it's a genuinely approachable first Arduino project — a speaker, a key, some timing code, and not much else.

What This Project Does

This build combines two functions in one box: a practice oscillator (key down produces a tone, key up is silent — exactly what you'd use to practice sending with a real key or paddle and get instant audible feedback) and a code trainer (the Arduino generates random characters, plays them as Morse tones at a selectable speed, and displays the correct answer on an LCD so you can check your copy). Both modes share the same tone-generation code, and switching between them is just a matter of whether the Arduino is reading your key input or generating its own character sequence.

Circuit Overview

The core circuit is simple enough to build on a small perfboard in an evening. The key or paddle connects through a 3.5mm jack to a digital input pin, pulled high internally (using the Arduino's built-in pull-up resistor) and grounded when the key contact closes — this is the same wiring convention real ham radio keys and paddles use, so this trainer can double as a genuine practice oscillator for an actual key you already own or plan to buy. The piezo element or small speaker connects to a PWM-capable output pin (used to generate the audio tone, typically in the 600-800Hz range that's comfortable for extended listening and matches common radio sidetone pitch), and for iambic paddle support (where squeezing both paddle contacts alternates dits and dahs automatically) the second paddle contact needs its own input pin and a small amount of extra logic in the keyer code.

ComponentArduino pin (typical)Function Straight key / paddle dit contactD2 (interrupt-capable)Key-down detection Paddle dah contact (if using a paddle)D3 (interrupt-capable)Second paddle contact for iambic keying Piezo/speakerD9 (PWM)Sidetone audio output Speed potentiometerA0 (analog in)WPM (words-per-minute) speed control Mode buttonD4Switch between practice oscillator and trainer modes I2C LCDA4 (SDA), A5 (SCL)Displays trainer characters and current WPM setting

Firmware: Timing Is Everything

Morse code timing follows a standard ratio regardless of speed: a dah is three times the length of a dit, the gap between elements within one character is one dit-length, the gap between characters is three dit-lengths, and the gap between words is seven dit-lengths. WPM speed is calculated from dit length using the standard PARIS timing reference (one dit length in milliseconds = 1200 / WPM), which is the same formula real keyers and commercial CW trainers use, so a speed you set on this trainer corresponds to the same standard "words per minute" figure you'll see referenced in licensing material and on-air conversation about copy speed.

For the trainer mode, store the standard Morse alphabet as a lookup table (each character mapped to its dit/dah pattern as a short string, e.g., "." "-" for the letter A being dit-dah), pick random characters from this table, play each one's pattern as timed tones with the correct inter-element and inter-character spacing, and display the correct character on the LCD either immediately or after a short delay (delayed reveal is more useful for training, since it forces you to commit to an answer before checking). A genuinely useful addition once the basic trainer works is Farnsworth timing — sending individual characters at a faster "character speed" while stretching the spacing between characters and words to give a slower effective "overall speed." This is how most modern CW instruction actually teaches: it trains your ear to recognize each character's true rhythm at full speed from the start, rather than learning a slowed-down, distorted version of the sound that you'd have to unlearn later.

Practice Oscillator Mode

In practice oscillator mode, the firmware does far less: it just reads the key input and gates the tone on and off in real time, with no timing logic of its own beyond debouncing the input pin so contact bounce doesn't produce stuttering tone artifacts. This mode is what you'd use to practice your own sending — fist consistency, rhythm, and paddle technique — with the same subjective feel as a real radio's sidetone, before ever keying an actual transmitter. Many new CW operators build or buy exactly this kind of standalone oscillator specifically so they can practice sending fluently at home, since sending accurately by ear-feel is a different skill from receiving, and it's much cheaper to make sending mistakes into a $10 piezo speaker than on the air.

Adding an Iambic Keyer Mode (Optional Extension)

Once the basic project works, a natural next step is implementing iambic keying logic for paddle users: when both paddle contacts are squeezed together, the keyer alternates dits and dahs automatically rather than requiring you to release and re-press for each element. This is genuinely more involved firmware — it needs to track paddle state on every loop iteration, handle "squeeze" memory (registering that the opposite paddle was briefly touched during an element even if it's released before that element finishes, which is standard iambic keyer behavior known as Mode A or Mode B depending on exact implementation), and time everything against the same WPM-derived dit length used elsewhere in the code. This is a well-documented style of keyer logic with reference implementations widely available if you want to study a working example rather than deriving the state machine from scratch.

Using It to Actually Learn CW

The most effective way to use a trainer like this is short, frequent sessions rather than occasional long ones — ten to fifteen minutes of focused character-recognition drills most days beats an hour once a week, because CW is fundamentally a pattern-recognition skill that benefits from spaced repetition. Start the trainer at a genuinely slow character speed if you're new to the mode, focus on a small subset of characters at a time rather than the whole alphabet at once, and resist the temptation to count dits and dahs consciously — the goal is to hear each character's whole rhythm as a single recognized sound, the same way you recognize a spoken word instead of spelling it out letter by letter.