MORPHEUS

Control Manual  ·  the EEG-driven desk robot
Brain  ·  Waveshare ESP32-S3-Touch-LCD-3.49B (single board)
Control  ·  Muse S EEG headband over BLE — think it, don't touch it
Firmware  ·  Arduino-framework C++ · PlatformIO · living docs + Git
Revision  ·  2026-07-04
EEG GESTURES HANDS-FREE DRIVE RETRO-CYBERPUNK EYE SEGWAY-STYLE
Morpheus systems architecture

1 · What Morpheus is

Morpheus is a two-wheel, Segway-style desk robot with a face. A wide bar display renders a pair of expressive sky-blue eyes and a live waveform "voice"; the whole thing is driven hands-free from a Muse S EEG headband. You furrow your brow to move, tilt your head to steer, and blink to brake — no phone, no remote.

The design goal is a robot that feels alive: a reactive synthwave UI up front, and a clean, scalable firmware behind it.

One board does everything

ESP32-S3R8 · Xtensa LX7 dual-core @ 240 MHz

PSRAM8 MB OPI (octal)
Flash16 MB (W25Q128)
Display3.49" IPS 172×640
Audioonboard mic + speaker
IMUQMI8658 6-axis
LinkWi-Fi + BLE 5 · USB-C

The 30-second data flow

// sense → think → act
Muse EEG  ─BLE─▶ control.h ─▶ drive
Muse accel─BLE─▶ head roll ─▶ steer
onboard mic─I2S▶ level     ─▶ waveform
       state.h (shared bus)
        ├─▶ ui.h    ─▶ eye display
        └─▶ motors.h─▶ servos

2 · Hardware at a glance

SubsystemPartInterfaceNotes
DisplayAXS15231BQSPI ≤ 40 MHz172×640 native; drawn on a 640×172 canvas, software-rotated
Mic (in)ES7210 ADCI2S · I2C 0x40dual MEMS, 16 kHz; own I2C bus on GPIO47/48
Speaker (out)ES8311 DACI2S · I2C 0x18wired; playback is roadmap
IMUQMI8658I2C 17/186-axis, for future self-balancing
GPIO expanderTCA9554I2C 17/18LCD reset + backlight (P2) + audio amp
Motion2× 360° servosLEDC PWMGPIO1 = L, GPIO2 = R; external 5V rail, common ground
ControlMuse S (Gen 1/2)BLE 54-ch EEG + 6-axis accel; preset p21
⚠ POWER RULE

Servos must draw from the external 5V rail, never board logic. A railed head-tilt pinning both servos at full can otherwise brown out the display and mic. Grounds must be tied.

MORPHEUS · Control Manual1 / 3

3 · The control scheme — how you drive it

Control is entirely gestural. The Muse S streams four EEG channels (TP9 AF7 AF8 TP10) plus accelerometer at 256 Hz. Firmware watches two envelopes per channel — a slow one (lf, eyelid movement) and a fast one (hf, muscle/EMG) — and tells four gestures apart. Detection is baseline-relative: each region tracks its own resting level, so "rest ≈ 1.0×" and a deliberate gesture is "2×+", regardless of electrode contact.

▲ Furrow brow — GO
frontal EMG (AF) > temples · sustained ≥ 220 ms

Accelerate forward and hold the speed you reach. Cruise ramps up +0.010 per 50 Hz tick while you hold the furrow, capped at full forward.

▼ Double-blink — BRAKE / REVERSE
two forehead LF spikes within 2000 ms

If moving → brake to a stop. If already stopped → drop into reverse at half the forward cap. (Braking rides on blinks because the Muse temples read too flat for a reliable jaw-clench brake.)

◀ ▶ Tilt head — STEER
accelerometer head roll · deadzone 0.12

Roll your head left/right to steer. A full ~28° tilt is full deflection; steering gain blends the two wheels. Tilt left → turn left.

● Blink — EXPRESSION
single forehead LF spike, EMG quiet

The eyes blink back at you. A single blink is purely expressive — only a deliberate double blink acts on the drive.

Drive state machine — control.h, 50 Hz

StateEntered bySpeedLeaves on
STOPboot · link lost · brake0furrow → FWD
FWDfurrow brow0 … +1.00 (held)double-blink → STOP
REVdouble-blink while stopped−0.50 (half)double-blink → STOP

Safety: if the Muse disconnects, control resets — steering zeroed, cruise dropped, state forced to STOP. Drive output is additionally gated behind the DRIVE_ENABLE compile flag with a deadzone, speed cap, and per-tick ramp.

Tunables — config.h

ConstantValueMeaning
EEG_BLINK_FACTOR2.2×frontal LF ratio to register a blink (refractory 280 ms)
EEG_FURROW_FACTOR1.8×frontal HF ratio to accelerate (held ≥ 220 ms)
EEG_DOUBLE_MS2000window for a "double" blink
DRIVE_ACCEL0.010cruise increment per tick while furrowing
DRIVE_FWD_MAX / REV_MAX1.00 / 0.50forward / reverse speed caps
DRIVE_STEER_GAIN0.60how hard head roll bends the wheels
HEAD_RANGE0.5 radhead tilt (~28°) for full steering deflection
CALIBRATION

Thresholds are dialled in live from the once-per-second [eeg] serial line: watch the ratio peaks and nudge each *_FACTOR so a real gesture clears it and resting never does.

MORPHEUS · Control Manual2 / 3

4 · The face

The display is a "porcelain" cartoon face: a white background, two flat sky-blue eyes that gaze and lean toward your head roll, a soft mouth, and a live onboard-mic waveform strip along the bottom. The panel is 172×640 native, so the firmware draws on a landscape 640×172 canvas in PSRAM and software-rotates it into the portrait buffer.

Render pipeline

Double-buffered canvas in PSRAM → transpose/rotate → QSPI DMA blit. Rendering is gated on gfxReady, so a bad display config can never crash the Muse or mic loops. Runs a steady ~31 fps.

Expression

Eyes track head roll (gaze leans up to 16 px), pupils dilate with mic level, and blink back on every detected blink. The waveform scroll is time-based (px/sec) so it looks live at any frame rate.

5 · Build · flash · monitor

# PlatformIO (platform = pioarduino; needs arduino-esp32 3.3.0+)
pio run                 # compile
pio run -t upload       # flash over USB-C
pio device monitor      # serial log @ 115200
⚠ DISPLAY GOTCHAS — do not relearn the hard way

QSPI clock must stay ≤ 40 MHz (the SPI source is 80 MHz; only 80/N clocks exist). PSRAM must be OPI (memory_type = qio_opi) — the canvas lives there. Display I2C is GPIO17/18 (old 8/9 collided with QSPI CS9). LCD_IPS = false for true colour. ESP32-S3 LEDC is 14-bit — a 16-bit ledcAttach fails silently and the servos go dead.

6 · Key pin map

SignalGPIOSignalGPIO
QSPI CS / CLK9 / 10Left servo1
QSPI D0–D311 · 12 · 13 · 14Right servo2
Display I2C SDA / SCL17 / 18Audio I2C SDA / SCL47 / 48
I2S MCLK / BCLK7 / 15I2S WS · DIN · DOUT46 · 6 · 45
BacklightTCA9554 P2Battery senseADC1 ch3

7 · Roadmap

Next

On-device EEG threshold calibration · confirm drive sign with wheels up · arm-gate before enabling drive.

Later

Segway self-balancing via the QMI8658 IMU · voice / sound via the ES8311 speaker · richer per-eye gaze and expression.

This manual is generated from the Morpheus living docs (docs/) and firmware (src/config.h · control.h · motors.h). Source of truth is the repo; if code and manual disagree, trust the code.

MORPHEUS · Control Manual3 / 3