Skip to the content
PCBKitNew board
2 layers80 × 80 mmGeist, the printed circuit board in 3D

Geist — A USB DAC dongle I'm building from scratch. ESP32-S3, PCM5102A, balanced 4.4mm output, real-time FFT visualizer, and a parametric EQ you can tune from a desktop app

Size
80 × 80 mm
Copper layers
2
Thickness
1.6 mm
Parts
51
Nets
69
Pads
238
Tracks, vias
513, 47
Pours
2
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
4 Jun 2026
Updated
14 Jul 2026

Overview

Geist is an open-source printed circuit board by ru3iks, from the ru3iks/Geist repository on GitHub, published under the MIT licence. It is a 2-layer board of 80 × 80 mm with 51 parts on 69 nets, routed with 513 tracks and 47 vias.

It is built around the ESP32-S3-WROOM-1-N8, OPA1656ID and AP2112K-3.3, with the LM27762DSS and PCM5102APW among its other parts. Its parts include 16 resistors, 15 capacitors, 9 connectors and 6 ICs. It belongs with the ESP32 boards of this library.

Main components

5

README

README.md

GEIST

A reference-grade balanced USB DAC, real-time DSP engine, and tactile media controller — designed and built from scratch.

ESP32-S3 · PCM5102A · OPA1656 · Rust · Tauri


24-bit · 96kHz · Balanced 4.4mm Pentaconn · 10-Band Parametric EQ · FFT Visualizer


What is Geist?

Geist is an open-source USB audio device that I designed from the ground up — the circuit, the PCB, the firmware, and the companion app. It plugs into a computer over USB-C and serves three functions simultaneously:

  1. Reference-Grade USB DAC — Enumerates as a class-compliant USB Audio device (UAC1). Receives lossless 24-bit/96kHz audio, converts it through a hardware DAC, and drives headphones through a true balanced amplifier stage.

  2. Real-Time DSP Engine — A 10-band parametric EQ processes audio in real-time on the ESP32-S3's vector DSP unit. An FFT spectrum visualizer renders live on the onboard display.

  3. Tactile Media Controller — Physical rotary encoder, mechanical buttons, and a toggle switch send HID commands back to the PC for volume and playback control. No drivers needed.

The companion desktop app (built in Rust + Tauri) pushes track metadata and album art to the device, provides a graphical EQ editor, and displays real-time audio analysis.

This project was built as a 2nd year B.Tech ECE student at Manipal Institute of Technology. Every part of it — analog circuit design, mixed-signal PCB layout, embedded Rust firmware, and desktop application — was learned and implemented from scratch.


Hardware Architecture

The Signal Chain

text
  USB-C ──► AP2112K (3.3V LDO) ──► ESP32-S3 ──► PCM5102A DAC ──► 2× OPA1656 ──► Headphones
       └──► LM27762 (±4.5V Charge Pump) ─────────────────────────────┘

Every component was selected to serve a specific role in the audio path:

BlockComponentRole
Power (Digital)AP2112K-3.3LDO regulator — steps 5V USB to clean 3.3V for all digital logic
Power (Analog)LM27762Bipolar charge pump — generates ±4.5V dual rails for the op-amps
Digital BrainESP32-S3-WROOM-1Dual-core 240MHz processor — USB audio streaming, DSP, UI control
DACPCM5102A32-bit delta-sigma DAC — converts I2S digital audio to analog
Amplifiers2× OPA1656Ultra-low-noise dual op-amps — buffers and creates balanced output
DisplayST7789 (240×240)SPI LCD — track info or FFT visualizer
InputAlps EC11 + 3 push buttons + SPDT toggleVolume/nav encoder, media keys, mode switch
Output4.4mm Pentaconn + 3.5mm TRSTrue balanced and single-ended headphone outputs

The Life of an Audio Sample

This table traces a single sample from the moment it leaves the PC to the moment it moves air:

text
Step   Location                  Signal State                        Domain
─────────────────────────────────────────────────────────────────────────────
 1     PC audio engine           24-bit integer: 0x3FFFFF            Software
 2     USB cable (D+/D-)         NRZI-encoded differential packet    Digital
 3     ESP32 USB DMA buffer      Bytes in RAM                        Digital
 4     ESP32 I2S peripheral      Serial bitstream at 3.072 MHz       Digital (3.3V)
 5     PCM5102A delta-sigma      1-bit stream at 6.144 MHz           Digital
 6     PCM5102A OUTL pin         +2.05V (audio + 1.55V DC bias)      Analog
 7     OPA1656 buffer output     +2.05V → OUT_L+ (hot phase)         Analog
 8     OPA1656 inverter output   −0.50V → OUT_L− (cold phase)        Analog
 9     After 10Ω resistors       +2.04V / −0.49V                     Analog
 10    Headphone driver          ΔV = 2.53V differential → sound     Acoustic

Why Balanced?

A balanced signal carries audio on two wires moving in opposite directions. Any noise that couples into the cable appears equally on both wires and is cancelled by subtraction at the receiver:

text
Single-Ended:                      Balanced:
Signal ── ╱╲╱~~╲╱╲ ── +V+noise     HOT  ── ╱╲╱~~╲╱╲ ── +V+noise
Ground ── ──────── ── 0V            COLD ── ╲╱╲~~╱╲╱ ── −V+noise

Amp sees: signal + noise            Amp subtracts: (+V+noise) − (−V+noise)
→ noise passes through ✗            = 2V signal, noise cancelled ✓

The OPA1656 op-amps create this balanced pair — one half buffers the DAC output (positive phase), the other half inverts it (negative phase, gain = −1).


PCB Design Philosophy

Geist is laid out on a 2-layer PCB with strict attention to analog/digital noise isolation:

text
◄──── DIGITAL ZONE ──────────►│◄──────── ANALOG ZONE ──────────►

┌──────────┐  ┌──────────────┐ │  ┌───────────┐  ┌───────────┐
│ ESP32-S3 │  │ Display,     │ │  │ OPA1656×2 │  │ Audio     │
│          │  │ Encoder,     │ │  │           │  │ Jacks     │
│          │  │ Buttons      │ │  │           │  │           │
└──────────┘  └──────────────┘ │  └───────────┘  └───────────┘
                               │
                         ┌─────┴─────┐
                         │ PCM5102A  │ ← sits on the border
                         └───────────┘

Bottom layer: UNBROKEN ground copper pour — no splits.

Key Layout Rules

  • Zonal Segregation — All digital components (ESP32, display, buttons) on one side. All analog components (op-amps, output jacks) on the other. The PCM5102A sits on the boundary with its digital input pins facing left and analog outputs facing right.

  • Solid Ground Plane — No split grounds. A single unbroken copper pour on the bottom layer allows return currents to flow directly beneath their source traces (path of least inductance), minimizing loop area and radiated noise. This is the modern best practice endorsed by TI, Analog Devices, and EMC engineers — not the outdated "split and bridge" approach.

  • Tight Decoupling — Every bypass capacitor is placed physically touching the power pins of its parent IC. A decoupling cap 15mm away is electrically useless — the trace inductance defeats its purpose.

  • Antenna Isolation — The ESP32's Wi-Fi/Bluetooth antenna overhangs the board edge with a keepout zone beneath it. No copper, no traces, no components under the antenna.


Firmware Architecture

The firmware is written in Rust using the Embassy async runtime — bare-metal, no RTOS, no garbage collector.

text
geist-firmware/
├── src/
│   ├── main.rs                ← Embassy entry, spawns all tasks
│   ├── usb/
│   │   ├── descriptors.rs     ← USB composite device (UAC1 + HID + CDC)
│   │   ├── audio.rs           ← Isochronous audio reception, buffer management
│   │   ├── hid.rs             ← Media key HID reports
│   │   └── cdc.rs             ← Serial link to companion app
│   ├── audio/
│   │   ├── i2s.rs             ← I2S DMA driver for PCM5102A
│   │   ├── buffer.rs          ← Lock-free ring buffer (USB → I2S)
│   │   └── dsp.rs             ← Biquad EQ engine + FFT analysis
│   ├── ui/
│   │   ├── display.rs         ← ST7789 SPI driver
│   │   ├── encoder.rs         ← EC11 rotary encoder with debounce
│   │   ├── buttons.rs         ← Debounced media buttons
│   │   └── renderer.rs        ← Track info view + spectrum visualizer
│   └── app/
│       └── state.rs           ← Global state machine

USB Composite Device

Geist enumerates as three USB interfaces simultaneously — no custom drivers required:

InterfaceUSB ClassFunction
Audio Control + Audio StreamingUAC1Appears as a sound card — receives PCM audio from the OS
HID (Consumer Control)HIDSends media keys (play/pause/next/prev) and volume from the encoder
CDC ACMCDCVirtual serial port for companion app communication

Real-Time DSP

The 10-band parametric EQ runs as a chain of biquad filters — each band is 5 multiplies and 4 adds per sample:

rust
// One EQ band — runs for every sample, every channel
fn process(&mut self, x: f32) -> f32 {
    let y = self.b0 * x + self.b1 * self.x1 + self.b2 * self.x2
          - self.a1 * self.y1 - self.a2 * self.y2;
    self.x2 = self.x1; self.x1 = x;
    self.y2 = self.y1; self.y1 = y;
    y
}

At 48kHz stereo with 10 bands, this amounts to ~5 million operations per second — less than 2% of the ESP32-S3's capacity. The biquad coefficients are computed by the companion app from human-readable parameters (frequency, gain, Q) and sent over the CDC serial link.

Task Model

text
Core 0 (Real-time audio — never blocked):
├── usb_audio_task()       ← Isochronous USB reception
├── i2s_feed_task()        ← Drains ring buffer → I2S DMA → PCM5102A
└── usb_hid_cdc_task()     ← HID reports + serial I/O

Core 1 (UI + DSP — lower priority):
├── dsp_task()             ← Biquad EQ processing + FFT analysis
├── display_task()         ← ST7789 rendering @ 30fps
└── input_task()           ← Encoder + button polling + debounce

Companion App

Built with Tauri (Rust backend + web frontend) — a ~5MB native app, not a 150MB Electron wrapper.

Features

  • Now Playing — Reads track info from the OS media session (Windows SMTC / macOS MediaRemote / Linux MPRIS2) and pushes title, artist, and album art to Geist's display over CDC serial
  • 10-Band Parametric EQ — Interactive frequency response curve editor. Drag bands to set frequency, gain, and Q. Coefficients are computed locally and sent to the ESP32 in real-time
  • Real-Time Visualizer — Captures system audio via loopback and renders spectrum analyzer, waveform, and spectrogram views in the browser frontend
  • Device Dashboard — Connection status, sample rate, firmware version, output mode
  • System Tray — Runs silently in the background, auto-connects when Geist is plugged in
  • Firmware Update — Flash new firmware to the ESP32 over USB without opening the case
  • Listening Stats — Tracks hours, volume levels, and session history in a local SQLite database

Tech Stack

LayerTechnology
BackendRust (serialport, windows-rs, cpal, rustfft, rusqlite)
FrontendSvelte + HTML5 Canvas for visualizations
FrameworkTauri v2
ProtocolJSON messages + binary RGB565 art over CDC serial

Bill of Materials

ComponentPart NumberQuantityDescription
MCUESP32-S3-WROOM-11Dual-core 240MHz, USB OTG, vector DSP
DACPCM5102A (PWR)132-bit delta-sigma, I2S input, 112dB SNR
Op-AmpOPA1656IDR2Ultra-low-noise dual op-amp, 2.8 nV/√Hz
LDOAP2112K-3.3TRG113.3V, 600mA, low-dropout regulator
Charge PumpLM27762DSSR1Bipolar ±output from single supply
USB ConnectorUSB-C 16-pin1PCB-mount receptacle
DisplayST7789 240×2401SPI TFT LCD module
EncoderAlps EC111Rotary encoder with push switch
ToggleSPDT1Mode select switch
ButtonsTactile push3Prev / Play-Pause / Next
Balanced Jack4.4mm Pentaconn1Panel-mount, 5-pole
SE Jack3.5mm TRS1Panel-mount, 3-pole
Resistors080515Various values (see schematic)
Capacitors0805150.1µF, 2.2µF, 4.7µF, 10µF

Project Structure

text
Geist/
├── hardware/                    ← KiCad project
│   ├── Geist.kicad_pro
│   ├── Geist.kicad_sch          ← Schematic
│   ├── Geist.kicad_pcb          ← PCB layout
│   └── fabrication/             ← Gerber files for manufacturing
│
├── firmware/                    ← ESP32-S3 Rust firmware
│   ├── Cargo.toml
│   └── src/
│
├── companion/                   ← Tauri desktop app
│   ├── src-tauri/               ← Rust backend
│   ├── src/                     ← Svelte frontend
│   └── package.json
│
├── enclosure/                   ← 3D-printable case (STEP/STL)
│
├── docs/
│   ├── hardware-signal-trace.md ← Complete electrical signal walkthrough
│   └── usb-audio-architecture.md← USB Audio Class deep dive
│
└── README.md

Design Specifications

ParameterValue
Audio FormatPCM, 24-bit, 48kHz / 96kHz
USB ClassUAC1 (class-compliant, driverless)
DAC SNR112 dB (A-weighted)
DAC THD+N−93 dB
Amp Noise2.8 nV/√Hz
Amp THD+N−132 dB (0.000025%)
Output (Balanced)4.4mm Pentaconn, ~4 Vrms differential
Output (SE)3.5mm TRS
DSP10-band parametric EQ (biquad IIR)
Visualization1024-point FFT, real-time spectrum
PowerUSB bus-powered (5V, <500mA)
PCB2-layer, solid ground plane
FirmwareRust + Embassy (bare-metal async)
Companion AppRust + Tauri + Svelte

Building

🚧 Build instructions will be added as each subsystem is completed.

Firmware

bash
# Prerequisites: Rust toolchain + ESP32 target
rustup target add xtensa-esp32s3-none-elf
cargo install espflash

# Build and flash
cd firmware
cargo build --release
espflash flash target/xtensa-esp32s3-none-elf/release/geist-firmware

Companion App

bash
# Prerequisites: Rust, Node.js 18+, pnpm
cd companion
pnpm install
pnpm tauri dev     # development
pnpm tauri build   # production binary

License

This project is open-source under the MIT License.


Designed and built by Arihant · MIT Manipal · 2026

A project born from curiosity about what sits between the music and the ear.

Parts

51
QtyValueFootprintReferences
810kResistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolderR5, R6, R7, R8, R9, R10, R11, R16
50.1uCapacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolderC8, C12, C13, C14, C15
52.2uCapacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolderC3, C4, C6, C10, C11
42.2Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolderR12, R13, R14, R15
4Conn_01x02Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ6, J7, J8, J9
34.7uCapacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolderC1, C2, C5
25.1kResistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolderR1, R2
2Conn_01x05Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_VerticalJ2, J5
2OPA1656IDPackage_SO:SOIC-8_3.9x4.9mm_P1.27mmU5, U6
110uCapacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolderC9
11uCapacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolderC7
126.7kResistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolderR4
127.4kResistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolderR3
1AP2112K-3.3Package_TO_SOT_SMD:SOT-23-5U2
1Conn_01x03Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ3
1Conn_01x07Connector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_VerticalJ4
1ESP32-S3-WROOM-1RF_Module:ESP32-S3-WROOM-1U3
1LM27762Package_SON:WSON-12-1EP_3x2mm_P0.5mm_EP1x2.65_ThermalViasU1
1LOGOGeist:owlv2G***
1PCM5102APackage_SO:TSSOP-20_4.4x6.5mm_P0.65mmU4
1USB_C_Receptacle_USB2.0_16PConnector_USB:USB_C_Receptacle_HRO_TYPE-C-31-M-12J1

The 47 most common of 51 parts.

Questions

How big is the Geist PCB?

It measures 80 × 80 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Geist use?

51 parts, built around the ESP32-S3-WROOM-1-N8, OPA1656ID, AP2112K-3.3 and LM27762DSS. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Geist KiCad files?

From the ru3iks/Geist repository on GitHub, at the commit this page was made from: the board is Geist.kicad_pcb and its schematic Geist.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Geist design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 4 layers125 × 95 mmPrerouted, the printed circuit board in 3D

    Prerouted

    devbisme/ai-circuit-design-tests

    Tests of KiCad-related AI tools.

    179 parts10
  • 2 layers44.7 × 93.6 mmUltisense, the printed circuit board in 3D

    Ultisense

    darshg321/Ultisense

    an esp32-s3 devboard made to sense as many things as possible

    94 parts3
  • 2 layers100 × 60.6 mmMusic Player, the printed circuit board in 3D

    Music Player

    aryan-madan/Offplay

    ESP-32 Bluetooth music player

    34 parts1AGPL-3.0
  • 4 layers70.9 × 75 mmFlagshep Audio Player V1, the printed circuit board in 3D

    Flagshep Audio Player V1

    HYLDAIC/Flagshep-V1_Audio-Player

    A high fidelity semi-portable audio player student project with provided PCB files, 3D files and Code

    77 parts1CERN-OHL-P-2.0
  • 4 layers60 × 60 mmMinion, the printed circuit board in 3D

    Minion

    vibsthebot/minion

    I'm a mac user, and i love my apple wired headphones. mN phone doesn't have headphone jack and my computer doesn't have micro sd or usb a.

    79 parts1
  • 2 layers300 × 100 mmSynthesizer, the printed circuit board in 3D

    Synthesizer

    garvita4/synthesizer

    Snap-Synth is a custom-built modular synthesizer featuring mechanical keys, rotary encoders, per-key RGB lighting, and a custom-designed PCB, combining hands-on sound synthesis with an expandable hardware architecture.

    150 parts
  • 6 layers96 × 129 mmBrain Rot Pod Nano PCB, the printed circuit board in 3D

    Brain Rot Pod Nano PCB

    ParthDhiman32/BrainRot-Pod-nano

    BrainRot Pod Nano is an overengineered Embedded system project wth complex PCB and beautiful CAD enclosure mainly made for playing meme sounds and also can work as a mini music player

    63 partsMIT
  • 4 layers33.8 × 93.1 mmPumper, the printed circuit board in 3D

    Pumper

    wolf-yuan-6115/lightwolf

    Pumper is an experimental RP2354-based USB Audio Class 2 DAC. The board uses a PCM5102A DAC with OPA1652 and OPA1622 analog stages in a compact design intended for learning about USB audio and DAC hardware.

    105 parts1GPL-3.0