DECtalk - Host GUI & Serial API
This directory contains host applications for controlling the DECtalk text-to-speech engine running on an ESP32 microcontroller:
- A browser-based Web GUI (
web/index.html) — no install required; runs entirely in Chrome / Edge using the Web Serial API. - A Qt desktop GUI (
dtesp_gui_qt.py) — full-featured Python / PySide6 application. - A serial API module (
dtesp_serial.py) — Python library used by the Qt GUI and reusable for scripting.
Table of Contents
- Overview
- Requirements
- Quick Start
- Web GUI (
web/index.html) - GUI Features
- ESPress GUI (
dtesp_gui_qt.py) - DECtalk ESPress Protocol
- Python API Module
Overview
The host communicates with the ESP32 over USB using the DECtalk ESPress serial protocol. The ESP32 firmware boots directly into ESPress protocol mode on its host USB port, which appears as a standard serial port on the host computer (for example COM, /dev/ttyACM*, or /dev/ttyUSB* depending on the board and driver). On ESP32-S3 the host link uses USB CDC-ACM (TinyUSB); on ESP32-C6 it uses the built-in USB Serial/JTAG interface. The dedicated physical-device guide in ../HARDWARE.md covers the photographed ESP32-C6 perfboard build; the host tools themselves support either firmware target.
Requirements
- Python 3.7 or later
- pyserial (
pip install pyserial) - PySide6 (
pip install PySide6) — for the Qt GUI. PyQt6 is accepted as a fallback. - ESP32-S3 / ESP32-C6 running the DECtalk firmware (see ../README.md; for the documented ESP32-C6 hardware build, see ../HARDWARE.md)
Quick Start
Option 1 — Web GUI (no install)
- Flash the DECtalk firmware to your ESP32 (see ../README.md).
- Open
host/web/index.htmlin Chrome or Edge 89+ (either by double-clicking the file, serving it from any static web server, or visiting the published GitHub Pages site). - Click Connect and select the ESP32 serial port from the browser chooser.
- Type text and click Speak.
Option 2 — Qt desktop GUI
Flash the DECtalk firmware to your ESP32 (see ../README.md).
Install the Python dependencies:
pip install pyserial PySide6Run the ESPress GUI:
python dtesp_gui_qt.pySelect the serial port for your ESP32 and click Connect. On Linux this is often
/dev/ttyACM*on ESP32-S3 and may be either/dev/ttyACM*or/dev/ttyUSB*on ESP32-C6 depending on the USB bridge/driver.Type text in the text box and click Speak.
Web GUI (web/index.html)
A single self-contained HTML page that talks to the ESP32 directly from the browser via the Web Serial API (Chrome / Edge 89+). No Python, backend, or local install is required. It mirrors the Qt GUI feature for feature, reorganised into a vertical browser-friendly layout:
- Connection – click Connect and pick the ESP32 from the browser's serial port chooser. Disconnect cleanly with Disconnect.
- Speech Settings – voice, rate (WPM) and pitch (Hz) sliders with live-updating value labels.
- Text to Speak – multi-line textarea; supports DECtalk inline commands such as
[:np]or[:dv ap 120]. - Speak / Pause / Resume / Flush / Query Status / Clear Text – same actions as the Qt app. Pause/Resume send SO/SI; Flush uses the TSR
]+ ETX + XON sequence and waits for the SOH ack. - Audio Settings dialog – output profile (speaker / headphone), headset auto-switch, volume, class-D speaker amp gain, mute, bass / treble, 5-band peaking EQ with named presets (
flat,speech,crisp,warm), DRC with tuning presets (soft,speech,loud). Each change is sent immediately as a[:fw …]inline command. Save to Device persists the current state to NVS. - Device Status panel – raw status word plus highlighted Ready / Transmitting / Flushing / Index indicators, polled every 2 s.
- Communications Log – timestamped TX/RX events, capped at 1000 lines, with auto-scroll and a Clear button.
The page is dark-themed to match the project's GitHub Pages site and the existing Web Flasher. Because it runs entirely in the browser, the same index.html file works whether opened from disk, served from a static web server, or hosted alongside the Web Flasher on GitHub Pages.
Browser support: the Web Serial API is currently only available in Chromium-based browsers (Chrome, Edge, Opera, Brave, …) on desktop. Firefox and Safari are not supported. On Linux, ensure your user has access to the serial device (typically by being in the
dialoutoruucpgroup).
GUI Features
Voice Selection
Choose from 9 built-in DECtalk voices:
| Voice | Description |
|---|---|
| Paul | Standard male (default) |
| Betty | Standard female |
| Harry | Deep male |
| Frank | Older male |
| Dennis | Breathy male |
| Kit | Child (~10 years) |
| Ursula | Light female |
| Rita | Deep female |
| Wendy | Whispery female |
Rate Control
Adjust the speaking rate from 75 to 600 words per minute (WPM) using the slider. The default is 200 WPM.
Pitch Control
Adjust the average pitch from 50 to 400 Hz using the slider. The leftmost position uses the voice's default pitch.
Text Editing
The multi-line text box supports standard editing operations (Cut/Copy/Paste, Select All, Undo/Redo) and keyboard navigation.
DECtalk Inline Commands
You can include DECtalk inline commands directly in the text box. These are processed by the DECtalk engine on the ESP32. For example:
[:phoneme on][hxeh<500,20>low<500,22>]
[:np] Hello, I am Paul. [:nb] And I am Betty.
[:dv ap 160 pr 50] This voice has modified pitch.
[:tone 500,500] A tone before speech.
For the full DECtalk inline-command reference, use the upstream DECtalk documentation that matches your language/voice package.
ESPress GUI (dtesp_gui_qt.py)
A Qt-based (PySide6/PyQt6) GUI for the DECtalk ESPress serial protocol with full accessibility support. Features:
- Connection – select serial port and connect with automatic device probe
- Multi-line text input with standard editing
- Voice / rate / pitch controls
- Audio Settings dialog (Audio Settings… button next to Query Status) – output profile (speaker / headphone), autoswitch, volume, class-D speaker amp gain, mute, bass / treble tone controls, 5-band peaking EQ with named presets (
flat,speech,crisp,warm), and DRC with tuning presets (soft,speech,loud). All changes are sent as[:fw …]inline commands and take effect immediately; the Save to Device button (both inside the dialog and in the main toolbar row) persists them to NVS. The dialog is modeless, so the main window stays interactive while it is open. - Pause / Resume buttons — send SO (0x0E) / SI (0x0F) control characters
- Flush button — cancel all pending speech (TSR flush sequence + SOH ack)
- Query Status — send ENQ (0x05) and decode the DLE status response
- Device Status panel — raw status word and colored indicators for Ready, Transmitting, Flushing, and Index states
- Automatic status polling for live state display
- Communications Log with timestamped TX/RX events
- Full screen-reader accessibility (NVDA, Orca, VoiceOver, …): accessible names/descriptions, keyboard mnemonics, logical tab order, high-contrast status indicators
DECtalk ESPress Protocol
The ESP32 firmware boots directly into ESPress protocol mode on its host USB port. No handshake or mode-switch command is needed. On ESP32-S3, opening the USB CDC-ACM port triggers a DTR assertion that the firmware detects, causing it to reset protocol state and send XON to indicate readiness. On ESP32-C6, connection detection uses the USB Serial/JTAG connected state; the firmware disables the RTS-triggered chip reset so opening the port does not reboot the device.
Key Protocol Elements
| Feature | Description |
|---|---|
| Transport | USB CDC-ACM on ESP32-S3; USB Serial/JTAG on ESP32-C6 (both appear as normal serial ports such as COM, /dev/ttyACM*, or /dev/ttyUSB*) |
| Text format | Plain ASCII text + CR |
| Flush/stop | ETX (0x03) |
| Status query | ENQ (0x05) → 4-byte DLE status response |
| Pause/Resume | SO (0x0E) / SI (0x0F) |
| Flow control | XON/XOFF (application-level, in-band) |
| Device ready | XON sent on startup / reconnect |
Python API Module
The dtesp_serial.py module can be used independently from the GUI:
from dtesp_serial import DECtalkESPressSerial
dtesp = DECtalkESPressSerial()
# List available serial ports
print(DECtalkESPressSerial.list_ports())
# Connect to the ESP32
dtesp.connect("/dev/ttyACM0")
# Detect device (same probe as the original comchk utility)
if dtesp.detect_device():
print("DECtalk device detected!")
# Send text (device speaks it immediately)
dtesp.send_text("Hello world.")
# Speak with voice and rate (uses inline commands)
dtesp.speak("Now I am Betty.", voice="Betty", rate=250)
# Request device status
status = dtesp.request_status()
if status >= 0:
print("Device status: 0x%04X" % status)
# Pause and resume speech
dtesp.pause()
dtesp.resume()
# Flush (cancel) all speech
dtesp.flush()
# Flush with acknowledgment (TSR FLUSH_TEXT sequence)
if dtesp.flush_with_ack():
print("Flush acknowledged")
# Disconnect
dtesp.disconnect()Available Voices
from dtesp_serial import VOICES
print(list(VOICES.keys()))
# ['Paul', 'Betty', 'Harry', 'Frank', 'Dennis', 'Kit', 'Ursula', 'Rita', 'Wendy']Building DECtalk Command Strings
from dtesp_serial import build_dtesp_prefix
# Build inline command prefix
prefix = build_dtesp_prefix(voice="Harry", rate=150, pitch=90)
print(prefix) # "[:nh][:ra 150][:dv ap 90]"