Expand description
§openbci
A pure-Rust driver for OpenBCI EEG/EMG boards. No C/C++ runtime, no BrainFlow dependency — speaks directly to the hardware over serial, Bluetooth LE, WiFi, or UDP.
§Supported boards
| Board | Channels | Interface | Type |
|---|---|---|---|
| Cyton | 8 EEG | USB serial (FTDI dongle) | board::cyton::CytonBoard |
| Cyton + Daisy | 16 EEG | USB serial (FTDI dongle) | board::cyton_daisy::CytonDaisyBoard |
| Cyton WiFi | 8 EEG | WiFi Shield → TCP | board::cyton_wifi::CytonWifiBoard |
| Cyton Daisy WiFi | 16 EEG | WiFi Shield → TCP | board::cyton_daisy_wifi::CytonDaisyWifiBoard |
| Ganglion | 4 EEG | Bluetooth LE (ble feature) | board::ganglion::GanglionBoard |
| Ganglion WiFi | 4 EEG | WiFi Shield → TCP | board::ganglion_wifi::GanglionWifiBoard |
| Galea | 24 EEG+EMG | UDP | board::galea::GaleaBoard |
§Design overview
Every board implements the Board trait, which follows a simple lifecycle:
prepare() → [apply_channel_config()] → start_stream() → [samples] → release()Cyton-family boards additionally implement ConfigurableBoard, which lets
you configure the ADS1299 amplifier gain, input mux, bias network, and SRB
connections on a per-channel basis.
Electrode placement is handled by ElectrodeLayout, backed by MNE-Python’s
standard 10-05/10-10/10-20 montage data (3-D head-surface positions for 334
named sites).
§Quick start
§Cyton — 8 channels over USB
use openbci::board::cyton::CytonBoard;
use openbci::board::{Board, ConfigurableBoard};
use openbci::channel_config::{ChannelConfig, Gain};
use openbci::electrode::{ElectrodeLayout, positions};
// Map each of the 8 channels to a standard 10-20 electrode site.
let layout = ElectrodeLayout::from_labels(&[
positions::FP1, positions::FP2,
positions::C3, positions::CZ,
positions::C4, positions::P3,
positions::PZ, positions::P4,
]);
let mut board = CytonBoard::new("/dev/ttyUSB0") // Windows: "COM3"
.with_electrode_layout(layout);
// Open the port and wait for the board's "$$$" ready marker.
board.prepare().unwrap();
// Optionally reconfigure amplifier gain on every channel.
board.apply_all_channel_configs(&vec![ChannelConfig::default(); 8]).unwrap();
// Start acquisition — the returned StreamHandle owns a background reader thread.
let stream = board.start_stream().unwrap();
for sample in stream.into_iter().take(250) { // ~1 second at 250 Hz
println!(
"t={:.3}s {} = {:+.1} µV",
sample.timestamp,
board.electrode_layout().label(0),
sample.eeg[0],
);
}
// `stream` drops here → stop signal sent to background thread.
board.release().unwrap();§Cyton + Daisy — 16 channels
use openbci::board::cyton_daisy::CytonDaisyBoard;
use openbci::board::Board;
use openbci::electrode::ElectrodeLayout;
let mut board = CytonDaisyBoard::new("/dev/ttyUSB0")
.with_electrode_layout(ElectrodeLayout::from_labels(&[
// Cyton (channels 0–7)
"Fp1","Fp2","F3","F4","C3","Cz","C4","Pz",
// Daisy (channels 8–15)
"P3","P4","O1","O2","F7","F8","T7","T8",
]));
board.prepare().unwrap();
let stream = board.start_stream().unwrap();
for sample in stream.into_iter().take(500) {
// sample.eeg has 16 µV values — one per channel.
let peak = sample.eeg.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
println!("sample {} — peak {:+.1} µV", sample.sample_num, peak);
}§Ganglion — 4 channels over Bluetooth LE
Requires the ble Cargo feature (enabled by default) and a Bluetooth
adapter.
#[cfg(feature = "ble")]
{
use openbci::board::ganglion::{GanglionBoard, GanglionConfig};
use openbci::board::Board;
use openbci::electrode::ElectrodeLayout;
let mut board = GanglionBoard::new(GanglionConfig::default())
.with_electrode_layout(ElectrodeLayout::from_labels(
&["Fp1", "Fp2", "C3", "C4"]
));
// Scans BLE for up to 10 seconds (configurable via GanglionConfig).
board.prepare().unwrap();
let stream = board.start_stream().unwrap();
for sample in stream.into_iter().take(400) {
println!("{:?}", sample.eeg);
}
board.release().unwrap();
}§Channel configuration
The Cyton’s ADS1299 amplifier is configurable per-channel. All
Cyton-family boards implement ConfigurableBoard:
use openbci::channel_config::{ChannelConfig, Gain, InputType};
use openbci::board::ConfigurableBoard;
// 24× gain, normal electrode input, included in bias, SRB2 reference.
let eeg = ChannelConfig::default()
.gain(Gain::X24)
.input_type(InputType::Normal)
.bias(true)
.srb2(true);
// Shorted input — measures the amplifier's intrinsic noise floor.
let noise = ChannelConfig::default()
.input_type(InputType::Shorted)
.bias(false)
.srb2(false);
board.apply_channel_config(0, &eeg).unwrap(); // channel 0 only
board.apply_channel_config(1, &noise).unwrap(); // channel 1 only
board.reset_to_defaults().unwrap(); // "d" — restore factory defaults§Electrode placement and montage
ElectrodeLayout maps channel indices to electrode metadata.
The electrode::positions module contains string constants for every
named site in the 10-05/10-10/10-20 systems, sourced from MNE-Python.
use openbci::electrode::{
Electrode, ElectrodeLayout, ElectrodePosition, SignalType,
positions, position, MONTAGE_1020, MONTAGE_1010, MONTAGE_1005,
};
// Quick construction from label strings.
let layout = ElectrodeLayout::from_labels(&[
positions::FP1, positions::FP2,
positions::C3, positions::CZ,
positions::C4, positions::P3,
positions::PZ, positions::P4,
]);
assert_eq!(layout.label(2), "C3");
assert_eq!(layout.label(7), "P4");
// Look up the 3-D head-surface position of an electrode (in metres).
let cz: ElectrodePosition = position("Cz").unwrap();
assert!(cz.z > 0.09); // Cz sits near the top of the head
// All 83 classic 10-20 sites, 176 10-10 sites, 334 10-05 sites.
println!("{} / {} / {} electrodes", MONTAGE_1020.len(), MONTAGE_1010.len(), MONTAGE_1005.len());§The Sample type
Sample is returned by every board:
| Field | Type | Meaning |
|---|---|---|
sample_num | u8 | Rolling counter from the board (0–255) |
eeg | Vec<f64> | µV per channel |
accel | Option<[f64;3]> | g (X, Y, Z) when available |
analog | Option<[f64;3]> | Raw analog pin readings |
resistance | Option<Vec<f64>> | Ω — Ganglion impedance mode |
timestamp | f64 | Seconds since UNIX epoch (host PC clock) |
end_byte | u8 | 0xC0 = accelerometer mode, 0xC1 = analog mode |
§Cargo features
| Feature | Default | Description |
|---|---|---|
ble | ✅ | Enables board::ganglion::GanglionBoard via btleplug + tokio |
Disable ble to avoid the heavy tokio/btleplug dependency tree:
[dependencies]
openbci = { version = "0.0.1", default-features = false }Re-exports§
pub use board::Board;pub use board::ConfigurableBoard;pub use channel_config::ChannelConfig;pub use channel_config::Gain;pub use channel_config::GainTracker;pub use channel_config::InputType;pub use electrode::Electrode;pub use electrode::ElectrodeLayout;pub use electrode::SignalType;pub use error::OpenBciError;pub use error::Result;pub use sample::Sample;pub use sample::StreamHandle;
Modules§
- board
- Board traits and all board-type implementations.
- channel_
config - Per-channel ADS1299 amplifier configuration.
- electrode
- Electrode placement, standard montages, and position lookup.
- error
- Error types returned by every board operation.
- packet
- Low-level packet decoding for all OpenBCI wire formats.
- sample
- The
Sampletype and theStreamHandlestreaming interface.