mendi 0.0.2

Rust client for the Mendi neurofeedback headband over BLE using btleplug
Documentation
//! # mendi
//!
//! Async Rust library for streaming fNIRS neurofeedback data from
//! [Mendi](https://www.mendi.io/) headbands over Bluetooth Low Energy.
//!
//! ## Hardware
//!
//! The Mendi headband is a consumer fNIRS (functional near-infrared spectroscopy)
//! device that measures blood oxygenation in the prefrontal cortex. It communicates
//! over BLE using protobuf-encoded messages on six GATT characteristics:
//!
//! | UUID   | Characteristic | Data |
//! |--------|---------------|------|
//! | 0xABB1 | Frame         | IMU + temperature + 3 optical channels (IR/red/ambient) |
//! | 0xABB2 | Sensor        | Optical sensor register read/write |
//! | 0xABB3 | IMU           | IMU register read/write |
//! | 0xABB4 | ADC           | Battery voltage, charging, USB status |
//! | 0xABB5 | Diagnostics   | Self-test results at power-on |
//! | 0xABB6 | Calibration   | LED current offsets, auto-cal, low-power mode |
//!
//! ## Quick start
//!
//! ```no_run
//! use mendi::prelude::*;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//!     let client = MendiClient::new(MendiClientConfig::default());
//!     let (mut rx, handle) = client.connect().await?;
//!
//!     while let Some(event) = rx.recv().await {
//!         match event {
//!             MendiEvent::Frame(f) => {
//!                 println!("temp={:.1}°C  IR: L={} R={} P={}",
//!                     f.temperature, f.ir_left, f.ir_right, f.ir_pulse);
//!             }
//!             MendiEvent::Battery(b) => {
//!                 println!("Battery: {:.2}V charging={}", b.voltage(), b.charging);
//!             }
//!             MendiEvent::Disconnected => break,
//!             _ => {}
//!         }
//!     }
//!     Ok(())
//! }
//! ```
//!
//! ## Module overview
//!
//! | Module | Purpose |
//! |--------|---------|
//! | [`mendi_client`] | BLE scanning, connecting, and the [`mendi_client::MendiHandle`] API |
//! | [`types`] | All event and data types produced by the client |
//! | [`protocol`] | GATT UUIDs and BLE constants |
//! | [`parse`] | Protobuf decoders for BLE notification payloads |
//! | [`wire`] | Generated protobuf types from `device_v4.proto` |
//!
//! ## Protobuf code generation
//!
//! The [`wire`] module contains Rust types generated from `proto/device_v4.proto`
//! using [`prost`]. By default, **pre-generated code** is bundled in
//! `src/wire_generated.rs` so you do **not** need `protoc` installed.
//!
//! To regenerate from the `.proto` file (e.g. after modifying it), enable the
//! `regenerate-proto` feature:
//!
//! ```bash
//! cargo build --features regenerate-proto
//! ```
//!
//! This requires the `protoc` compiler to be available on your `PATH`.
//! After regenerating, copy the output to `src/wire_generated.rs` to update
//! the bundled version:
//!
//! ```bash
//! cp target/debug/build/mendi-*/out/mendi.rs src/wire_generated.rs
//! ```
//!
//! ## Feature flags
//!
//! | Feature | Description |
//! |---------|-------------|
//! | `simulate` | Simulated + mock devices for testing without hardware |
//! | `tui` | Terminal UI with real-time charts (implies `simulate`) |
//! | `regenerate-proto` | Rebuild protobuf types from `.proto` (requires `protoc`) |

pub mod mendi_client;
pub mod parse;
pub mod protocol;
pub mod types;

/// Simulated and mock Mendi devices for testing without hardware.
///
/// Requires `--features simulate`.
///
/// - [`simulate::SimulatedDevice`] — generates realistic fNIRS data with
///   sinusoidal optical signals, IMU noise, and periodic battery/calibration.
/// - [`simulate::MockDevice`] — deterministic scripted mock for unit tests.
#[cfg(feature = "simulate")]
pub mod simulate;

/// Generated protobuf types from the Mendi V4 wire protocol.
///
/// By default the pre-generated code in `src/wire_generated.rs` is used.
/// Enable the `regenerate-proto` feature (requires `protoc`) to rebuild from
/// `proto/device_v4.proto`.
pub mod wire {
    #[cfg(feature = "regenerate-proto")]
    include!(concat!(env!("OUT_DIR"), "/mendi.rs"));

    #[cfg(not(feature = "regenerate-proto"))]
    include!("wire_generated.rs");
}

/// Convenience re-exports.
///
/// ```no_run
/// use mendi::prelude::*;
/// ```
pub mod prelude {
    pub use crate::mendi_client::{MendiClient, MendiClientConfig, MendiDevice, MendiHandle};
    pub use crate::types::{
        BatteryReading, CalibrationReading, DeviceInfo, DiagnosticsReading, FrameReading,
        MendiEvent, SensorReading, ACCEL_SCALE, GYRO_SCALE, MENDI_FCC_ID,
    };
    pub use crate::protocol::{
        mendi_uuid, FRAME_CHARACTERISTIC, ADC_CHARACTERISTIC, CALIBRATION_CHARACTERISTIC,
        DIAGNOSTICS_CHARACTERISTIC, SENSOR_CHARACTERISTIC, IMU_CHARACTERISTIC,
        MENDI_SERVICE_UUID, OPTICAL_CHANNEL_NAMES,
    };
}