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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
//! MLX90614-specific functions
use crate::{
ic,
register_access::mlx90614::{self, Register, DEV_ADDR},
Error, Mlx9061x, SlaveAddr,
};
use core::marker::PhantomData;
use embedded_hal::{delay::DelayNs, digital::OutputPin, i2c::I2c};
impl<E, I2C> Mlx9061x<I2C, ic::Mlx90614>
where
I2C: I2c<Error = E>,
{
/// Create new instance of the MLX90614 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_mlx90614(
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,
})
}
/// 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 1 temperature in celsius degrees
pub fn object1_temperature(&mut self) -> Result<f32, Error<E>> {
let t = self.read_u16(Register::TOBJ1)?;
let t = f32::from(t) * 0.02 - 273.15;
Ok(t)
}
/// Read the object 1 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 `object1_temperature()`.
pub fn object1_temperature_as_int(&mut self) -> Result<u16, Error<E>> {
let t = self.read_u16(Register::TOBJ1)?;
let t = (t * 2) / 100 - 273;
Ok(t)
}
/// Read the object 2 temperature in celsius degrees
///
/// Note that this is only available in dual-zone thermopile device variants.
pub fn object2_temperature(&mut self) -> Result<f32, Error<E>> {
let t = self.read_u16(Register::TOBJ2)?;
let t = f32::from(t) * 0.02 - 273.15;
Ok(t)
}
/// Read the object 2 temperature in celsius degrees as u16 value
///
/// Note that this is only available in dual-zone thermopile device variants.
///
/// Note ONLY use to avoid floating-point ops, as this gives less accurate
/// temperature readings compared to using `object2_temperature()`.
pub fn object2_temperature_as_int(&mut self) -> Result<u16, Error<E>> {
let t = self.read_u16(Register::TOBJ2)?;
let t = (t * 2) / 100 - 273;
Ok(t)
}
/// Read the channel 1 raw IR data
pub fn raw_ir_channel1(&mut self) -> Result<u16, Error<E>> {
self.read_u16(Register::RAW_IR1)
}
/// Read the channel 2 raw IR data
pub fn raw_ir_channel2(&mut self) -> Result<u16, Error<E>> {
self.read_u16(Register::RAW_IR2)
}
/// Get emissivity epsilon
pub fn emissivity(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_u16(Register::EMISSIVITY)?;
Ok(f32::from(raw) / 65535.0)
}
/// Set emissivity epsilon [0.1-1.0]
///
/// Wrong values will return `Error::InvalidInputData`.
pub fn set_emissivity<D: DelayNs>(
&mut self,
epsilon: f32,
delay: &mut D,
) -> Result<(), Error<E>> {
if epsilon < 0.1 || epsilon > 1.0 {
return Err(Error::InvalidInputData);
}
let eps = (epsilon * 65535.0 + 0.5) as u16;
if eps < 6553 {
return Err(Error::InvalidInputData);
}
self.write_u16_eeprom(Register::EMISSIVITY, eps, delay)
}
/// Get the device ID
pub fn device_id(&mut self) -> Result<u64, Error<E>> {
let mut id = 0;
for i in 0..4 {
let part = self.read_u16(Register::ID0 + i)?;
let part = u64::from(part) << (16 * (3 - i));
id |= part;
}
Ok(id)
}
}
/// Wake device from sleep mode.
///
/// Note that this includes a 33ms delay.
pub fn wake_mlx90614<E, SclPin: OutputPin<Error = E>, SdaPin: OutputPin<Error = E>, D: DelayNs>(
scl: &mut SclPin,
sda: &mut SdaPin,
delay: &mut D,
) -> Result<(), E> {
scl.set_high()?;
sda.set_low()?;
delay.delay_ms(u32::from(mlx90614::WAKE_DELAY_MS));
sda.set_high()
}