use anyhow::Result;
use log::info;
#[cfg(feature = "raw")]
fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let args: Vec<String> = std::env::args().collect();
let list_mode = args.contains(&"--list".to_string());
let device_serial = args
.windows(2)
.find_map(|w| {
if w[0] == "--device" {
Some(w[1].clone())
} else {
None
}
});
#[tokio::main]
async fn run_client(list_mode: bool, device_serial: Option<String>) -> Result<()> {
use emotiv::raw;
if list_mode {
list_devices().await?;
} else {
stream_from_device(device_serial).await?;
}
Ok(())
}
#[tokio::main]
async fn list_devices() -> Result<()> {
use emotiv::raw;
info!("๐ Discovering Emotiv devices...\n");
let devices = raw::discover_devices().await?;
if devices.is_empty() {
info!("No devices found.");
return Ok(());
}
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!("โ Found {} device(s):", devices.len());
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค");
for (i, device) in devices.iter().enumerate() {
let transport = format!("{}", device.transport);
let channels = device.model.channel_count();
println!(
"โ #{} โ {} โ {} channels โ {} โ {}% battery",
i + 1,
device.model,
channels,
transport,
device.battery_percent
);
println!("โ โโ {}", device.serial);
}
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
Ok(())
}
#[tokio::main]
async fn stream_from_device(device_serial: Option<String>) -> Result<()> {
use emotiv::raw;
info!("๐ Discovering devices...");
let devices = raw::discover_devices().await?;
if devices.is_empty() {
log::error!("โ No devices found!");
return Err(anyhow::anyhow!("No devices found"));
}
let device = if let Some(serial) = device_serial {
devices
.iter()
.find(|d| d.serial.contains(&serial))
.ok_or_else(|| anyhow::anyhow!("Device not found: {}", serial))?
} else {
&devices[0]
};
info!(
"๐ฏ Connecting to {} ({}) via {}",
device.model, device.serial, device.transport
);
let (mut rx, _handle) = raw::RawDevice::from_info(device.clone()).connect().await?;
info!("โ
Connected! Streaming {}ch @ {} Hz\n", device.model.channel_count(), device.model.sampling_rate());
info!(
"Received โ Rate (Hz) โ Signal Quality โ Battery โ EEG (ยตV)\n\
โโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"
);
let start = std::time::Instant::now();
let mut packet_count = 0u64;
let mut first_batch = true;
while let Some(data) = rx.recv().await {
packet_count += 1;
if packet_count % 32 == 0 {
let elapsed = start.elapsed().as_secs_f64();
let rate = (packet_count as f64) / elapsed;
let signal_symbol = match (data.signal_quality) {
4 => "โโโโ Excellent",
3 => "โโโโ Good",
2 => "โโโโ Fair",
1 => "โโโโ Poor",
_ => "โโโโ None",
};
let mut eeg_display = String::new();
for (i, &uv) in data.eeg_uv.iter().take(3).enumerate() {
if i > 0 {
eeg_display.push_str(", ");
}
eeg_display.push_str(&format!("{: 8.1}", uv));
}
println!(
" {pkt:6} โ {rate:9.2} โ {signal_symbol:<14} โ {bat:>3}% โ {eeg}",
pkt = packet_count,
rate = rate,
signal_symbol = signal_symbol,
bat = data.battery_percent,
eeg = eeg_display
);
if first_batch && packet_count == 32 {
first_batch = false;
info!("(Streaming {} samples per second)", (rate / device.model.sampling_rate() as f64) as u32);
}
}
if data.signal_quality < 1 {
log::warn!("โ ๏ธ Signal lost or very poor! Re-check electrode placement.");
}
}
let elapsed = start.elapsed().as_secs_f64();
let avg_rate = packet_count as f64 / elapsed;
info!(
"\nโ Streamed {} packets in {:.1}s ({:.1} Hz)",
packet_count, elapsed, avg_rate
);
Ok(())
}
run_client(list_mode, device_serial)
}
#[cfg(not(feature = "raw"))]
fn main() {
eprintln!("Error: This example requires the `raw` feature.");
eprintln!(" cargo run --example raw_stream --features raw");
std::process::exit(1);
}