#![allow(dead_code)]
use sensors::{Thermometer, Barometer};
use std::thread;
use std::time::Duration;
use std::error::Error;
use core::I2CDevice;
use byteorder::{ByteOrder, BigEndian};
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]);
let adj = (rawval as f32 / 2_f32.powi(fractional_bits + extrabits)) /
10_f32.powi(dec_pt_zero_pad);
adj
}
impl MPL115A2Coefficients {
pub fn new<E: Error>(i2cdev: &mut I2CDevice<Error=E>) -> Result<MPL115A2Coefficients, E> {
let mut buf: [u8; 8] = [0; 8];
try!(i2cdev.write(&[REGISTER_ADDR_A0]));
try!(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 I2CDevice<Error=E>) -> Result<MPL115A2RawReading, E> {
try!(i2cdev.smbus_write_byte_data(REGISTER_ADDR_START_CONVERSION, 0x00));
thread::sleep(Duration::from_millis(3));
let mut buf = [0_u8; 4];
try!(i2cdev.write(&[REGISTER_ADDR_PADC]));
try!(i2cdev.read(&mut buf));
let padc: u16 = BigEndian::read_u16(&buf) >> 6;
let tadc: u16 = BigEndian::read_u16(&buf[2..]) >> 6;
Ok(MPL115A2RawReading {
padc: padc,
tadc: tadc,
})
}
pub fn temperature_celsius(&self) -> f32 {
(self.tadc as f32 - 498.0) / -5.35 + 25.0
}
pub fn pressure_kpa(&self, coeff: &MPL115A2Coefficients) -> f32 {
let pcomp: f32 = coeff.a0 + (coeff.b1 + coeff.c12 * self.tadc as f32) * self.padc as f32 +
(coeff.b2 * self.tadc as f32);
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 = try!(MPL115A2Coefficients::new(&mut i2cdev));
Ok(MPL115A2BarometerThermometer {
i2cdev: i2cdev,
coeff: 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 = try!(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 = try!(MPL115A2RawReading::new(&mut self.i2cdev));
Ok(reading.temperature_celsius())
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::calc_coefficient;
use sensors::*;
use mock::MockI2CDevice;
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);
}
}