veml7700/
device_impl.rs

1#[cfg(feature = "lux_as_f32")]
2use crate::calculate_raw_threshold_value;
3#[cfg(feature = "lux_as_f32")]
4use crate::correction::{correct_high_lux, get_lux_raw_conversion_factor};
5use crate::{
6    Config, Error, FaultCount, Gain, IntegrationTime, InterruptStatus, PowerSavingMode, Veml7700,
7    DEVICE_ADDRESS,
8};
9#[cfg(feature = "is_sync")]
10use embedded_hal::i2c::{ErrorType, I2c, SevenBitAddress};
11#[cfg(not(feature = "is_sync"))]
12use embedded_hal_async::i2c::{ErrorType, I2c, SevenBitAddress};
13use maybe_async::maybe_async;
14
15struct Register;
16impl Register {
17    const ALS_CONF: u8 = 0x00;
18    const ALS_WH: u8 = 0x01;
19    const ALS_WL: u8 = 0x02;
20    const PSM: u8 = 0x03;
21    const ALS: u8 = 0x04;
22    const WHITE: u8 = 0x05;
23    const ALS_INT: u8 = 0x06;
24}
25
26struct BitFlags;
27impl BitFlags {
28    const ALS_SD: u16 = 0x01;
29    const ALS_INT_EN: u16 = 0x02;
30    const PSM_EN: u16 = 0x01;
31    const INT_TH_LOW: u16 = 1 << 15;
32    const INT_TH_HIGH: u16 = 1 << 14;
33}
34
35impl Config {
36    fn with_high(self, mask: u16) -> Self {
37        Config {
38            bits: self.bits | mask,
39        }
40    }
41    fn with_low(self, mask: u16) -> Self {
42        Config {
43            bits: self.bits & !mask,
44        }
45    }
46}
47
48impl<I2C> Veml7700<I2C>
49where
50    I2C: I2c<SevenBitAddress>,
51    I2C::Error: Into<Error<I2C::Error>>,
52{
53    /// Create new instance of the VEML6040 device.
54    pub fn new(i2c: I2C) -> Self {
55        Veml7700 {
56            i2c,
57            config: Config {
58                bits: BitFlags::ALS_SD,
59            },
60            gain: Gain::One,
61            it: IntegrationTime::_100ms,
62        }
63    }
64
65    /// Destroy driver instance, return I²C bus instance.
66    pub fn destroy(self) -> I2C {
67        self.i2c
68    }
69}
70
71impl<I2C> Veml7700<I2C>
72where
73    I2C: I2c<SevenBitAddress>,
74    I2C::Error: Into<Error<I2C::Error>>,
75{
76    /// Enable the device.
77    ///
78    /// Note that when activating the sensor a wait time of 4 ms should be
79    /// observed before the first measurement is picked up to allow for a
80    /// correct start of the signal processor and oscillator.
81    #[maybe_async]
82    pub async fn enable(&mut self) -> Result<(), Error<I2C::Error>> {
83        let config = self.config.with_low(BitFlags::ALS_SD);
84        self.set_config(config).await
85    }
86
87    /// Disable the device (shutdown).
88    #[maybe_async]
89    pub async fn disable(&mut self) -> Result<(), Error<I2C::Error>> {
90        let config = self.config.with_high(BitFlags::ALS_SD);
91        self.set_config(config).await
92    }
93
94    /// Set the integration time.
95    #[maybe_async]
96    pub async fn set_integration_time(&mut self, it: IntegrationTime) -> Result<(), Error<I2C::Error>> {
97        let mask = match it {
98            IntegrationTime::_25ms => 0b1100,
99            IntegrationTime::_50ms => 0b1000,
100            IntegrationTime::_100ms => 0b0000,
101            IntegrationTime::_200ms => 0b0001,
102            IntegrationTime::_400ms => 0b0010,
103            IntegrationTime::_800ms => 0b0011,
104        };
105        let config = self.config.bits & !(0b1111 << 6) | (mask << 6);
106        self.set_config(Config { bits: config }).await?;
107        self.it = it;
108        Ok(())
109    }
110
111    /// Set the gain.
112    #[maybe_async]
113    pub async fn set_gain(&mut self, gain: Gain) -> Result<(), Error<I2C::Error>> {
114        let mask = match gain {
115            Gain::One => 0,
116            Gain::Two => 1,
117            Gain::OneEighth => 2,
118            Gain::OneQuarter => 3,
119        };
120        let config = self.config.bits & !(0b11 << 11) | mask << 11;
121        self.set_config(Config { bits: config }).await?;
122        self.gain = gain;
123        Ok(())
124    }
125
126    /// Set the number of times a threshold crossing must happen consecutively
127    /// to trigger an interrupt.
128    #[maybe_async]
129    pub async fn set_fault_count(&mut self, fc: FaultCount) -> Result<(), Error<I2C::Error>> {
130        let mask = match fc {
131            FaultCount::One => 0,
132            FaultCount::Two => 1,
133            FaultCount::Four => 2,
134            FaultCount::Eight => 3,
135        };
136        let config = self.config.bits & !(0b11 << 4) | mask << 4;
137        self.set_config(Config { bits: config }).await
138    }
139
140    /// Enable interrupt generation.
141    #[maybe_async]
142    pub async fn enable_interrupts(&mut self) -> Result<(), Error<I2C::Error>> {
143        let config = self.config.with_high(BitFlags::ALS_INT_EN);
144        self.set_config(config).await
145    }
146
147    /// Disable interrupt generation.
148    #[maybe_async]
149    pub async fn disable_interrupts(&mut self) -> Result<(), Error<I2C::Error>> {
150        let config = self.config.with_low(BitFlags::ALS_INT_EN);
151        self.set_config(config).await
152    }
153
154    /// Set the ALS high threshold in raw format
155    #[maybe_async]
156    pub async fn set_high_threshold_raw(&mut self, threshold: u16) -> Result<(), Error<I2C::Error>> {
157        Ok(self.write_register(Register::ALS_WH, threshold).await?)
158    }
159
160    /// Set the ALS low threshold in raw format
161    #[maybe_async]
162    pub async fn set_low_threshold_raw(&mut self, threshold: u16) -> Result<(), Error<I2C::Error>> {
163        Ok(self.write_register(Register::ALS_WL, threshold).await?)
164    }
165
166    /// Set the ALS high threshold in lux.
167    ///
168    /// For values higher than 1000 lx and 1/4 or 1/8 gain,
169    /// the inverse of the compensation formula is applied (this involves
170    /// quite some math).
171    #[cfg(feature = "lux_as_f32")]
172    #[maybe_async]
173    pub async fn set_high_threshold_lux(&mut self, lux: f32) -> Result<(), Error<I2C::Error>> {
174        let raw = self.calculate_raw_threshold_value(lux);
175        self.set_high_threshold_raw(raw).await
176    }
177    // TODO make a const-able version for pre-calculating the raw-threshold_lux
178
179    /// Set the ALS low threshold in lux.
180    ///
181    /// For values higher than 1000 lx and 1/4 or 1/8 gain,
182    /// the inverse of the compensation formula is applied (this involves
183    /// quite some math).
184    #[cfg(feature = "lux_as_f32")]
185    #[maybe_async]
186    pub async fn set_low_threshold_lux(&mut self, lux: f32) -> Result<(), Error<I2C::Error>> {
187        let raw = self.calculate_raw_threshold_value(lux);
188        self.set_low_threshold_raw(raw).await
189    }
190
191    /// Calculate raw value for threshold applying compensation if necessary.
192    ///
193    /// This takes into consideration the configured integration time and gain
194    /// and compensates the lux value if necessary.
195    ///
196    /// For values higher than 1000 lx and 1/4 or 1/8 gain, the inverse of the
197    /// compensation formula is applied. This involves quite some math so it
198    /// may be interesting to calculate the threshold values ahead of time.
199    #[cfg(feature = "lux_as_f32")]
200    pub fn calculate_raw_threshold_value(&self, lux: f32) -> u16 {
201        calculate_raw_threshold_value(self.it, self.gain, lux)
202    }
203
204    /// Enable the power-saving mode
205    #[maybe_async]
206    pub async fn enable_power_saving(&mut self, psm: PowerSavingMode) -> Result<(), Error<I2C::Error>> {
207        let mask = match psm {
208            PowerSavingMode::One => 0,
209            PowerSavingMode::Two => 1,
210            PowerSavingMode::Three => 2,
211            PowerSavingMode::Four => 3,
212        };
213        let value = BitFlags::PSM_EN | mask << 1;
214        Ok(self.write_register(Register::PSM, value).await?)
215    }
216
217    /// Disable the power-saving mode
218    #[maybe_async]
219    pub async fn disable_power_saving(&mut self) -> Result<(), Error<I2C::Error>> {
220        Ok(self.write_register(Register::PSM, 0).await?)
221    }
222
223    #[maybe_async]
224    async fn set_config(&mut self, config: Config) -> Result<(), Error<I2C::Error>> {
225        self.write_register(Register::ALS_CONF, config.bits).await?;
226        self.config = config;
227        Ok(())
228    }
229
230    #[maybe_async]
231    async fn write_register(
232        &mut self,
233        register: u8,
234        value: u16,
235    ) -> Result<(), <I2C as ErrorType>::Error> {
236        self.i2c
237            .write(DEVICE_ADDRESS, &[register, value as u8, (value >> 8) as u8]).await
238    }
239}
240
241impl<I2C> Veml7700<I2C>
242where
243    I2C: I2c<SevenBitAddress>,
244    I2C::Error: Into<Error<I2C::Error>>,
245{
246    /// Read whether an interrupt has occurred.
247    ///
248    /// Note that the interrupt status is updated at the same rate as the
249    /// measurements. Once triggered, flags will stay true until a measurement
250    /// is taken which does not exceed the threshold.
251    #[maybe_async]
252    pub async fn read_interrupt_status(&mut self) -> Result<InterruptStatus, Error<I2C::Error>> {
253        let data = self.read_register(Register::ALS_INT).await?;
254        Ok(InterruptStatus {
255            was_too_low: (data & BitFlags::INT_TH_LOW) != 0,
256            was_too_high: (data & BitFlags::INT_TH_HIGH) != 0,
257        })
258    }
259
260    /// Read ALS high resolution output data in raw format
261    #[maybe_async]
262    pub async fn read_raw(&mut self) -> Result<u16, Error<I2C::Error>> {
263        self.read_register(Register::ALS).await
264    }
265
266    /// Read ALS high resolution output data converted to lux
267    ///
268    /// For values higher than 1000 lx and 1/4 or 1/8 gain,
269    /// the following compensation formula is applied:
270    /// `lux = 6.0135e-13*(lux^4) - 9.3924e-9*(lux^3) + 8.1488e-5*(lux^2) + 1.0023*lux`
271    #[cfg(feature = "lux_as_f32")]
272    #[maybe_async]
273    pub async fn read_lux(&mut self) -> Result<f32, Error<I2C::Error>> {
274        let raw = self.read_register(Register::ALS).await?;
275        Ok(self.convert_raw_als_to_lux(raw))
276    }
277
278    /// Calculate lux value for a raw ALS measurement.
279    ///
280    /// This takes into consideration the configured integration time and gain
281    /// and compensates the lux value if necessary.
282    ///
283    /// For values higher than 1000 lx and 1/4 or 1/8 gain,
284    /// the following compensation formula is applied:
285    /// `lux = 6.0135e-13*(lux^4) - 9.3924e-9*(lux^3) + 8.1488e-5*(lux^2) + 1.0023*lux`
286    #[cfg(feature = "lux_as_f32")]
287    pub fn convert_raw_als_to_lux(&self, raw_als: u16) -> f32 {
288        convert_raw_als_to_lux(self.it, self.gain, raw_als)
289    }
290
291    /// Read white channel measurement
292    #[maybe_async]
293    pub async fn read_white(&mut self) -> Result<u16, Error<I2C::Error>> {
294        self.read_register(Register::WHITE).await
295    }
296
297    #[maybe_async]
298    async fn read_register(&mut self, register: u8) -> Result<u16, Error<I2C::Error>> {
299        let mut data = [0; 2];
300        self.i2c
301            .write_read(DEVICE_ADDRESS, &[register], &mut data).await
302            .map_err(Error::I2C)
303            .and(Ok(u16::from(data[0]) | u16::from(data[1]) << 8))
304    }
305}
306
307/// Calculate lux value for a raw ALS measurement.
308///
309/// For values higher than 1000 lx and 1/4 or 1/8 gain,
310/// the following compensation formula is applied:
311/// `lux = 6.0135e-13*(lux^4) - 9.3924e-9*(lux^3) + 8.1488e-5*(lux^2) + 1.0023*lux`
312#[cfg(feature = "lux_as_f32")]
313pub fn convert_raw_als_to_lux(it: IntegrationTime, gain: Gain, raw_als: u16) -> f32 {
314    let factor = get_lux_raw_conversion_factor(it, gain);
315    let lux = f32::from(raw_als) * factor;
316    if (gain == Gain::OneQuarter || gain == Gain::OneEighth) && lux > 1000.0 {
317        correct_high_lux(lux)
318    } else {
319        lux
320    }
321}