bmi160_rust_no_std_esp32 0.1.0

no_std BMI160 + BMP280 driver for ESP32 (Embassy + esp-hal)
#![no_std]  
use libm::pow;
use esp_hal::i2c::master::I2c;
use embassy_time::{Duration, Timer};



#[cfg(feature = "defmt")]
use defmt::Format;

/// Accelerometer data structure containing raw 16-bit axis values
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct AccelData {
    /// X-axis acceleration (raw ADC value)
    pub x: i16,
    /// Y-axis acceleration (raw ADC value)
    pub y: i16,
    /// Z-axis acceleration (raw ADC value)
    pub z: i16,
}

impl AccelData {
    /// Convert raw accelerometer data to g-force (±2g range)
    pub fn to_g(&self) -> (f32, f32, f32) {
        const SCALE: f32 = 1.0 / 16384.0; // ±2g range
        (
            self.x as f32 * SCALE,
            self.y as f32 * SCALE,
            self.z as f32 * SCALE,
        )
    }
}

/// Gyroscope data structure containing raw 16-bit rotation values
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct GyroData {
    /// X-axis rotation (raw ADC value)
    pub x: i16,
    /// Y-axis rotation (raw ADC value)
    pub y: i16,
    /// Z-axis rotation (raw ADC value)
    pub z: i16,
}

impl GyroData {
    /// Convert raw gyroscope data to degrees per second (±2000°/s range)
    pub fn to_dps(&self) -> (f32, f32, f32) {
        const SCALE: f32 = 1.0 / 131.0; // ±2000°/s range
        (
            self.x as f32 * SCALE,
            self.y as f32 * SCALE,
            self.z as f32 * SCALE,
        )
    }
}

/// Combined sensor data containing both accelerometer and gyroscope readings
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct SensorData {
    /// Accelerometer readings
    pub accel: AccelData,
    /// Gyroscope readings
    pub gyro: GyroData,
}

/// BMI160 Error types for sensor operations
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]  // Added this line!
pub enum Bmi160Error {
    /// I2C communication error
    I2cError,
    /// Wrong chip ID detected during initialization
    InvalidChipId(u8),
    /// Sensor not responding to commands
    NoResponse,
}



pub struct Bmi160 {
    i2c: I2c<'static, esp_hal::Async>,
    address: u8,
}

impl Bmi160 {
    // Register addresses
    const CHIP_ID_REG: u8 = 0x00;
    const CMD_REG: u8 = 0x7E;
    const ACCEL_DATA_REG: u8 = 0x12;
    const GYRO_DATA_REG: u8 = 0x0C;

    // Commands
    const SOFT_RESET_CMD: u8 = 0xB6;
    const ACCEL_NORMAL_MODE: u8 = 0x11;
    const GYRO_NORMAL_MODE: u8 = 0x15;

    // Constants
    const EXPECTED_CHIP_ID: u8 = 0xD1;

    pub const ADDRESS_LOW: u8 = 0x68;
    pub const ADDRESS_HIGH: u8 = 0x69;

    // new() has no async operations inside — no need for async
    pub fn new(i2c: I2c<'static, esp_hal::Async>, address: u8) -> Self {
        Self { i2c, address }
    }

    // new_default() calls new() which is sync now — also no need for async
    pub fn new_default(i2c: I2c<'static, esp_hal::Async>) -> Self {
        Self::new(i2c, Self::ADDRESS_LOW)
    }

    pub async fn init(&mut self) -> Result<(), Bmi160Error> {
        // Soft reset — was missing self.write_register(...)
        self.write_register(Self::CMD_REG, Self::SOFT_RESET_CMD).await?;
        Timer::after(Duration::from_millis(10)).await;

        // Wake up accelerometer
        self.write_register(Self::CMD_REG, Self::ACCEL_NORMAL_MODE).await?;
        Timer::after(Duration::from_millis(4)).await;

        // Wake up gyroscope
        self.write_register(Self::CMD_REG, Self::GYRO_NORMAL_MODE).await?;
        Timer::after(Duration::from_millis(80)).await;

        // Verify chip ID
        let chip_id = self.read_chip_id().await?;
        if chip_id == Self::EXPECTED_CHIP_ID {
            Ok(())
        } else {
            Err(Bmi160Error::InvalidChipId(chip_id))
        }
    }

    pub async fn read_chip_id(&mut self) -> Result<u8, Bmi160Error> {
        let mut buffer = [0u8; 1];
        self.i2c
            .write_read_async(self.address, &[Self::CHIP_ID_REG], &mut buffer)
            .await
            .map_err(|_| Bmi160Error::I2cError)?;
        Ok(buffer[0])
    }

    pub async fn read_accel(&mut self) -> Result<AccelData, Bmi160Error> {
        let mut buffer = [0u8; 6];
        self.i2c
            .write_read_async(self.address, &[Self::ACCEL_DATA_REG], &mut buffer)
            .await
            .map_err(|_| Bmi160Error::I2cError)?;

        Ok(AccelData {
            x: i16::from_le_bytes([buffer[0], buffer[1]]),
            y: i16::from_le_bytes([buffer[2], buffer[3]]),
            z: i16::from_le_bytes([buffer[4], buffer[5]]),
        })
    }

    pub async fn read_gyro(&mut self) -> Result<GyroData, Bmi160Error> {
        let mut buffer = [0u8; 6];
        self.i2c
            .write_read_async(self.address, &[Self::GYRO_DATA_REG], &mut buffer)
            .await
            .map_err(|_| Bmi160Error::I2cError)?;

        Ok(GyroData {
            x: i16::from_le_bytes([buffer[0], buffer[1]]),
            y: i16::from_le_bytes([buffer[2], buffer[3]]),
            z: i16::from_le_bytes([buffer[4], buffer[5]]),
        })
    }

    pub async fn read_all(&mut self) -> Result<SensorData, Bmi160Error> {
        let accel = self.read_accel().await?;
        let gyro = self.read_gyro().await?;
        Ok(SensorData { accel, gyro })
    }

    async fn write_register(&mut self, reg: u8, value: u8) -> Result<(), Bmi160Error> {
        self.i2c
            .write_async(self.address, &[reg, value])  // fixed typo: wrrite -> write
            .await
            .map_err(|_| Bmi160Error::I2cError)
    }
}