Firmware v7.1.5 · ESP32 CYD · Open Source
The IW1RMM CW Encoder turns a €10 ESP32 touchscreen board into a full-featured Morse code keyer — all keying modes, speed control, preset messages, SD card playback, Bluetooth wireless control, and much more. Plug in your paddle and key.
Developed by Mauri IW1RMM, 2025–2026 · Based on original firmware by VK2IDL · GPL v3
Questo progetto implementa un encoder CW (codice Morse) basato su ESP32 CYD con interfaccia web. È progettato per radioamatori, test RF e sperimentazione digitale.
The project implements a CW (Morse code) encoder based on ESP32 CYD with a web interface, designed for ham radio operators, RF testing and digital experimentation.
What it does
Everything starts with a paddle and a key output. The IW1RMM CW Encoder is first and foremost a high-quality Morse keyer — then a touchscreen interface, a message player, a beacon, and a contest tool.
Iambic A, Iambic B, Manual (straight key), Auto (electronic bug), Ultimatic. Switch modes in one tap. Each transmitted character is decoded in real time and shown on the scrolling display — even in manual mode.
5 to 100 WPM on screen. Full control over DAH ratio (2.0–4.5×), inter-element spacing weight, and Farnsworth character spacing for training. All values saved to non-volatile memory and restored on every boot.
A clean, responsive 5-tab graphical UI on the 2.8" ILI9341 display. Scrolling text bars show transmitted characters live. Interactive 4-point touch calibration on first boot, stored permanently in NVS. NEW v7.1.5
CQ, Name, Test, Ant/Rig, RST, Free — send any message with a single tap. Edit directly on screen with the virtual keyboard. Stored permanently in NVS flash. Messages can be sent individually or chained automatically.
Load any .txt file from MicroSD and transmit it in Morse at any speed. Full PLAY / PAUSE / STOP control. Text scrolls live on the display as it is transmitted. Ideal for long-text practice or automated operation.
Built-in Bluetooth Low Energy (Nordic UART) lets you control the keyer from any Android phone or PC. Send text, change speed, play memories, query status. Always available regardless of operating mode. FIX v7.1.3
More features
Automatic CQ at configurable intervals (10–600 s). Ideal for propagation tests and unattended beacons.
Auto-incrementing serial number per transmission. Counter saved to NVS and survives reboots.
Internal ESP32 DAC generates clean sidetone, 300–1200 Hz adjustable. Four volume levels. No external audio chip needed.
High Speed CW (60–100+ WPM) for meteor scatter and aurora contacts.
Ultra-slow CW (3s, 6s, 10s, 30s per DIT) for low-power propagation experiments and QRSS grabbers.
Full prosign set: <AR> <SK> <BT> <KN> <AS> <SN> — sent without inter-character space, per ITU standard.
Built-in digital clock on display. Optional DS3231 RTC for persistent timekeeping across reboots.
All transmitted characters logged to CSV with timestamp, WPM, frequency and session metadata.
Swap DIT and DAH in one tap. For left-handed operators or reversed cables.
Full WinKey 2.3 binary protocol at 1200 baud for N1MM+, Win-Test, DXLog and other logging software.
K3NG ASCII command set at 115200 baud via USB serial and BLE. Backslash commands for full control.
morseTask on Core 0, LVGL on Core 1. 1 kHz hardware timer for sub-millisecond keying accuracy.
Hardware
Everything you need in a single €10 board. No external display, no extra MCU, no separate audio chip.
| Component | Detail |
|---|---|
| MCU | ESP32-D0WD dual-core 240 MHz · 520 KB RAM · 4 MB flash |
| Display | ILI9341 2.8" TFT 320×240 · SPI (VSPI) |
| Touch | XPT2046 resistive · SPI (HSPI) · 4-point calibrated |
| KEY / PTT output | GPIO 27 · shared pin · HIGH = active |
| Sidetone | GPIO 25 DAC · 300–1200 Hz adjustable |
| Left paddle (DIT) | GPIO 35 |
| Right paddle (DAH) | GPIO 22 |
| SD card | HSPI · GPIO 18 / 19 / 23 / 5 |
| Power | USB-C 5V · no external supply needed |
| Wireless | BLE 4.2 integrated · Wi-Fi available for future use |
Changelog
| Version | Changes |
|---|---|
| v7.1.5 | Code comments optimised, reduced code lines. Minor text adjustments. |
| v7.1.4 | Interactive 4-point touch calibration. Separate NVS namespace touch_cal. Serial escape, timeout and fallback protection. New K3NG commands \J \Y \X \E. LATEST |
| v7.1.3 | BLE fix: device name now visible on Android BLE scan. Name set in primary advertising payload; service UUID moved to scan response. |
| v7.1.2 | MANUAL mode fix: characters now displayed on text bars. mySet[] extended to 121 elements (full ITU punctuation + prosigns). Prosign display: <AR> <BT> <SK> etc. |
| v7.1.1 | FreeRTOS multi-task architecture refactoring. BLE with native BLEDevice. morseTask stack increased to 8192 bytes. |
| v6.9.0 | Base version by VK2IDL. Complete Morse keyer, BLE Nordic UART, SD player, WinKey 2.3, K3NG serial, LVGL 5 tabs, clock, QRSS, Farnsworth, beacon, contest. |
Getting Started
git clone https://github.com/mfalco1959/IW1RMM_CW_Encoder.git
Or download the ZIP from GitHub and extract it.
In Arduino IDE: File → Preferences → Additional boards manager URLs, add:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Then Tools → Board → Boards Manager → search esp32 → install esp32 by Espressif Systems.
⚠ Tested with ESP32 Arduino Core 3.x. The BLE and DacESP32 libraries require Core 3.x — do not use 2.x.
In Arduino IDE: Sketch → Include Library → Manage Libraries
| Library | Version | Author | How to install |
|---|---|---|---|
| lvgl | 8.4.0 | LVGL | Library Manager → search lvgl |
| TFT_eSPI | 2.5.43 | Bodmer | Library Manager → search TFT_eSPI |
| XPT2046_Touchscreen | 1.4 | Paul Stoffregen | Library Manager → search XPT2046 |
| DacESP32 | 2.1.2 | Thomas Jentzsch | Library Manager → search DacESP32 |
| SD | built-in | Arduino | Built-in — no install needed |
| SPI | built-in | Arduino | Built-in — no install needed |
| Preferences (NVS) | built-in | Espressif | Built-in with ESP32 Core |
| BLEDevice / BLEServer / BLEUtils / BLE2902 | built-in | Espressif | Built-in with ESP32 Core |
| esp_bt.h | built-in | Espressif | Built-in with ESP32 Core (ESP-IDF) |
Copy docs/User_Setup_example.h to your TFT_eSPI library folder
(typically Documents/Arduino/libraries/TFT_eSPI/) and rename it User_Setup.h,
replacing the existing file. Key settings for the CYD:
#define ILI9341_DRIVER #define TFT_MISO 12 #define TFT_MOSI 13 #define TFT_SCLK 14 #define TFT_CS 15 #define TFT_DC 2 #define TFT_RST -1 #define TOUCH_CS -1 #define SPI_FREQUENCY 40000000
⚠ Make sure TFT_MISO 12 is defined and NOT commented out — the SD card will fail otherwise.
Copy include/lv_conf.h from this repository into the sketch folder
(same directory as IW1RMM_CW_Encoder.ino).
This file enables the fonts used by the UI — without it the sketch will not compile.
Select board and configure upload options exactly as shown:
| Setting | Value |
|---|---|
| Board | ESP32 Dev Module |
| Flash Mode | DIO |
| Flash Frequency | 40 MHz |
| Partition Scheme | Huge APP (3MB No OTA/1MB SPIFFS) |
| Upload Speed | 115200 |
| Core Debug Level | None |
⚠ DIO + 40 MHz are mandatory. CYD boards often use ZBIT ZB25VQ32 flash chips that are sensitive to faster or QIO settings — using QIO or 80 MHz will cause upload failures or boot loops.
💡 Upload speed: 115200 baud is the safe recommended value. If you plan to upload only occasionally, you may try increasing the speed (e.g. 460800 or 921600) to reduce upload time, but stability depends on your USB cable and PC driver.
Open IW1RMM_CW_Encoder/IW1RMM_CW_Encoder.ino in Arduino IDE.
Connect the CYD via USB-C. Select the correct COM port under Tools → Port.
Click Upload.
On first boot, the touch calibration wizard will start automatically. Follow the on-screen instructions to touch each of the 4 corner crosshairs. Calibration is saved permanently — it will not repeat on subsequent boots.
If you prefer not to use Arduino IDE, flash the precompiled .bin directly:
python -m esptool --chip esp32 --port COM4 --baud 115200 \ --before default-reset --after hard-reset \ write-flash -z --flash-mode dio --flash-freq 40m \ --flash-size detect 0x10000 IW1RMM_CW_Encoder_v714.bin
Replace COM4 with your actual port (Linux: /dev/ttyUSB0, Mac: /dev/cu.usbserial-*).
3D Printable Case
A complete printable enclosure designed for the ESP32-2432S028R board. Print with PLA or PETG, 0.2 mm layer height. Files render in 3D directly on GitHub.
Documentation
Acknowledgements
Original CW Encoder firmware. This project is built upon VK2IDL's foundational work and would not exist without it.
K3NG Arduino CW Keyer — command set and protocol reference.
github.com/k3ng
WinKey 2/3 protocol specification and WK3 IC datasheet.
k1el.com
Light and Versatile Graphics Library — the embedded GUI engine powering this project.
lvgl.io
ESP32 Cheap Yellow Display community — documentation, pinouts and hardware resources.
Architecture, WinKey protocol, BLE, FreeRTOS integration, LVGL UI, touch calibration, K3NG extensions. Savona, Italy — 2025/2026.