#![allow(dead_code)]
extern crate byteorder;
extern crate docopt;
extern crate i2cdev;
#[cfg(any(target_os = "linux", target_os = "android"))]
use docopt::Docopt;
#[cfg(any(target_os = "linux", target_os = "android"))]
use sensors::{
adxl345_accelerometer::*, mpl115a2_barometer::*, Accelerometer, Barometer, Thermometer,
};
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::{env::args, thread, time::Duration};
#[cfg(any(target_os = "linux", target_os = "android"))]
use i2cdev::linux::*;
mod sensors {
use std::error::Error;
#[derive(Debug)]
pub struct AccelerometerSample {
pub x: f32,
pub y: f32,
pub z: f32,
}
pub trait Accelerometer {
type Error: Error;
fn accelerometer_sample(&mut self) -> Result<AccelerometerSample, Self::Error>;
}
pub trait Thermometer {
type Error: Error;
fn temperature_celsius(&mut self) -> Result<f32, Self::Error>;
}
pub trait Barometer {
type Error: Error;
fn pressure_kpa(&mut self) -> Result<f32, Self::Error>;
}
pub trait Altimeter {
type Error: Error;
fn altitude_meters(&mut self, sea_level_kpa: f32) -> Result<f32, Self::Error>;
}
impl<T> Altimeter for T
where
T: Barometer,
{
type Error = <Self as Barometer>::Error;
fn altitude_meters(&mut self, sea_level_kpa: f32) -> Result<f32, Self::Error> {
let pressure = self.pressure_kpa()? * 1000.;
let sea_level_pa = sea_level_kpa * 1000.;
let altitude = 44330. * (1. - (pressure / sea_level_pa).powf(0.1903));
Ok(altitude)
}
}
pub mod adxl345_accelerometer {
use super::*;
use byteorder::{ByteOrder, LittleEndian};
use i2cdev::core::I2CDevice;
pub const SLAVE_ADDR_PRIMARY: u16 = 0x1D;
pub const SLAVE_ADDR_ALT: u16 = 0x53;
const REGISTER_DEVID: u8 = 0x00;
const REGSITER_THRESH_TAP: u8 = 0x1D;
const REGISTER_OFSX: u8 = 0x1E;
const REGISTER_OFSY: u8 = 0x1F;
const REGISTER_OFSZ: u8 = 0x20;
const REGISTER_DUR: u8 = 0x21;
const REGISTER_LATENT: u8 = 0x22;
const REGISTER_WINDOW: u8 = 0x23;
const REGISTER_THRESH_ACT: u8 = 0x24;
const REGISTER_THRESH_INACT: u8 = 0x25;
const REGISTER_TIME_INACT: u8 = 0x26;
const REGISTER_ACT_INACT_CTL: u8 = 0x27;
const REGISTER_THRESH_FF: u8 = 0x28;
const REGISTER_TIME_FF: u8 = 0x29;
const REGISTER_TAP_AXES: u8 = 0x2A;
const REGISTER_ACT_TAP_STATUS: u8 = 0x2B;
const REGISTER_BW_RATE: u8 = 0x2C;
const REGISTER_POWER_CTL: u8 = 0x2D;
const REGISTER_INT_ENABLE: u8 = 0x2E;
const REGISTER_INT_MAP: u8 = 0x2F;
const REGISTER_INT_SOURCE: u8 = 0x30;
const REGISTER_DATA_FORMAT: u8 = 0x31;
const REGISTER_X0: u8 = 0x32;
const REGISTER_X1: u8 = 0x33;
const REGISTER_Y0: u8 = 0x34;
const REGISTER_Y1: u8 = 0x35;
const REGISTER_Z0: u8 = 0x36;
const REGISTER_Z1: u8 = 0x37;
const REGISTER_FIFO_CTL: u8 = 0x38;
const REGISTER_FIFO_STATUS: u8 = 0x39;
#[repr(u8)]
#[allow(non_camel_case_types)]
enum ADXL345DataRate {
RATE_3200HZ = 0x0F,
RATE_1600HZ = 0x0E,
RATE_800HZ = 0x0D,
RATE_400HZ = 0x0C,
RATE_200HZ = 0x0B,
RATE_100HZ = 0x0A,
RATE_50HZ = 0x09,
RATE_25HZ = 0x08,
RATE_12HZ5 = 0x07,
RATE_6HZ25 = 0x06,
}
pub struct ADXL345Accelerometer<T: I2CDevice + Sized> {
i2cdev: T,
}
impl<T> ADXL345Accelerometer<T>
where
T: I2CDevice + Sized,
{
pub fn new(mut i2cdev: T) -> Result<ADXL345Accelerometer<T>, T::Error> {
i2cdev.smbus_write_byte_data(REGISTER_POWER_CTL, 0x00)?;
i2cdev
.smbus_write_byte_data(REGISTER_BW_RATE, ADXL345DataRate::RATE_1600HZ as u8)?;
i2cdev.smbus_write_byte_data(REGISTER_DATA_FORMAT, 0x08)?;
i2cdev.smbus_write_byte_data(REGISTER_OFSX, 0xFD)?;
i2cdev.smbus_write_byte_data(REGISTER_OFSY, 0x03)?;
i2cdev.smbus_write_byte_data(REGISTER_OFSZ, 0xFE)?;
i2cdev.smbus_write_byte_data(REGISTER_POWER_CTL, 0x08)?;
Ok(ADXL345Accelerometer { i2cdev })
}
pub fn device_id(&mut self) -> Result<u8, T::Error> {
self.i2cdev.smbus_read_byte_data(REGISTER_DEVID)
}
}
const ACCEL_RANGE: f32 = 2.0; const ACCEL_BITS: u8 = 10;
impl<T> Accelerometer for ADXL345Accelerometer<T>
where
T: I2CDevice + Sized,
{
type Error = T::Error;
fn accelerometer_sample(&mut self) -> Result<AccelerometerSample, T::Error> {
let mut buf: [u8; 6] = [0u8; 6];
self.i2cdev.write(&[REGISTER_X0])?;
self.i2cdev.read(&mut buf)?;
let x: i16 = LittleEndian::read_i16(&[buf[0], buf[1]]);
let y: i16 = LittleEndian::read_i16(&[buf[2], buf[3]]);
let z: i16 = LittleEndian::read_i16(&[buf[4], buf[5]]);
Ok(AccelerometerSample {
x: (f32::from(x) / 1023.0) * (ACCEL_RANGE * 2.0),
y: (f32::from(y) / 1023.0) * (ACCEL_RANGE * 2.0),
z: (f32::from(z) / 1023.0) * (ACCEL_RANGE * 2.0),
})
}
}
}
pub mod mpl115a2_barometer {
use super::*;
use byteorder::{BigEndian, ByteOrder};
use i2cdev::core::I2CDevice;
use std::error::Error;
use std::thread;
use std::time::Duration;
pub const MPL115A2_I2C_ADDR: u16 = 0x60;
const REGISTER_ADDR_PADC: u8 = 0x00;
const REGISTER_ADDR_TADC: u8 = 0x02;
const REGISTER_ADDR_A0: u8 = 0x04; const REGISTER_ADDR_START_CONVERSION: u8 = 0x12;
pub struct MPL115A2BarometerThermometer<T: I2CDevice + Sized> {
pub i2cdev: T,
pub coeff: MPL115A2Coefficients,
}
#[derive(Debug)]
pub struct MPL115A2RawReading {
padc: u16, tadc: u16, }
#[derive(Debug)]
pub struct MPL115A2Coefficients {
a0: f32, b1: f32, b2: f32, c12: f32, }
fn calc_coefficient(
msb: u8,
lsb: u8,
integer_bits: i32,
fractional_bits: i32,
dec_pt_zero_pad: i32,
) -> f32 {
let extrabits = 16 - integer_bits - fractional_bits - 1;
let rawval: i16 = BigEndian::read_i16(&[msb, lsb]);
(f32::from(rawval) / 2_f32.powi(fractional_bits + extrabits))
/ 10_f32.powi(dec_pt_zero_pad)
}
impl MPL115A2Coefficients {
pub fn new<E: Error>(
i2cdev: &mut dyn I2CDevice<Error = E>,
) -> Result<MPL115A2Coefficients, E> {
let mut buf: [u8; 8] = [0; 8];
i2cdev.write(&[REGISTER_ADDR_A0])?;
i2cdev.read(&mut buf)?;
Ok(MPL115A2Coefficients {
a0: calc_coefficient(buf[0], buf[1], 12, 3, 0),
b1: calc_coefficient(buf[2], buf[3], 2, 13, 0),
b2: calc_coefficient(buf[4], buf[5], 1, 14, 0),
c12: calc_coefficient(buf[6], buf[7], 0, 13, 9),
})
}
}
impl MPL115A2RawReading {
pub fn new<E: Error>(
i2cdev: &mut dyn I2CDevice<Error = E>,
) -> Result<MPL115A2RawReading, E> {
i2cdev.smbus_write_byte_data(REGISTER_ADDR_START_CONVERSION, 0x00)?;
thread::sleep(Duration::from_millis(3));
let mut buf = [0_u8; 4];
i2cdev.write(&[REGISTER_ADDR_PADC])?;
i2cdev.read(&mut buf)?;
let padc: u16 = BigEndian::read_u16(&buf) >> 6;
let tadc: u16 = BigEndian::read_u16(&buf[2..]) >> 6;
Ok(MPL115A2RawReading { padc, tadc })
}
pub fn temperature_celsius(&self) -> f32 {
(f32::from(self.tadc) - 498.0) / -5.35 + 25.0
}
pub fn pressure_kpa(&self, coeff: &MPL115A2Coefficients) -> f32 {
let pcomp: f32 = coeff.a0
+ (coeff.b1 + coeff.c12 * f32::from(self.tadc)) * f32::from(self.padc)
+ (coeff.b2 * f32::from(self.tadc));
let pkpa: f32 = pcomp * ((115.0 - 50.0) / 1023.0) + 50.0;
pkpa
}
}
impl<T> MPL115A2BarometerThermometer<T>
where
T: I2CDevice + Sized,
{
pub fn new(mut i2cdev: T) -> Result<MPL115A2BarometerThermometer<T>, T::Error> {
let coeff = MPL115A2Coefficients::new(&mut i2cdev)?;
Ok(MPL115A2BarometerThermometer { i2cdev, coeff })
}
}
impl<T> Barometer for MPL115A2BarometerThermometer<T>
where
T: I2CDevice + Sized,
{
type Error = T::Error;
fn pressure_kpa(&mut self) -> Result<f32, T::Error> {
let reading = MPL115A2RawReading::new(&mut self.i2cdev)?;
Ok(reading.pressure_kpa(&self.coeff))
}
}
impl<T> Thermometer for MPL115A2BarometerThermometer<T>
where
T: I2CDevice + Sized,
{
type Error = T::Error;
fn temperature_celsius(&mut self) -> Result<f32, T::Error> {
let reading = MPL115A2RawReading::new(&mut self.i2cdev)?;
Ok(reading.temperature_celsius())
}
}
#[cfg(test)]
mod tests {
use super::calc_coefficient;
use super::*;
use mock::MockI2CDevice;
use sensors::*;
macro_rules! assert_almost_eq {
($left:expr, $right:expr) => {{
match (&($left), &($right)) {
(left_val, right_val) => {
if (*left_val - *right_val).abs() > 0.0001 {
panic!("assertion failed: ({:?} != {:?})", *left_val, *right_val);
}
}
}
}};
}
fn make_dev(mut i2cdev: MockI2CDevice) -> MPL115A2BarometerThermometer<MockI2CDevice> {
(&mut i2cdev.regmap).write_regs(
0x04,
&[
74, 98,
165, 150,
182, 106,
63, 232,
],
); MPL115A2BarometerThermometer::new(i2cdev).unwrap()
}
#[test]
fn test_calc_coefficient() {
assert_almost_eq!(calc_coefficient(0x00, 0b1000, 12, 3, 0), 1.0);
assert_almost_eq!(calc_coefficient(0xFF, 0xF8, 12, 3, 0), -1.0);
assert_almost_eq!(calc_coefficient(0x80, 0x00, 15, 0, 0), -32_768_f32);
assert_almost_eq!(calc_coefficient(0x00, 0x01, 15, 0, 10), 0.000_000_000_1);
}
#[test]
fn test_basic_pressure_read() {
let mut i2cdev = MockI2CDevice::new();
(&mut i2cdev.regmap).write_regs(0x00, &[0x6e, 0xc0, 0x81, 0x40]);
let mut dev = make_dev(i2cdev);
assert_almost_eq!(dev.pressure_kpa().unwrap(), 83.93877);
}
#[test]
fn test_basic_temp_read() {
let mut i2cdev = MockI2CDevice::new();
(&mut i2cdev.regmap).write_regs(0, &[0x6e, 0xc0, 0x81, 0x40]);
let mut dev = make_dev(i2cdev);
assert_almost_eq!(dev.temperature_celsius().unwrap(), 21.448599);
}
}
}
}
const USAGE: &str = "
Reading sensor data from a variety of sensors
Usage:
sensors <device>
sensors (-h | --help)
sensors --version
Options:
-h --help Show this help text.
--version Show version.
";
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn main() {}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn main() {
let args = Docopt::new(USAGE)
.and_then(|d| d.argv(args()).parse())
.unwrap_or_else(|e| e.exit());
let device = args.get_str("<device>");
let mpl115a2_i2cdev = LinuxI2CDevice::new(device, MPL115A2_I2C_ADDR).unwrap();
let adxl345_i2cdev = LinuxI2CDevice::new(device, 0x53).unwrap();
let mut mpl115a2 = MPL115A2BarometerThermometer::new(mpl115a2_i2cdev).unwrap();
let mut adxl345 = ADXL345Accelerometer::new(adxl345_i2cdev).unwrap();
println!("== ADXL345 ID: 0x{:X} ==", adxl345.device_id().unwrap());
loop {
let accel = adxl345.accelerometer_sample().unwrap();
println!(
"Temperature: {:?} C",
mpl115a2.temperature_celsius().unwrap()
);
println!("Pressure: {:?} kPa", mpl115a2.pressure_kpa().unwrap());
println!("Accel: {:?}", accel);
println!(
"Accel Tot: {:?}",
(accel.x.powi(2) + accel.y.powi(2) + accel.z.powi(2)).sqrt()
);
thread::sleep(Duration::from_millis(1000));
}
}