1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::{
    ic,
    register_access::mlx90615::{self, Register, DEV_ADDR},
    Error, Mlx9061x, SlaveAddr,
};
use core::marker::PhantomData;
use embedded_hal::{
    blocking::{delay::DelayMs, i2c},
    digital::v2::OutputPin,
};

impl<E, I2C> Mlx9061x<I2C, ic::Mlx90615>
where
    I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
{
    /// Create new instance of the MLX90615 device.
    ///
    /// The slave address must match the address stored in the device EEPROM.
    /// To change it you need to connect first and then change it with `set_address()`.
    /// An invalid alternative slave address will return `Error::InvalidInputData`.
    ///
    /// When writing to the EEPROM waiting a certain amount of time is necessary.
    /// This delay is configured through the `eeprom_write_delay_ms` parameter
    /// in milliseconds.
    pub fn new_mlx90615(
        i2c: I2C,
        address: SlaveAddr,
        eeprom_write_delay_ms: u8,
    ) -> Result<Self, Error<E>> {
        let address = Self::get_address(address, DEV_ADDR)?;
        Ok(Mlx9061x {
            i2c,
            eeprom_write_delay_ms,
            address,
            _ic: PhantomData,
        })
    }
}

impl<E, I2C> Mlx9061x<I2C, ic::Mlx90615>
where
    I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
{
    /// Read the ambient temperature in celsius degrees
    pub fn ambient_temperature(&mut self) -> Result<f32, Error<E>> {
        let t = self.read_u16(Register::TA)?;
        let t = f32::from(t) * 0.02 - 273.15;
        Ok(t)
    }

    /// Read the ambient temperature in celsius degrees as u16 value
    ///
    /// Note ONLY use to avoid floating-point ops, as this gives less accurate
    /// temperature readings compared to using `ambient_temperature()`.
    pub fn ambient_temperature_as_int(&mut self) -> Result<u16, Error<E>> {
        let t = self.read_u16(Register::TA)?;
        let t = (t * 2) / 100 - 273;
        Ok(t)
    }

    /// Read the object temperature in celsius degrees
    pub fn object_temperature(&mut self) -> Result<f32, Error<E>> {
        let t = self.read_u16(Register::TOBJ)?;
        let t = f32::from(t) * 0.02 - 273.15;
        Ok(t)
    }

    /// Read the object temperature in celsius degrees
    ///
    /// Note ONLY use to avoid floating-point ops, as this gives less accurate
    /// temperature readings compared to using `object_temperature()`.
    pub fn object_temperature_as_int(&mut self) -> Result<u16, Error<E>> {
        let t = self.read_u16(Register::TOBJ)?;
        let t = (t * 2) / 100 - 273;
        Ok(t)
    }

    /// Read the raw IR data
    pub fn raw_ir(&mut self) -> Result<u16, Error<E>> {
        self.read_u16(Register::RAW_IR)
    }

    /// Get emissivity epsilon
    pub fn emissivity(&mut self) -> Result<f32, Error<E>> {
        let raw = self.read_u16(Register::EMISSIVITY)?;
        Ok(f32::from(raw) / 16384.0)
    }

    /// Set emissivity epsilon [0.0-1.0]
    ///
    /// Wrong values will return `Error::InvalidInputData`.
    pub fn set_emissivity<D: DelayMs<u8>>(
        &mut self,
        epsilon: f32,
        delay: &mut D,
    ) -> Result<(), Error<E>> {
        if epsilon < 0.0 || epsilon > 1.0 {
            return Err(Error::InvalidInputData);
        }
        let eps = (epsilon * 16384.0 + 0.5) as u16;
        self.write_u16_eeprom(Register::EMISSIVITY, eps, delay)
    }

    /// Get the device ID
    pub fn device_id(&mut self) -> Result<u32, Error<E>> {
        let id0 = self.read_u16(Register::ID0)?;
        let id1 = self.read_u16(Register::ID0 + 1)?;
        Ok((u32::from(id0) << 16) | u32::from(id1))
    }
}

/// Wake device from sleep mode.
///
/// Note that this includes a 39ms delay.
pub fn wake_mlx90615<E, P: OutputPin<Error = E>, D: DelayMs<u8>>(
    scl: &mut P,
    delay: &mut D,
) -> Result<(), E> {
    scl.set_low()?;
    delay.delay_ms(mlx90615::WAKE_DELAY_MS);
    scl.set_high()
}