mlx9061x 0.2.0

Platform-agnostic Rust driver for the MLX90614 and MLX90615 non-contact infrared thermometers.
Documentation
use crate::{Error, Mlx9061x, SlaveAddr};
use embedded_hal::blocking::{delay, i2c};
use smbus_pec::pec;

pub mod mlx90614 {
    const EEPROM_COMMAND: u8 = 0x20;
    pub const SLEEP_COMMAND: u8 = 0xFF;
    pub const WAKE_DELAY_MS: u8 = 33;
    pub const DEV_ADDR: u8 = 0x5A;
    pub struct Register {}
    impl Register {
        pub const RAW_IR1: u8 = 0x04;
        pub const RAW_IR2: u8 = 0x05;
        pub const TA: u8 = 0x06;
        pub const TOBJ1: u8 = 0x07;
        pub const TOBJ2: u8 = 0x08;
        pub const EMISSIVITY: u8 = 0x04 | EEPROM_COMMAND;
        pub const ADDRESS: u8 = 0x0E | EEPROM_COMMAND;
        pub const ID0: u8 = 0x1C | EEPROM_COMMAND;
    }
}

pub mod mlx90615 {
    const EEPROM_COMMAND: u8 = 0x10;
    const RAM_COMMAND: u8 = 0x20;
    pub const SLEEP_COMMAND: u8 = 0xC6;
    pub const WAKE_DELAY_MS: u8 = 39;
    pub const DEV_ADDR: u8 = 0x5B;
    pub struct Register {}
    impl Register {
        pub const RAW_IR: u8 = 0x05 | RAM_COMMAND;
        pub const TA: u8 = 0x06 | RAM_COMMAND;
        pub const TOBJ: u8 = 0x07 | RAM_COMMAND;
        pub const ADDRESS: u8 = /*0x00 |*/ EEPROM_COMMAND;
        pub const EMISSIVITY: u8 = 0x03 | EEPROM_COMMAND;
        pub const ID0: u8 = 0x0E | EEPROM_COMMAND;
    }
}

impl<E, I2C, IC> Mlx9061x<I2C, IC>
where
    I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
{
    pub(crate) fn read_u16(&mut self, register: u8) -> Result<u16, Error<E>> {
        let mut data = [0; 3];
        self.i2c
            .write_read(self.address, &[register], &mut data)
            .map_err(Error::I2C)?;
        let pec = data[2];
        Self::check_pec(
            &[
                self.address << 1,
                register,
                (self.address << 1) + 1,
                data[0],
                data[1],
            ],
            pec,
        )?;
        Ok(u16::from(data[0]) | (u16::from(data[1]) << 8))
    }

    pub(crate) fn write_u8(&mut self, command: u8) -> Result<(), Error<E>> {
        let pec = pec(&[self.address << 1, command]);
        self.i2c
            .write(self.address, &[command, pec])
            .map_err(Error::I2C)
    }

    pub(crate) fn write_u16(&mut self, command: u8, data: u16) -> Result<(), Error<E>> {
        let low = data as u8;
        let high = (data >> 8) as u8;
        let pec = pec(&[self.address << 1, command, low, high]);
        self.i2c
            .write(self.address, &[command, low, high, pec])
            .map_err(Error::I2C)
    }

    pub(crate) fn write_u16_eeprom<D: delay::DelayMs<u8>>(
        &mut self,
        command: u8,
        data: u16,
        delay: &mut D,
    ) -> Result<(), Error<E>> {
        self.write_u16(command, 0)?;
        delay.delay_ms(self.eeprom_write_delay_ms);
        self.write_u16(command, data)
    }

    pub(crate) fn check_pec(data: &[u8], expected: u8) -> Result<(), Error<E>> {
        if pec(data) != expected {
            Err(Error::ChecksumMismatch)
        } else {
            Ok(())
        }
    }

    pub(crate) fn get_address(address: SlaveAddr, default: u8) -> Result<u8, Error<E>> {
        match address {
            SlaveAddr::Default => Ok(default),
            SlaveAddr::Alternative(a) if a == 0 => Err(Error::InvalidInputData),
            SlaveAddr::Alternative(a) if a > 127 => Err(Error::InvalidInputData),
            SlaveAddr::Alternative(a) => Ok(a),
        }
    }
}