use crate::{addresses, Error, I2cDevice, Result};
use embedded_hal::i2c::I2c;
const LSM6DSOX_CTRL1_XL: u8 = 0x10;
const LSM6DSOX_CTRL2_G: u8 = 0x11;
const LSM6DSOX_CTRL3_C: u8 = 0x12;
const LSM6DSOX_STATUS_REG: u8 = 0x1E;
const LSM6DSOX_OUTX_L_G: u8 = 0x22;
const LSM6DSOX_OUTX_L_A: u8 = 0x28;
const LSM6DSOX_WHO_AM_I: u8 = 0x0F;
const LSM6DSOX_WHO_AM_I_VALUE: u8 = 0x6C;
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MovementValues {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl MovementValues {
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub fn magnitude(&self) -> f32 {
libm::sqrtf(self.x * self.x + self.y * self.y + self.z * self.z)
}
}
impl From<(f32, f32, f32)> for MovementValues {
fn from((x, y, z): (f32, f32, f32)) -> Self {
Self::new(x, y, z)
}
}
impl From<MovementValues> for (f32, f32, f32) {
fn from(v: MovementValues) -> Self {
(v.x, v.y, v.z)
}
}
pub struct Movement<I2C> {
device: I2cDevice<I2C>,
accel_sensitivity: f32,
gyro_sensitivity: f32,
}
impl<I2C, E> Movement<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C) -> Result<Self, E> {
Self::new_with_address(i2c, addresses::MOVEMENT[0])
}
pub fn discover(i2c: &mut I2C) -> Result<u8, E> {
let addresses = addresses::MOVEMENT;
for &addr in &addresses {
if i2c.write(addr, &[]).is_ok() {
return Ok(addr);
}
}
i2c.write(addresses[0], &[])
.map(|_| addresses[0])
.map_err(Error::I2c)
}
pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, E> {
let mut movement = Self {
device: I2cDevice::new(i2c, address),
accel_sensitivity: 0.061, gyro_sensitivity: 8.75, };
let who_am_i = movement.device.read_reg(LSM6DSOX_WHO_AM_I)?;
if who_am_i != LSM6DSOX_WHO_AM_I_VALUE {
return Err(Error::DeviceNotFound);
}
movement.init()?;
Ok(movement)
}
pub fn address(&self) -> u8 {
self.device.address
}
fn init(&mut self) -> Result<(), E> {
self.device.write_reg(LSM6DSOX_CTRL3_C, 0x01)?;
self.device.write_reg(LSM6DSOX_CTRL1_XL, 0x40)?;
self.accel_sensitivity = 0.061;
self.device.write_reg(LSM6DSOX_CTRL2_G, 0x40)?;
self.gyro_sensitivity = 8.75;
self.device.write_reg(LSM6DSOX_CTRL3_C, 0x44)?;
Ok(())
}
pub fn acceleration(&mut self) -> Result<MovementValues, E> {
let mut buf = [0u8; 6];
self.device.read_regs(LSM6DSOX_OUTX_L_A, &mut buf)?;
let x_raw = i16::from_le_bytes([buf[0], buf[1]]);
let y_raw = i16::from_le_bytes([buf[2], buf[3]]);
let z_raw = i16::from_le_bytes([buf[4], buf[5]]);
let scale = self.accel_sensitivity / 1000.0;
Ok(MovementValues {
x: x_raw as f32 * scale,
y: y_raw as f32 * scale,
z: z_raw as f32 * scale,
})
}
pub fn acceleration_magnitude(&mut self) -> Result<f32, E> {
Ok(self.acceleration()?.magnitude())
}
pub fn angular_velocity(&mut self) -> Result<MovementValues, E> {
let mut buf = [0u8; 6];
self.device.read_regs(LSM6DSOX_OUTX_L_G, &mut buf)?;
let x_raw = i16::from_le_bytes([buf[0], buf[1]]);
let y_raw = i16::from_le_bytes([buf[2], buf[3]]);
let z_raw = i16::from_le_bytes([buf[4], buf[5]]);
let scale = self.gyro_sensitivity / 1000.0;
Ok(MovementValues {
x: x_raw as f32 * scale,
y: y_raw as f32 * scale,
z: z_raw as f32 * scale,
})
}
pub fn gyro(&mut self) -> Result<MovementValues, E> {
self.angular_velocity()
}
pub fn data_ready(&mut self) -> Result<bool, E> {
let status = self.device.read_reg(LSM6DSOX_STATUS_REG)?;
Ok((status & 0x03) != 0)
}
pub fn release(self) -> I2C {
self.device.release()
}
}