cognionics
Rust library and terminal UI for streaming EEG data from Cognionics / CGX headsets over USB serial (FTDI dongle).
Supported Hardware
| Model | EEG ch | ExG | ACC | Default Rate | Channels |
|---|---|---|---|---|---|
| Quick-20 | 20 | 4 | ✗ | 500 Hz | 26 total |
| Quick-20r-v1 | 20 | 1 | ✓ | 500 Hz | 26 total |
| Quick-20m | 20 | 1 | ✓ | 500 Hz | 26 total |
| Quick-20r | 20 | 2 | ✓ | 500 Hz | 27 total |
| Quick-32r | 30 | 2 | ✓ | 500 Hz | 37 total |
| Quick-8r | 8+ref | 1 | ✓ | 500 Hz | 15 total |
| AIM-2 | — | 11 | ✗ | 500 Hz | 13 total |
| Dev Kit | 8 | — | ✓ | 500 Hz | 13 total |
| Patch-v1 | 2 | 3 | ✓ | 250 Hz | 10 total |
| Patch-v2 | 2 | 2 | ✓ | 250 Hz | 8 total |
Device model is auto-detected from the USB descriptor string. All models use the same serial protocol — 24-bit ADC samples at 3 bytes per channel, with sync-byte framing over FTDI USB serial.
Quick Start
CLI
# List available devices and supported models
# Auto-detect and stream from the first CGX device found
# Specify a model explicitly
# Specify a serial port
# Enable impedance check
TUI (Terminal UI)
# Real-time waveform viewer (auto-detect device)
# Simulate a Quick-20r (no hardware needed)
# Simulate a specific model
TUI Keys:
| Key | Action |
|---|---|
1 |
EEG-only view |
2 |
All channels view |
+ / - |
Zoom out / in (µV scale) |
a |
Auto-scale to fit signal |
v |
Toggle smooth overlay |
p / r |
Pause / Resume |
c |
Clear buffers |
q / Esc |
Quit |
Library Usage
use *;
async
Cargo.toml
[]
# Full build (includes TUI + virtual device):
= "0.1.0"
# Library only — no TUI, no virtual device:
= { = "0.1.0", = false }
# Library + virtual device for testing (no TUI):
= { = "0.1.0", = false, = ["virtual_device"] }
Features
| Feature | Default | Description |
|---|---|---|
tui |
✓ | ratatui terminal UI binary |
virtual_device |
✓ | Mock device module for testing without hardware |
Architecture
USB Dongle ──(FTDI serial)──▸ CgxReader (background OS thread)
│
▼
byte stream → frame sync → 24-bit decode
│
▼
tokio::mpsc::Sender<CgxEvent>
│
▼
user's mpsc::Receiver<CgxEvent>
The reader runs on a dedicated OS thread (not a tokio task) because serial port I/O is blocking. Events are forwarded to the async world through a tokio mpsc channel.
Events
The CgxEvent enum carries all data from the device:
| Variant | Description |
|---|---|
Sample(CgxSample) |
One multi-channel sample with channels (µV), packet_counter, trigger, timestamp |
Connected(String) |
Connection established; carries device descriptor |
Disconnected |
Device disconnected or stream ended |
PacketLoss { lost, prev_counter, curr_counter } |
Gap detected in packet counter sequence |
Error(String) |
Non-fatal error (e.g. data gap timeout) |
Info(String) |
Informational log message |
Protocol Details
CGX headsets transmit a continuous byte stream over FTDI USB serial:
- Framing: Each sample starts with sync byte
0xFFfollowed by overhead bytes (status/battery), then 3 bytes per channel (24-bit big-endian signed ADC) - Channels: Last two channels are always Packet Counter (8-bit, wrapping 0–255) and TRIGGER
- ADC scale:
raw × 3.880510727564494e-10volts/LSB - Impedance: 125 Hz carrier wave toggled via DTR; scale factor 0.0390625 ohms/LSB
Linux Permissions
On Linux you may need a udev rule to access the USB port without root:
|
# Unplug and re-plug the dongle
Module Overview
| Module | Purpose |
|---|---|
prelude |
One-line glob import of all commonly needed types |
cgx_client |
Device scanning, serial connection, and CgxHandle API |
types |
All event and data types (CgxEvent, CgxSample, etc.) |
devices |
Channel layouts, baud rates, and device configs for all models |
parse |
24-bit ADC decoders, frame sync, and packet loss detection |
virtual_device |
(feature) Mock device, fault injection, build_frame(), start_virtual() |
License
MIT