hermes-ble
Async Rust library and terminal UI for streaming EEG data from Hermes V1 headsets over Bluetooth Low Energy.
Supported hardware
| Model | ADC | EEG channels | IMU | Notes |
|---|---|---|---|---|
| Hermes V1 | ADS1299 | 8 | 9-DOF (accel + gyro + mag) | 250 Hz EEG, 24-bit resolution |
Features
- Cross-platform BLE — built on btleplug (Linux, macOS, Windows)
- Async Rust — tokio-based, zero-copy packet parsing
- Full-screen TUI — real-time 8-channel EEG waveform viewer with ratatui
- 9-DOF motion — accelerometer, gyroscope, and magnetometer streaming
- Packet-loss detection — automatic gap detection in the 0–127 packet index ring
- Built-in simulator — test the TUI without hardware (
--simulate) - Testable app state — the
tui_appmodule can be driven with mock signals in unit tests
Quick start
As a library
Add to your Cargo.toml:
[]
= "0.0.1"
use *;
async
Headless CLI
Scans for a Hermes device, connects, and prints all EEG/IMU events to stdout. Supports interactive commands via stdin:
| Command | Action |
|---|---|
q |
Quit |
t |
Enable test mode |
d |
Disconnect |
Terminal UI
Or run with the built-in simulator (no hardware needed):
Keyboard shortcuts (streaming view)
| Key | Action |
|---|---|
Tab |
Open device picker |
1 |
Switch to EEG view |
2 |
Switch to Motion view |
s |
Trigger a fresh BLE scan |
+ / = |
Zoom out (increase µV scale) |
- |
Zoom in (decrease µV scale) |
a |
Auto-scale Y axis to current peak |
v |
Toggle smooth overlay |
p |
Pause display |
r |
Resume display |
c |
Clear waveform buffers |
d |
Disconnect current device |
q / Esc |
Quit |
Keyboard shortcuts (device picker)
| Key | Action |
|---|---|
↑ / ↓ |
Navigate list |
Enter |
Connect to highlighted device |
s |
Rescan |
Esc |
Close picker |
Module overview
| Module | Purpose |
|---|---|
prelude |
One-line glob import of commonly needed types |
hermes_client |
BLE scanning, connecting, and the HermesHandle command API |
types |
All event and data types (EegSample, MotionData, HermesEvent, …) |
protocol |
GATT UUIDs, sampling constants, ADS1299 conversion, command builders |
parse |
Low-level byte-to-sample decoders for EEG and IMU packets |
tui_app |
Testable TUI state machine, signal simulator, and smoothing filter |
Event types
The mpsc::Receiver<HermesEvent> returned by connect() emits:
| Variant | Description |
|---|---|
Eeg(EegSample) |
8-channel EEG sample in µV (multiple per BLE notification) |
Motion(MotionData) |
9-DOF reading (accel in g, gyro in °/s, mag in gauss) |
Event(DeviceEvent) |
Device event (e.g. button press) |
Config(ConfigResponse) |
Config characteristic response |
Connected(String) |
BLE link established (device name) |
Disconnected |
BLE link lost |
PacketsDropped(usize) |
Gap detected in EEG packet index sequence |
Testing
Run the full test suite (103 tests including doc-tests):
Testing the TUI with mock signals
The tui_app module exposes the core App state machine without any terminal or BLE dependencies, so you can drive it with arbitrary mock signals in tests:
use ;
use *;
let mut app = new;
// Feed the built-in simulator
for i in 0..500
assert_eq!;
// Or feed custom mock signals
app.push;
// Or apply full HermesEvent structs
app.apply_event;
// Test scale, pause, view switching, etc.
app.auto_scale;
app.paused = true;
app.view = Motion;
Benchmarks
HTML reports are generated at target/criterion/report/index.html.
Benchmark suite
| Benchmark | What it measures |
|---|---|
decode_i24_be |
24-bit signed integer decoding |
parse_eeg_packet/{1,4,8,16}_samples |
Full EEG notification parsing at various sizes |
detect_missing_{none,gap_10,wrap} |
Packet-loss detection |
parse_motion |
9-DOF IMU notification parsing |
ads1299_to_microvolts |
ADC-to-µV conversion |
sim_sample / sim_8ch_one_tick |
Signal simulator throughput |
smooth_signal_500pts_w9 |
Moving-average filter (500 points, window 9) |
app_push_one_sample / app_push_8ch_one_tick |
TUI buffer ingestion |
auto_scale_full_bufs |
Auto-scale with full 8×500 sample buffers |
Project structure
hermes-ble-rs/
├── Cargo.toml
├── LICENSE # GPL-3.0-or-later
├── README.md
├── src/
│ ├── lib.rs # Crate root and prelude
│ ├── main.rs # Headless CLI binary
│ ├── hermes_client.rs # BLE client, scanning, HermesHandle
│ ├── types.rs # EegSample, MotionData, HermesEvent, …
│ ├── protocol.rs # GATT UUIDs, constants, ADS1299 conversion
│ ├── parse.rs # Packet decoders (EEG, motion)
│ ├── tui_app.rs # Testable TUI state, simulator, smoothing
│ └── bin/
│ └── tui.rs # Full-screen ratatui TUI binary
├── tests/
│ ├── parse_tests.rs # 25 integration tests
│ ├── protocol_tests.rs # 12 integration tests
│ ├── types_tests.rs # 9 integration tests
│ └── tui_app_tests.rs # 17 integration tests (mock signals)
└── benches/
├── parse_bench.rs # Parsing benchmarks
└── protocol_bench.rs # Protocol + TUI app benchmarks
Platform notes
Linux
Requires the libdbus development headers:
# Debian / Ubuntu
# Fedora
macOS
Works out of the box via CoreBluetooth. The library waits for the Bluetooth adapter to reach PoweredOn state before scanning.
Windows
Works via WinRT Bluetooth APIs. No additional setup required.
License
This project is licensed under the GNU General Public License v3.0 or later.
Copyright © 2026 Eugene Hauptmann, Frédéric Simard