MorseTrainer is a standalone Morse code practice device built on the Waveshare ESP32-S3-Touch-LCD-2.8. Connect your CW paddle or straight key and start practicing -- no computer, no phone, no internet required.
Source code and releases: github.com/Hex4rts/Morsetrainer
Iambic A, Iambic B, and adaptive Straight Key that learns your personal timing
Falling Letters, Callsign Rush, Morse Trace with guided LEARN mode, and QSO Simulator
5-tab interface on a 320x240 IPS display with dark theme
Drop a firmware file on the SD card, reboot, done
| Component | Detail |
|---|---|
| Board | Waveshare ESP32-S3-Touch-LCD-2.8 (16MB flash, OPI PSRAM) |
| Dit paddle | GPIO 15 -- active LOW, internal pullup |
| Dah paddle | GPIO 18 -- active LOW, internal pullup |
| NeoPixels | 20x WS2812 on GPIO 43 |
| Audio | I2S DAC (BCLK=48, LRC=38, DOUT=47) |
| SD card | 4-bit SDMMC -- stores scores, progress, and settings backup |
The screen is divided into five tabs along the bottom: HOME, KEY, OPS, CFG, and NET. Tap any tab to switch screens.
This is your main operating screen. As you key, colored bars appear at the top showing each dit and dah in real time. In Iambic mode, dits are blue and dahs are amber. In Straight Key mode, all bars are green with their width matching exactly how long you held the key.
Below the bars, a large text area displays the decoded characters as you send them. A CLR button lets you clear the text.
At the bottom of the screen, three cards show your current WPM speed, keyer mode, and callsign at a glance.
This is where you choose your keyer mode and adjust your sending speed. The current WPM is shown in large text along with the dit duration in milliseconds. Use the + and - buttons to adjust speed from 5 to 60 WPM.
Three mode buttons let you switch between Iambic A, Iambic B, and Straight Key. The active mode is highlighted. A paddle swap button at the bottom lets you reverse which paddle is dit and which is dah.
The games menu. Four game cards show the name, a short description, and your best score. Tap a card to start playing.
A RESET SCORES button at the bottom clears all high scores and LEARN mode progress. It requires a confirmation tap to prevent accidents.
All device settings in a scrollable list. Each row has a label and a control -- sliders, switches, dropdowns, or buttons.
| Setting | What it does |
|---|---|
| CALL | Your callsign. Tap to edit with on-screen keyboard. |
| VOLUME | Sidetone volume |
| TONE | Sidetone frequency (200-1200 Hz) |
| LIGHT | Screen backlight brightness |
| FLIP | Rotate the display 180 degrees for upside-down mounting |
| LED | Choose the LED effect mode (8 options) |
| LEDs | Number of NeoPixels in your strip (1-60). Use + and - to match your hardware. |
| KEY BRT | Brightness for key-press LED flashes |
| BG BRT | Brightness for background LED animations |
| COLOR | Pick the color used by LED modes (7 presets) |
| BACKUP | Save all settings to the SD card |
| RESTORE | Load settings from SD card backup |
| RESET | Factory reset -- all settings back to defaults |
If flipping makes touch unusable, hold both paddles while powering on to force normal orientation.
WiFi settings for future features. Enter your network SSID and password. Credentials are stored on the SD card.
All games have an EXIT button to return to the menu at any time. Scores are saved to the SD card.
Letters fall from the top of the screen. Key the correct Morse code to destroy each letter before it hits the danger zone at the bottom. The speed increases with each level. The game uses the full A-Z alphabet. If two identical letters are on screen, keying always destroys the one closest to the bottom first.
Beginner: the letter and its Morse code hint are both shown. Expert: only the letter -- you need to know the code.
A random callsign appears on screen. Send it back in Morse as fast as you can. Callsigns use realistic prefixes from around the world.
Beginner: callsign shown with visual and audio, no time limit -- practice at your own pace. Intermediate: callsign shown with audio, 12-second timer. Expert: audio only, 12-second timer.
The device presents a letter (visually, audibly, or both depending on difficulty). You key it back. The device compares your timing to the correct pattern. Four difficulty levels: LEARN, Beginner, Intermediate, and Expert.
Practice realistic CW contacts. The device plays an incoming transmission and you respond with the appropriate reply. Three difficulty levels combined with three complexity levels (simple exchange, RST report, and full QSO) give you 9 combinations to master.
The LEARN option inside Morse Trace is designed to teach Morse code from scratch. It introduces one letter at a time and uses a drill-and-recall cycle to build your memory.
Learning order: E T I A N M S U R W D K G O H V F L P J B X C Z Y Q 0-9
First, you drill each new letter with full guidance -- the letter is shown, the sound is played, and visual bars show the pattern. You copy what you see and hear. After you've learned your first 3 letters, the system starts testing you with recall rounds: the letter appears but there's no sound and no bars. You have to key it from memory.
You get 3 tries per recall. If you fail all 3, the letter goes back to the drill queue. After 3 correct recalls in a row, the next new letter is introduced. The system alternates between drilling new letters and testing learned ones, making sure you don't forget what you've already practiced.
Your progress saves automatically to the SD card. You can turn off the device and pick up exactly where you left off.
Hold both paddles and the keyer produces alternating dits and dahs. Release both paddles and it stops after the current element finishes. Dit and dah lengths are determined by your WPM setting.
Same as Iambic A, but if you release a paddle during an element, the opposite element still plays. This is the "squeeze and release" technique preferred by many experienced CW operators.
Either paddle works as a simple on/off key. The device listens to your timing and figures out your speed automatically -- you don't need to set a WPM for input. It learns the difference between your dits and dahs, detects character boundaries, and inserts spaces, all based on how you actually key.
The WPM setting only controls playback speed in games when the device sends Morse to you.
The device has a strip of 20 NeoPixel LEDs with 8 selectable modes. Two separate brightness sliders let you control how bright the key-press flashes are versus how bright the background animation is.
| Mode | What it does |
|---|---|
| OFF | Completely dark. No response to key presses or game events. |
| KEY FLASH | Dark when idle. Flashes briefly each time you press a key. |
| WPM METER | Shows your current speed as a bar graph across the strip. |
| STEADY | Solid glow in your chosen color. |
| BREATHE | Your chosen color gently pulses up and down. |
| STARFIELD | Random pixels twinkle and fade independently, like stars. |
| CHASE | A bright tail of light runs continuously around the strip. |
| RAINBOW | A continuous rainbow cycles across all the LEDs. |
Every mode except OFF also flashes when you press a key. The flash briefly overrides the background animation, then the background resumes. You can pick your color from 7 presets in the CFG tab -- it affects Steady, Breathe, Starfield, and Chase.
Two ways to get the firmware onto a new device. All files are at github.com/Hex4rts/Morsetrainer.
For those who want to build from the source code or make modifications.
Install Arduino IDE and add ESP32 board support (esp32 by Espressif Systems from Board Manager).
Install libraries: LVGL 9.x and Adafruit NeoPixel via Library Manager.
Copy lv_conf.h from the project into your Arduino libraries folder. It must sit next to the lvgl folder, not inside it:
Extract all project files into a folder called MorseTrainer inside your Arduino sketch folder. The .ino file, all .h/.cpp files, and the partitions.csv file must all be in the same folder.
Configure board settings in the Tools menu:
Connect the board via USB and upload.
The included partitions.csv creates two app partitions, which is required for OTA updates to work. If you select "Huge APP", the firmware will compile and run, but you won't be able to update via SD card later. You'd have to re-flash via USB to fix it.
If someone provides you with a compiled .bin file, you can flash it without Arduino IDE.
Install esptool (requires Python):
With a merged binary (single file, simplest):
Replace COM3 with your actual port (on Linux, usually /dev/ttyACM0).
With separate files:
If the board isn't detected, hold the BOOT button while plugging in USB, then release.
Once the firmware is installed with the correct partition scheme, you can update it without a USB cable.
Get the new firmware binary. If you compiled it yourself, use Sketch > Export Compiled Binary in Arduino IDE. Otherwise, download the .bin from the GitHub releases page.
Copy the file to the root of your SD card and rename it to exactly firmware.bin.
Insert the SD card and power on the device. Every time the device boots, it checks for a file called firmware.bin on the SD card. If it finds one, it flashes the update automatically, renames the file to firmware.done so it won't re-flash next time, and reboots into the new version.
OTA updates require the device to already have been flashed with the custom partition scheme. If your initial install used "Huge APP", you'll need to re-flash via USB once with the correct partition before SD card updates will work.
The device creates these files as needed. Deleting any of them just resets that data.
| File | Purpose |
|---|---|
/firmware.bin | Firmware update -- consumed on boot, renamed to .done |
/mt_settings.txt | Settings backup (from the BACKUP button in CFG) |
/mt_wifi.txt | WiFi network name and password |
/trainer2.txt | LEARN mode progress |
/koch.txt | Koch lesson progress |
/scores/*.txt | High scores for each game |