ltr303/
lib.rs

1//! # Introduction
2//! This is a platform-agnostic Rust driver for the [`LTR-303 Ambient Light Sensor`](https://optoelectronics.liteon.com/en-global/Led/LED-Component/Detail/926/0/0/16/200) using [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.
3//!
4//! ## Supported devices
5//! Tested with the following sensor(s):
6//! - [LTR-303ALS-01](https://www.mouser.com/datasheet/2/239/Lite-On_LTR-303ALS-01_DS_ver%201.1-1175269.pdf)
7//!
8//! ## Usage
9//! ### Setup
10//!
11//! Instantiate a new driver instance using a [blocking I²C HAL
12//! implementation](https://docs.rs/embedded-hal/0.2.*/embedded_hal/blocking/i2c/index.html).
13//! For example, using `linux-embedded-hal` and an LTR303 sensor:
14//! ```no_run
15//! use linux_embedded_hal::{I2cdev};
16//! use ltr303;
17//!
18//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
19//! let mut sensor = ltr303::LTR303::init(dev);
20//! let config = ltr303::LTR303Config::default();
21//! ```
22//!
23//! ### Device Info
24//!
25//! Then, you can query information about the sensor:
26//!
27//! ```no_run
28//! # use linux_embedded_hal::{I2cdev};
29//! # use ltr303;
30//! # let dev = I2cdev::new("/dev/i2c-1").unwrap();
31//! # let mut sensor = ltr303::LTR303::init(dev);
32//! let part_id = sensor.get_part_id().unwrap();
33//! let mfc_id = sensor.get_mfc_id().unwrap();
34//! ```
35//!
36//! ### Measurements
37//!
38//! For measuring the illuminance, simply start a measurement and wait for a result:
39//! ```no_run
40//! use linux_embedded_hal::{Delay, I2cdev};
41//! # use ltr303;
42//! # let dev = I2cdev::new("/dev/i2c-1").unwrap();
43//! # let mut sensor = ltr303::LTR303::init(dev);
44//! let config = ltr303::LTR303Config::default();
45//! sensor.start_measurement(&config).unwrap();
46//! while sensor.data_ready().unwrap() != true {}
47//!
48//! let lux_val = sensor.get_lux_data().unwrap();
49//! println!("LTR303 current lux phys: {}", lux_val.lux_phys);
50//! ```
51//!
52#![no_std]
53#[macro_use]
54extern crate num_derive;
55use embedded_hal::i2c;
56use paste::paste;
57use types::LuxData;
58use types::RawData;
59
60mod fields;
61mod macros;
62mod registers;
63mod types;
64pub use crate::fields::*;
65pub use crate::registers::*;
66pub use crate::types::Error;
67
68const LTR303_BASE_ADDRESS: u8 = 0x29;
69
70create_struct_with! (LTR303Config, {mode: Mode, gain: Gain, integration_time: IntegrationTime, measurement_rate: MeasurementRate, int_thrsh_up: u16, int_thrsh_down: u16});
71
72impl Default for LTR303Config {
73    fn default() -> Self {
74        LTR303Config {
75            mode: crate::Mode::STANDBY,
76            gain: crate::Gain::Gain1x,
77            integration_time: crate::IntegrationTime::Ms100,
78            measurement_rate: crate::MeasurementRate::Ms500,
79            int_thrsh_up: 0x0000,
80            int_thrsh_down: 0xFFFF,
81        }
82    }
83}
84
85pub struct LTR303<I2C> {
86    i2c: I2C,
87    gain: Gain,
88    integration_time: IntegrationTime,
89}
90
91impl<I2C, E> LTR303<I2C>
92where
93    I2C: i2c::I2c<Error = E>,
94{
95    /// Initializes the LTR303 driver while consuming the i2c bus
96    pub fn init(i2c: I2C) -> Self {
97        LTR303 {
98            i2c,
99            gain: Gain::Gain1x,
100            integration_time: IntegrationTime::Ms100,
101        }
102    }
103
104    /// Get the manufacturer ID stored inside LTR303. This ID should be 0x05.
105    pub fn get_mfc_id(&mut self) -> Result<u8, Error<E>> {
106        self.read_register(Register::MANUFAC_ID)
107    }
108
109    /// Get the part ID stored inside LTR303. This ID should be 0xA0.
110    pub fn get_part_id(&mut self) -> Result<u8, Error<E>> {
111        self.read_register(Register::PART_ID)
112    }
113
114    /// Destroy driver instance, return I²C bus instance.
115    pub fn destroy(self) -> I2C {
116        self.i2c
117    }
118
119    // Starts a single-shot measurement!
120    pub fn start_measurement(&mut self, config: &LTR303Config) -> Result<(), Error<E>> {
121        // Save the current gain and integration times => To be used when translating raw to phys
122        self.gain = config.gain;
123        self.integration_time = config.integration_time;
124
125        // Configure gain, set active mode
126        let control_reg = ControlRegister::default()
127            .with_gain(config.gain)
128            .with_mode(Mode::ACTIVE);
129
130        // Then configure the integration time & measurement rate
131        let meas_rate_reg = MeasRateRegister::default()
132            .with_integration_time(config.integration_time)
133            .with_measurement_rate(config.measurement_rate);
134
135        self.write_register(Register::ALS_MEAS_RATE, meas_rate_reg.value())?;
136
137        // Then, configure the thresholds for the interrupt!
138        self.write_register(
139            Register::ALS_THRES_LOW_0,
140            config.int_thrsh_down.to_be_bytes()[1],
141        )?;
142        self.write_register(
143            Register::ALS_THRES_LOW_1,
144            config.int_thrsh_down.to_be_bytes()[0],
145        )?;
146        self.write_register(
147            Register::ALS_THRES_UP_0,
148            config.int_thrsh_up.to_be_bytes()[1],
149        )?;
150        self.write_register(
151            Register::ALS_THRES_UP_1,
152            config.int_thrsh_up.to_be_bytes()[0],
153        )?;
154
155        // Then enable interrupts
156        // TODO: Implement similar to the other registers, with bits and InterruptReg.set_high(Flags::ISREnable)
157        self.write_register(Register::INTERRUPT, 0b00000010)?;
158
159        // Then we start a measurement
160        self.write_register(Register::ALS_CONTR, control_reg.value())?;
161
162        Ok(())
163    }
164
165    /// Returns the contents of the ALS_STATUS register.
166    pub fn get_status(&mut self) -> Result<StatusRegister, Error<E>> {
167        let data = self.read_register(Register::ALS_STATUS)?;
168
169        let status_reg: StatusRegister = data.into();
170        Ok(status_reg)
171    }
172
173    /// Check if new sensor data is ready.
174    pub fn data_ready(&mut self) -> Result<bool, Error<E>> {
175        let status = self.get_status()?;
176        Ok(status.data_status.value == DataStatus::New)
177    }
178
179    /// Reads the Ambient Light Level from LTR303's registers and returns the physical
180    /// lux value.
181    pub fn get_lux_data(&mut self) -> Result<LuxData, Error<E>> {
182        let raw_data = self.get_raw_data()?;
183
184        Ok(LuxData {
185            lux_raw: raw_data,
186            lux_phys: raw_to_lux(
187                raw_data.ch1_raw,
188                raw_data.ch0_raw,
189                self.gain,
190                self.integration_time,
191            ),
192        })
193    }
194
195    /// Puts the sensor in a low-power Standby mode where it consumes 5uA of current.
196    pub fn standby(&mut self) -> Result<(), Error<E>> {
197        self.write_register(
198            Register::ALS_CONTR,
199            ControlRegister::default().with_mode(Mode::STANDBY).value(),
200        )?;
201        Ok(())
202    }
203}
204
205impl<I2C, E> LTR303<I2C>
206where
207    I2C: i2c::I2c<Error = E>,
208{
209    fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
210        self.i2c
211            .write(LTR303_BASE_ADDRESS, &[register, data])
212            .map_err(Error::I2C)
213            .and(Ok(()))
214    }
215
216    fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
217        let mut data: [u8; 1] = [0];
218        self.i2c
219            .write_read(LTR303_BASE_ADDRESS, &[register], &mut data)
220            .map_err(Error::I2C)
221            .and(Ok(data[0]))
222    }
223
224    fn get_raw_data(&mut self) -> Result<RawData, Error<E>> {
225        // Read raw illuminance data
226        // Use a single transaction to ensure that the data is from the same measurement
227        // (see pg. 17 of datasheet)
228        let mut data: [u8; 4] = [0; 4];
229        self.i2c
230            .write_read(LTR303_BASE_ADDRESS, &[Register::ALS_DATA_CH1_0], &mut data)
231            .map_err(Error::I2C)?;
232        let ch1_raw = u16::from_le_bytes([data[0], data[1]]);
233        let ch0_raw = u16::from_le_bytes([data[2], data[3]]);
234
235        Ok(RawData { ch0_raw, ch1_raw })
236    }
237}
238
239fn raw_to_lux(ch1_data: u16, ch0_data: u16, gain: Gain, itime: IntegrationTime) -> f32 {
240    let ratio = ch1_data as f32 / (ch0_data as f32 + ch1_data as f32);
241    let als_gain: f32 = gain.into();
242    let int_time: f32 = itime.into();
243
244    if ratio < 0.45 {
245        ((1.7743 * f32::from(ch0_data)) + (1.1059 * f32::from(ch1_data))) / als_gain / int_time
246    } else if (0.45..0.64).contains(&ratio) {
247        ((4.2785 * f32::from(ch0_data)) - (1.9548 * f32::from(ch1_data))) / als_gain / int_time
248    } else if (0.64..0.85).contains(&ratio) {
249        ((0.5926 * f32::from(ch0_data)) - (0.1185 * f32::from(ch1_data))) / als_gain / int_time
250    } else {
251        0.0
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    // this code lives inside a `tests` module
258
259    extern crate std;
260    use crate::{DataStatus, DataValidity, Gain, IntStatus};
261
262    use super::*;
263
264    use embedded_hal_mock::eh1::i2c;
265    const LTR303_ADDR: u8 = 0x29;
266
267    #[test]
268    fn manufacturer_info() {
269        let expectations = [i2c::Transaction::write_read(
270            LTR303_ADDR,
271            std::vec![Register::MANUFAC_ID],
272            std::vec![0x05],
273        )];
274        let mock = i2c::Mock::new(&expectations);
275
276        let mut ltr303 = LTR303::init(mock);
277        let mfc = ltr303.get_mfc_id().unwrap();
278        assert_eq!(0x05, mfc);
279
280        let mut mock = ltr303.destroy();
281        mock.done(); // verify expectations
282    }
283
284    #[test]
285    fn part_id() {
286        let expectations = [i2c::Transaction::write_read(
287            LTR303_ADDR,
288            std::vec![Register::PART_ID],
289            std::vec![0xA0],
290        )];
291        let mock = i2c::Mock::new(&expectations);
292
293        let mut ltr303 = LTR303::init(mock);
294        let part_id = ltr303.get_part_id().unwrap();
295        assert_eq!(0xA0, part_id);
296
297        let mut mock = ltr303.destroy();
298        mock.done(); // verify expectations
299    }
300
301    #[test]
302    fn sensor_status() {
303        let expectations = [i2c::Transaction::write_read(
304            LTR303_ADDR,
305            std::vec![Register::ALS_STATUS],
306            std::vec![0b11111010],
307        )];
308        let mock = i2c::Mock::new(&expectations);
309
310        let mut ltr303 = LTR303::init(mock);
311        let sensor_status = ltr303.get_status().unwrap();
312
313        assert_eq!(sensor_status.data_status.value, DataStatus::Old);
314        assert_eq!(sensor_status.data_valid.value, DataValidity::DataInvalid);
315        assert_eq!(sensor_status.gain.value, Gain::Gain96x);
316        assert_eq!(sensor_status.int_status.value, IntStatus::Active);
317
318        assert_eq!(sensor_status.value(), 0b11111000);
319
320        let mut mock = ltr303.destroy();
321        mock.done(); // verify expectations
322    }
323
324    #[test]
325    fn start_measurement() {
326        let expectations = [
327            i2c::Transaction::write(
328                LTR303_ADDR,
329                std::vec![Register::ALS_MEAS_RATE, 0b00010101], // 200ms integration time & 2000ms meas rate
330            ),
331            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_0, 0xFF]),
332            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_1, 0xFF]),
333            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_0, 0x00]),
334            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_1, 0x00]),
335            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::INTERRUPT, 0b00000010]),
336            i2c::Transaction::write(
337                LTR303_ADDR,
338                std::vec![Register::ALS_CONTR, 0b00000001], // Active mode, default otherwise
339            ),
340        ];
341
342        let mock = i2c::Mock::new(&expectations);
343
344        let config = LTR303Config {
345            integration_time: crate::IntegrationTime::Ms200,
346            measurement_rate: crate::MeasurementRate::Ms2000,
347            ..Default::default()
348        };
349
350        let mut ltr303 = LTR303::init(mock);
351
352        ltr303.start_measurement(&config).unwrap();
353
354        let mut mock = ltr303.destroy();
355        mock.done(); // verify expectations
356    }
357
358    // Do a complete measurement from start to finish and test that we get the proper data
359    #[test]
360    fn single_shot_measurement() {
361        // Start a Gain4x single measurement with integration time 100ms and measurement rate 2000ms,
362        // then after we got a result, put the sensor to sleep.
363        let expectations = [
364            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_MEAS_RATE, 0b0001_1101]), // set times
365            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_0, 0xFF]), // set thresholds
366            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_LOW_1, 0xFF]), // set thresholds
367            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_0, 0x00]), // set thresholds
368            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_THRES_UP_1, 0x00]), // set thresholds
369            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::INTERRUPT, 0b00000010]), // enable interrupts
370            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_CONTR, 0b0000_1001]), // wake-up sensor. start
371            i2c::Transaction::write_read(
372                LTR303_ADDR,
373                std::vec![Register::ALS_STATUS],
374                std::vec![0b1010_0000],
375            ), // current status: still measuring
376            i2c::Transaction::write_read(
377                LTR303_ADDR,
378                std::vec![Register::ALS_STATUS],
379                std::vec![0b0010_0100],
380            ), // current status: new data available
381            i2c::Transaction::write_read(
382                LTR303_ADDR,
383                std::vec![Register::ALS_DATA_CH1_0],
384                std::vec![0xAD, 0xDE, 0xEF, 0xBE],
385            ), // Reading channel data
386            i2c::Transaction::write(LTR303_ADDR, std::vec![Register::ALS_CONTR, 0b0000_0000]), // put sensor to sleep
387        ];
388        let mock = i2c::Mock::new(&expectations);
389
390        // Created expected bus communication. Below is the expected developer workflow:
391        let config = crate::LTR303Config::default()
392            .with_integration_time(crate::IntegrationTime::Ms400)
393            .with_measurement_rate(crate::MeasurementRate::Ms2000)
394            .with_gain(crate::Gain::Gain4x);
395
396        let mut ltr303 = LTR303::init(mock);
397        ltr303.start_measurement(&config).unwrap();
398
399        while ltr303.get_status().unwrap().data_status.value != DataStatus::New {}
400
401        let lux_data = ltr303.get_lux_data().unwrap();
402        ltr303.standby().unwrap();
403
404        assert_eq!(lux_data.lux_raw.ch1_raw, 0xDEAD);
405        assert_eq!(lux_data.lux_raw.ch0_raw, 0xBEEF);
406
407        let mut mock = ltr303.destroy();
408        mock.done(); // verify expectations
409    }
410
411    #[cfg(test)]
412    mod unit_tests {
413        use crate::{
414            raw_to_lux, ControlRegister, Field, Gain, IntegrationTime, MeasRateRegister,
415            MeasurementRate, Mode, ResetStatus,
416        };
417
418        #[test]
419        fn calculate_lux_from_raw() {
420            let ch0_data: u16 = 0x0000;
421            let ch1_data: u16 = 0xFFFF;
422
423            // First, test that CH1 >> CH0 returns 0 lux
424            let ltr303_config = crate::LTR303Config::default();
425
426            let lux = raw_to_lux(
427                ch1_data,
428                ch0_data,
429                ltr303_config.gain,
430                ltr303_config.integration_time,
431            );
432
433            assert_eq!(lux, 0.0);
434
435            // Then a normal random value testing ratio >= 0.45 && ratio < 0.64
436            let ch0_data: u16 = 0x1000;
437            let ch1_data: u16 = 0x1000;
438            let lux = raw_to_lux(
439                ch1_data,
440                ch0_data,
441                ltr303_config.gain,
442                ltr303_config.integration_time,
443            );
444
445            assert_eq!(lux, 9517.875);
446        }
447
448        #[test]
449        fn test_registers() {
450            // Test that the register API works as expected!
451            let control_reg = ControlRegister::default()
452                .with_mode(Mode::STANDBY)
453                .with_gain(Gain::Gain8x);
454
455            assert_eq!(control_reg.gain.value, Gain::Gain8x);
456            assert_eq!(control_reg.value(), 0b0000_1100);
457
458            let measrate_reg = MeasRateRegister::default()
459                .with_integration_time(IntegrationTime::Ms200)
460                .with_measurement_rate(MeasurementRate::Ms2000);
461
462            assert_eq!(measrate_reg.integration_time.value, IntegrationTime::Ms200);
463            assert_eq!(measrate_reg.value(), 0b0001_0101);
464        }
465
466        #[test]
467        fn test_register_from_u8() {
468            // Tests that we can properly transform a u8 value to a register with fields!
469            let contr_reg_val: u8 = 0b0000_1011;
470            let control_reg: ControlRegister = contr_reg_val.into();
471
472            assert_eq!(control_reg.gain.value, Gain::Gain4x);
473            assert_eq!(control_reg.sw_reset.value, ResetStatus::Resetting);
474            assert_eq!(control_reg.mode.value, Mode::ACTIVE);
475        }
476
477        #[test]
478        fn test_fields() {
479            // Field one should be 0b00010110
480            let field1 = Field {
481                start_index: 1,
482                width: 4,
483                value: 0x0Bu8,
484            };
485            assert_eq!(field1.bits(), 0b0001_0110)
486        }
487    }
488}