Remote CW keying

For my remote/portable station I wanted something that sounds simple on paper: the paddle on my desk, the radio somewhere else, and yet a CW signal that "feels" like everything is sitting right next to each other. No lag, no glitches, no dropped dits and dahs. In this post I'll walk through the design and first test results of my homebrew remote keyer.

The problem

A remote station typically has a network sitting between the operator and the radio. For voice, and even for digital modes, a bit of latency is rarely a disaster. CW is different: the timing of the key line is the signal. Any jitter of a few tens of milliseconds immediately shows up as distorted dits and dahs.

Classic solutions like wfview usually handle this by converting text you type on your keyboard into Morse code on the radio. I wanted to take a slightly different approach: transport the bare key line itself over the network, with the iambic logic (for now) living on a Morserino on the paddle side.

The architecture

The system consists of two identical Olimex ESP32-PoE boards with W5500 Ethernet. A standard ESP32 only has Wi-Fi, and I wanted to focus on low latency, so I chose a solution with Ethernet.

Olimex ESP32

  • paddle-node — sits on the Morserino side and reads the bare key line
  • radio-node — sits at the transceiver and drives the PTT/key line

Current status: no iambic keyer is integrated into the ESP32 yet. Iambic keying is still handled by the Morserino. The Morserino's output is connected to the ESP32. On the output of the second ESP32, I used an optocoupler to drive my transceiver's keying input.

The two boards talk to each other over UDP (port 5005) using a small, deliberately minimal protocol (morse_packet_t): a type, sequence number, timestamp in microseconds, and the key state. There's a 50ms heartbeat with a 200ms dead-man failsafe — if the link drops, the key line automatically closes instead of getting stuck "open."

The firmware is a PlatformIO project with a shared protocol.h between both nodes, built with help from Claude Code. It was the first time I used Claude code on the command line, and I was really impressed. I got the code working in no time, and much better than what I achieved in previous projects.

First test results

After the necessary troubleshooting (self-healing heartbeat, switching to unicast, adding debounce), I got these numbers during a test session:

  • 2456 packets sent
  • 0 packets lost (0.00%)
  • jitter: roughly 400µs spread, stddev ~48µs
  • measured over 284 key transitions

For a network link, that's comfortably within what's needed to keep CW readable, the human ear doesn't notice jitter at that scale.

What's next

The next step is to take the Morserino out of the chain and integrate the K3NG keyer's iambic logic directly on the paddle-node ESP32, speed control via rotary encoder + LCD, command mode via paddle or serial port, and straight-key support.

Source code is on Codeberg: morse-remote.

More to come once the K3NG integration is up and running.

73, Bert — ON4DMD

Comments

← Back to overview

Who am I?