Reference
Morse code table, timing formulas, settings, API endpoints, and project layout.
Morse Code Table
Letters
A
·−
B
−···
C
−·−·
D
−··
E
·
F
··−·
G
−−·
H
····
I
··
J
·−−−
K
−·−
L
·−··
M
−−
N
−·
O
−−−
P
·−−·
Q
−−·−
R
·−·
S
···
T
−
U
··−
V
···−
W
·−−
X
−··−
Y
−·−−
Z
−−··
Numbers
0
−−−−−
1
·−−−−
2
··−−−
3
···−−
4
····−
5
·····
6
−····
7
−−···
8
−−−··
9
−−−−·
Punctuation
.
·−·−·−
,
−−··−−
?
··−−··
'
·−−−−·
!
−·−·−−
/
−··−·
(
−·−−·
)
−·−−·−
&
·−···
:
−−−···
=
−···−
+
·−·−·
-
−····−
@
·−−·−·
_
··−−·−
"
·−··−·
Timing & WPM
All timing in Morse-Pi is derived from the dot unit, which is calculated from your WPM setting:
dot_unit = 1200ms / WPM
| Element | Duration | At 20 WPM |
|---|---|---|
| Dot | 1 unit | 60 ms |
| Dash | 3 units | 180 ms |
| Inter-element gap | 1 unit | 60 ms |
| Letter gap | 3 units | 180 ms |
| Word gap | 7 units | 420 ms |
Farnsworth timing
Farnsworth timing keeps character elements at full speed while stretching spacing, making copy practice easier for beginners.
- Character speed uses
wpm_target - Spacing speed uses
farnsworth_wpm - Farnsworth is effectively ON when
farnsworth_wpm < wpm_target
In the UI, configure this in CONFIG → Farnsworth Timing (On/Off + Farnsworth Speed WPM).
Settings Reference
| Setting | Type | Default | Description |
|---|---|---|---|
device_name | string | "Morse Pi" | Name shown to other devices on the network |
wpm_target | int | 20 | Words per minute — all timing derives from this |
pin_mode | string | "single" | single = straight key, dual = iambic paddle |
data_pin | int | 17 | BCM pin for straight key input |
dot_pin | int | 22 | BCM pin for dot paddle (dual mode) |
dash_pin | int | 27 | BCM pin for dash paddle (dual mode) |
speaker_pin | int | 18 | BCM pin for speaker (pin 1) |
speaker_pin2 | int|null | null | Second speaker GPIO pin for push-pull drive. When set (and pigpio is available), the two pins are driven in anti-phase for louder audio. |
output_type | string | "speaker" | speaker = PWM audio, led = on/off |
ground_pin | int|null | null | Legacy single ground output pin |
grounded_pins | list | [] | Multiple GPIO pins driven LOW as grounds |
dot_freq | int | 700 | Dot tone frequency in Hz (100–4000) |
dash_freq | int | 500 | Dash tone frequency in Hz (100–4000) |
volume | float | 0.75 | PWM duty cycle (0.01–0.95) |
theme | string | "dark" | UI theme: dark or light |
difficulty | string | "easy" | Quiz difficulty: easy, medium, hard |
farnsworth_wpm | int | 20 | Spacing speed for Farnsworth timing. Set below wpm_target for extra spacing. |
farnsworth_enabled | bool | false | Compatibility flag accepted by /save_settings; effective mode is derived from farnsworth_wpm < wpm_target. |
farnsworth_letter_mult | float | 2.0 | Legacy/internal compatibility field (not used by current UI controls). |
farnsworth_word_mult | float | 2.0 | Legacy/internal compatibility field (not used by current UI controls). |
kb_enabled | bool | false | Enable USB HID keyboard output |
kb_mode | string | "letters" | letters = decoded chars, custom = mapped keys |
kb_dot_key | string | "z" | Key sent for dot in custom mode |
kb_dash_key | string | "x" | Key sent for dash in custom mode |
radio_local_monitor | bool | true | Play your own local keying audio while transmitting |
radio_remote_monitor | bool | true | Play incoming live key audio from peers |
radio_receive_enabled | bool | true | Allow/ignore incoming live key events from peers |
ℹ Settings persistence
All settings are stored in /opt/morse-pi/morse-translator/settings.json regardless of which backend you're running. New settings added in updates are automatically merged with your existing file — you never lose your configuration.
API Routes
Morse-Pi exposes a REST-like API. The web UI uses these internally, but they can be called from scripts or other tools.
Core
| Route | Method | Description |
|---|---|---|
/ | GET | Main web UI |
/network | GET | Dedicated radio control page |
/radio | GET | Legacy alias for /network |
/diag | GET | GPIO diagnostic page |
/status | GET | Full app state (polled by UI every 250ms) |
/get_settings | GET | Current settings |
/save_settings | POST | Update and persist settings |
/set_mode | POST | Switch active mode |
/clear | POST | Clear all output buffers |
Morse operations
| Route | Method | Description |
|---|---|---|
/encode | POST | Text → Morse code |
/play | POST | Play last encoded text as audio |
/decode_submit | POST | Submit decode quiz answer |
/submit_test | POST | Submit speed test for scoring |
/key_press | POST | Simulate straight key press/release |
/key_press_dual | POST | Simulate dot/dash paddle press/release |
Networking
| Route | Method | Description |
|---|---|---|
/peers | GET | Device identity + discovered peers |
/send_to_peer | POST | Send message to another Pi |
/receive_morse | POST | Receive incoming Morse from peer |
/net_status | GET | Peers + inbox + live receive fields + TX mode/mute config |
/net_key_mode | POST | Enable/disable keyed-message mode |
/net_key_press | POST | Record local key press/release for keyed-message mode |
/net_clear_morse | POST | Clear keyed-message buffers |
/net_send_morse | POST | Send buffered keyed Morse message to selected peer |
/net_live_transmit_set | POST | Toggle this device On Air / Off Air (Off Air blocks live TX/RX) |
/net_live_config | POST | Set live TX mode (single/selected/all_on_air), selected peers, and muted peers |
/net_live_transmit_symbol | POST | Forward live dot/dash symbol events to a peer |
/net_receive_live_symbol | POST | Receive live dot/dash symbol events from peer |
/net_live_key | POST | Forward live key down/up events according to current TX mode |
/net_receive_live_key | POST | Receive live key down/up events from peer (filtered by Off Air / receive enabled / muted sender) |
USB HID keyboard
| Route | Method | Description |
|---|---|---|
/kb_status | GET | HID keyboard status |
/kb_enable | POST | Enable/disable keyboard mode |
/kb_mode | POST | Set letters or custom mode |
/kb_set_keys | POST | Set custom dot/dash key mappings |
Project Structure
Morse-Pi/
├── install.sh ← fresh installer — Python backend
├── install-rust.sh ← fresh installer — Rust backend
├── transition-rust.sh ← transition script: Python → Rust
├── update.sh ← updater — Python backend
├── update-rust.sh ← updater — Rust backend (pull + rebuild)
├── packages.sh ← package updater (Python: upgrade apt + pip)
├── docs/ ← documentation site (GitHub Pages)
│ ├── index.html ← home / landing page
│ ├── getting-started.html ← features, install, wiring, modes guide
│ ├── maintenance.html ← updating, packages, services
│ ├── usb-hid.html ← USB HID keyboard setup & usage
│ ├── reference.html ← morse table, settings, API, structure
│ ├── troubleshooting.html ← common issues & error solutions
│ └── style.css ← shared stylesheet
├── morse-translator/ ← Python backend (Flask)
│ ├── app.py ← Flask app, GPIO, Morse timing, networking, HID
│ ├── settings.json ← user settings (auto-created, preserved on update)
│ ├── stats.json ← user statistics (auto-created, preserved on update)
│ ├── words.json ← word list for quiz/speed modes
│ └── templates/
│ ├── index.html ← single-page web UI
│ ├── radio.html ← dedicated radio control page
│ └── diag.html ← GPIO live diagnostic popup
└── morse-translator-rust/ ← Rust backend (native binary)
├── Cargo.toml ← Rust package manifest & dependencies
├── templates/ ← source templates synced into runtime app dir by install/update scripts
│ ├── index.html
│ ├── radio.html
│ └── diag.html
└── src/
├── main.rs ← HTTP server, routing, template engine
├── state.rs ← settings, stats, app state, JSON serialization
├── morse.rs ← Morse code translation, word lists, phrases
├── keyboard.rs ← USB HID keyboard via /dev/hidg0
├── gpio.rs ← GPIO via pigpiod_if2 C FFI
├── sound.rs ← tones, iambic keyer, send/speed/net modes
└── network.rs ← UDP beacon peer discovery
# Created by installer on Raspberry Pi:
/etc/systemd/system/morse-pi.service ← auto-start on boot
/etc/systemd/system/morse-pi-hid.service ← USB HID gadget service
/usr/local/bin/morse-pi-hid-setup.sh ← USB HID configuration script
/etc/udev/rules.d/99-morse-pi-hid.rules ← udev rule for /dev/hidg0