#![no_std]
#[macro_use]
extern crate num_derive;
use embedded_hal::i2c;
use paste::paste;
use types::LuxData;
use types::RawData;
mod fields;
mod macros;
mod registers;
mod types;
pub use crate::fields::*;
pub use crate::registers::*;
pub use crate::types::Error;
const LTR303_BASE_ADDRESS: u8 = 0x29;
create_struct_with! (LTR303Config, {mode: Mode, gain: Gain, integration_time: IntegrationTime, measurement_rate: MeasurementRate, int_thrsh_up: u16, int_thrsh_down: u16});
impl Default for LTR303Config {
fn default() -> Self {
LTR303Config {
mode: crate::Mode::STANDBY,
gain: crate::Gain::Gain1x,
integration_time: crate::IntegrationTime::Ms100,
measurement_rate: crate::MeasurementRate::Ms500,
int_thrsh_up: 0x0000,
int_thrsh_down: 0xFFFF,
}
}
}
pub struct LTR303<I2C> {
i2c: I2C,
gain: Gain,
integration_time: IntegrationTime,
}
impl<I2C, E> LTR303<I2C>
where
I2C: i2c::I2c<Error = E>,
{
pub fn init(i2c: I2C) -> Self {
LTR303 {
i2c,
gain: Gain::Gain1x,
integration_time: IntegrationTime::Ms100,
}
}
pub fn get_mfc_id(&mut self) -> Result<u8, Error<E>> {
self.read_register(Register::MANUFAC_ID)
}
pub fn get_part_id(&mut self) -> Result<u8, Error<E>> {
self.read_register(Register::PART_ID)
}
pub fn destroy(self) -> I2C {
self.i2c
}
pub fn start_measurement(&mut self, config: <R303Config) -> Result<(), Error<E>> {
self.gain = config.gain;
self.integration_time = config.integration_time;
let control_reg = ControlRegister::default()
.with_gain(config.gain)
.with_mode(Mode::ACTIVE);
let meas_rate_reg = MeasRateRegister::default()
.with_integration_time(config.integration_time)
.with_measurement_rate(config.measurement_rate);
self.write_register(Register::ALS_MEAS_RATE, meas_rate_reg.value())?;
self.write_register(
Register::ALS_THRES_LOW_0,
config.int_thrsh_down.to_be_bytes()[1],
)?;
self.write_register(
Register::ALS_THRES_LOW_1,
config.int_thrsh_down.to_be_bytes()[0],
)?;
self.write_register(
Register::ALS_THRES_UP_0,
config.int_thrsh_up.to_be_bytes()[1],
)?;
self.write_register(
Register::ALS_THRES_UP_1,
config.int_thrsh_up.to_be_bytes()[0],
)?;
self.write_register(Register::INTERRUPT, 0b00000010)?;
self.write_register(Register::ALS_CONTR, control_reg.value())?;
Ok(())
}
pub fn get_status(&mut self) -> Result<StatusRegister, Error<E>> {
let data = self.read_register(Register::ALS_STATUS)?;
let status_reg: StatusRegister = data.into();
Ok(status_reg)
}
pub fn data_ready(&mut self) -> Result<bool, Error<E>> {
let status = self.get_status()?;
Ok(status.data_status.value == DataStatus::New)
}
pub fn get_lux_data(&mut self) -> Result<LuxData, Error<E>> {
let raw_data = self.get_raw_data()?;
Ok(LuxData {
lux_raw: raw_data,
lux_phys: raw_to_lux(
raw_data.ch1_raw,
raw_data.ch0_raw,
self.gain,
self.integration_time,
),
})
}
pub fn standby(&mut self) -> Result<(), Error<E>> {
self.write_register(
Register::ALS_CONTR,
ControlRegister::default().with_mode(Mode::STANDBY).value(),
)?;
Ok(())
}
}
impl<I2C, E> LTR303<I2C>
where
I2C: i2c::I2c<Error = E>,
{
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
self.i2c
.write(LTR303_BASE_ADDRESS, &[register, data])
.map_err(Error::I2C)
.and(Ok(()))
}
fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
let mut data: [u8; 1] = [0];
self.i2c
.write_read(LTR303_BASE_ADDRESS, &[register], &mut data)
.map_err(Error::I2C)
.and(Ok(data[0]))
}
fn get_raw_data(&mut self) -> Result<RawData, Error<E>> {
let mut data: [u8; 4] = [0; 4];
self.i2c
.write_read(LTR303_BASE_ADDRESS, &[Register::ALS_DATA_CH1_0], &mut data)
.map_err(Error::I2C)?;
let ch1_raw = u16::from_le_bytes([data[0], data[1]]);
let ch0_raw = u16::from_le_bytes([data[2], data[3]]);
Ok(RawData { ch0_raw, ch1_raw })
}
}
fn raw_to_lux(ch1_data: u16, ch0_data: u16, gain: Gain, itime: IntegrationTime) -> f32 {
let ratio = ch1_data as f32 / (ch0_data as f32 + ch1_data as f32);
let als_gain: f32 = gain.into();
let int_time: f32 = itime.into();
if ratio < 0.45 {
((1.7743 * f32::from(ch0_data)) + (1.1059 * f32::from(ch1_data))) / als_gain / int_time
} else if (0.45..0.64).contains(&ratio) {
((4.2785 * f32::from(ch0_data)) - (1.9548 * f32::from(ch1_data))) / als_gain / int_time
} else if (0.64..0.85).contains(&ratio) {
((0.5926 * f32::from(ch0_data)) - (0.1185 * f32::from(ch1_data))) / als_gain / int_time
} else {
0.0
}
}
#[cfg(test)]
mod tests {
extern crate std;
use crate::{DataStatus, DataValidity, Gain, IntStatus};
use super::*;
use embedded_hal_mock::eh1::i2c;
const LTR303_ADDR: u8 = 0x29;
#[test]
fn manufacturer_info() {
let expectations = [i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::MANUFAC_ID],
std::vec![0x05],
)];
let mock = i2c::Mock::new(&expectations);
let mut ltr303 = LTR303::init(mock);
let mfc = ltr303.get_mfc_id().unwrap();
assert_eq!(0x05, mfc);
let mut mock = ltr303.destroy();
mock.done(); }
#[test]
fn part_id() {
let expectations = [i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::PART_ID],
std::vec![0xA0],
)];
let mock = i2c::Mock::new(&expectations);
let mut ltr303 = LTR303::init(mock);
let part_id = ltr303.get_part_id().unwrap();
assert_eq!(0xA0, part_id);
let mut mock = ltr303.destroy();
mock.done(); }
#[test]
fn sensor_status() {
let expectations = [i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::ALS_STATUS],
std::vec![0b11111010],
)];
let mock = i2c::Mock::new(&expectations);
let mut ltr303 = LTR303::init(mock);
let sensor_status = ltr303.get_status().unwrap();
assert_eq!(sensor_status.data_status.value, DataStatus::Old);
assert_eq!(sensor_status.data_valid.value, DataValidity::DataInvalid);
assert_eq!(sensor_status.gain.value, Gain::Gain96x);
assert_eq!(sensor_status.int_status.value, IntStatus::Active);
assert_eq!(sensor_status.value(), 0b11111000);
let mut mock = ltr303.destroy();
mock.done(); }
#[test]
fn start_measurement() {
let expectations = [
i2c::Transaction::write(
LTR303_ADDR,
std::vec![Register::ALS_MEAS_RATE, 0b00010101], ),
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_0, 0xFF]),
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_1, 0xFF]),
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_0, 0x00]),
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_1, 0x00]),
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::INTERRUPT, 0b00000010]),
i2c::Transaction::write(
LTR303_ADDR,
std::vec![Register::ALS_CONTR, 0b00000001], ),
];
let mock = i2c::Mock::new(&expectations);
let config = LTR303Config {
integration_time: crate::IntegrationTime::Ms200,
measurement_rate: crate::MeasurementRate::Ms2000,
..Default::default()
};
let mut ltr303 = LTR303::init(mock);
ltr303.start_measurement(&config).unwrap();
let mut mock = ltr303.destroy();
mock.done(); }
#[test]
fn single_shot_measurement() {
let expectations = [
i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_MEAS_RATE, 0b0001_1101]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_0, 0xFF]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_1, 0xFF]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_0, 0x00]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_1, 0x00]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::INTERRUPT, 0b00000010]), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_CONTR, 0b0000_1001]), i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::ALS_STATUS],
std::vec![0b1010_0000],
), i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::ALS_STATUS],
std::vec![0b0010_0100],
), i2c::Transaction::write_read(
LTR303_ADDR,
std::vec![Register::ALS_DATA_CH1_0],
std::vec![0xAD, 0xDE, 0xEF, 0xBE],
), i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_CONTR, 0b0000_0000]), ];
let mock = i2c::Mock::new(&expectations);
let config = crate::LTR303Config::default()
.with_integration_time(crate::IntegrationTime::Ms400)
.with_measurement_rate(crate::MeasurementRate::Ms2000)
.with_gain(crate::Gain::Gain4x);
let mut ltr303 = LTR303::init(mock);
ltr303.start_measurement(&config).unwrap();
while ltr303.get_status().unwrap().data_status.value != DataStatus::New {}
let lux_data = ltr303.get_lux_data().unwrap();
ltr303.standby().unwrap();
assert_eq!(lux_data.lux_raw.ch1_raw, 0xDEAD);
assert_eq!(lux_data.lux_raw.ch0_raw, 0xBEEF);
let mut mock = ltr303.destroy();
mock.done(); }
#[cfg(test)]
mod unit_tests {
use crate::{
raw_to_lux, ControlRegister, Field, Gain, IntegrationTime, MeasRateRegister,
MeasurementRate, Mode, ResetStatus,
};
#[test]
fn calculate_lux_from_raw() {
let ch0_data: u16 = 0x0000;
let ch1_data: u16 = 0xFFFF;
let ltr303_config = crate::LTR303Config::default();
let lux = raw_to_lux(
ch1_data,
ch0_data,
ltr303_config.gain,
ltr303_config.integration_time,
);
assert_eq!(lux, 0.0);
let ch0_data: u16 = 0x1000;
let ch1_data: u16 = 0x1000;
let lux = raw_to_lux(
ch1_data,
ch0_data,
ltr303_config.gain,
ltr303_config.integration_time,
);
assert_eq!(lux, 9517.875);
}
#[test]
fn test_registers() {
let control_reg = ControlRegister::default()
.with_mode(Mode::STANDBY)
.with_gain(Gain::Gain8x);
assert_eq!(control_reg.gain.value, Gain::Gain8x);
assert_eq!(control_reg.value(), 0b0000_1100);
let measrate_reg = MeasRateRegister::default()
.with_integration_time(IntegrationTime::Ms200)
.with_measurement_rate(MeasurementRate::Ms2000);
assert_eq!(measrate_reg.integration_time.value, IntegrationTime::Ms200);
assert_eq!(measrate_reg.value(), 0b0001_0101);
}
#[test]
fn test_register_from_u8() {
let contr_reg_val: u8 = 0b0000_1011;
let control_reg: ControlRegister = contr_reg_val.into();
assert_eq!(control_reg.gain.value, Gain::Gain4x);
assert_eq!(control_reg.sw_reset.value, ResetStatus::Resetting);
assert_eq!(control_reg.mode.value, Mode::ACTIVE);
}
#[test]
fn test_fields() {
let field1 = Field {
start_index: 1,
width: 4,
value: 0x0Bu8,
};
assert_eq!(field1.bits(), 0b0001_0110)
}
}
}