• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eldruin / opt300x-rs / 16826662433

08 Aug 2025 09:13AM UTC coverage: 99.245% (+0.003%) from 99.242%
16826662433

Pull #2

github

web-flow
Merge f9d42999f into e1ee20127
Pull Request #2: Defmt support

263 of 265 relevant lines covered (99.25%)

12.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.16
/src/device_impl.rs
1
use core::{
2
    future::{poll_fn, Future},
3
    task::Poll,
4
};
5

6
use crate::{
7
    ic, marker, mode, ComparisonMode, Config, Error, FaultCount, IntegrationTime,
8
    InterruptPinPolarity, LuxRange, Measurement, ModeChangeError, Opt300x, PhantomData, SlaveAddr,
9
    Status,
10
};
11
use embedded_hal::i2c;
12

13
struct Register;
14
impl Register {
15
    const RESULT: u8 = 0x00;
16
    const CONFIG: u8 = 0x01;
17
    const LOW_LIMIT: u8 = 0x02;
18
    const HIGH_LIMIT: u8 = 0x03;
19
    const MANUFACTURER_ID: u8 = 0x7E;
20
    const DEVICE_ID: u8 = 0x7F;
21
}
22

23
struct BitFlags;
24
impl BitFlags {
25
    const CT: u16 = 1 << 11;
26
    const MODE1: u16 = 1 << 10;
27
    const MODE0: u16 = 1 << 9;
28
    const OVF: u16 = 1 << 8;
29
    const CRF: u16 = 1 << 7;
30
    const FH: u16 = 1 << 6;
31
    const FL: u16 = 1 << 5;
32
    const L: u16 = 1 << 4;
33
    const POL: u16 = 1 << 3;
34
    const ME: u16 = 1 << 2;
35
}
36

37
impl Default for Config {
38
    fn default() -> Self {
64✔
39
        Config { bits: 0xC810 }
64✔
40
    }
64✔
41
}
42

43
impl marker::WithDeviceId for ic::Opt3001 {}
44
impl marker::WithDeviceId for ic::Opt3004 {}
45
impl marker::WithDeviceId for ic::Opt3006 {}
46
impl marker::WithDeviceId for ic::Opt3007 {}
47

48
macro_rules! create {
49
    ($ic:ident, $method:ident) => {
50
        impl<I2C> Opt300x<I2C, ic::$ic, mode::OneShot> {
51
            /// Create new instance of the device
52
            pub fn $method(i2c: I2C, address: SlaveAddr) -> Self {
60✔
53
                Opt300x {
60✔
54
                    i2c,
60✔
55
                    address: address.addr(),
60✔
56
                    config: Config::default(),
60✔
57
                    low_limit: 0,
60✔
58
                    was_conversion_started: false,
60✔
59
                    _ic: PhantomData,
60✔
60
                    _mode: PhantomData,
60✔
61
                }
60✔
62
            }
60✔
63
        }
64
    };
65
}
66
create!(Opt3001, new_opt3001);
67
create!(Opt3002, new_opt3002);
68
create!(Opt3004, new_opt3004);
69
create!(Opt3006, new_opt3006);
70

71
impl<I2C> Opt300x<I2C, ic::Opt3007, mode::OneShot> {
72
    /// Create new instance of the OPT3007 device, which has a fixed I2C address.
73
    pub fn new_opt3007(i2c: I2C) -> Self {
1✔
74
        Opt300x {
1✔
75
            i2c,
1✔
76
            address: 0b100_0101,
1✔
77
            config: Config::default(),
1✔
78
            low_limit: 0,
1✔
79
            was_conversion_started: false,
1✔
80
            _ic: PhantomData,
1✔
81
            _mode: PhantomData,
1✔
82
        }
1✔
83
    }
1✔
84
}
85

86
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE> {
87
    /// Destroy driver instance, return I²C bus instance.
88
    pub fn destroy(self) -> I2C {
60✔
89
        self.i2c
60✔
90
    }
60✔
91
}
92

93
impl<I2C, IC> Opt300x<I2C, IC, mode::OneShot>
94
where
95
    I2C: i2c::I2c,
96
{
97
    /// Change into continuous measurement mode
98
    ///
99
    /// Note that the conversion ready flag is cleared automatically
100
    /// after calling this method.
101
    pub fn into_continuous(
22✔
102
        mut self,
22✔
103
    ) -> Result<Opt300x<I2C, IC, mode::Continuous>, ModeChangeError<I2C::Error, Self>> {
22✔
104
        if let Err(Error::I2C(e)) = self.set_config(
22✔
105
            self.config
22✔
106
                .with_high(BitFlags::MODE0)
22✔
107
                .with_high(BitFlags::MODE1),
22✔
108
        ) {
109
            return Err(ModeChangeError::I2C(e, self));
×
110
        }
22✔
111
        Ok(Opt300x {
22✔
112
            i2c: self.i2c,
22✔
113
            address: self.address,
22✔
114
            config: self.config,
22✔
115
            low_limit: self.low_limit,
22✔
116
            was_conversion_started: false,
22✔
117
            _ic: PhantomData,
22✔
118
            _mode: PhantomData,
22✔
119
        })
22✔
120
    }
22✔
121
}
122

123
impl<I2C, IC> Opt300x<I2C, IC, mode::Continuous>
124
where
125
    I2C: i2c::I2c,
126
{
127
    /// Change into one-shot mode
128
    ///
129
    /// This will actually shut down the device until a measurement is requested.
130
    pub fn into_one_shot(
1✔
131
        mut self,
1✔
132
    ) -> Result<Opt300x<I2C, IC, mode::OneShot>, ModeChangeError<I2C::Error, Self>> {
1✔
133
        if let Err(Error::I2C(e)) = self.set_config(
1✔
134
            self.config
1✔
135
                .with_low(BitFlags::MODE0)
1✔
136
                .with_low(BitFlags::MODE1),
1✔
137
        ) {
138
            return Err(ModeChangeError::I2C(e, self));
×
139
        }
1✔
140
        Ok(Opt300x {
1✔
141
            i2c: self.i2c,
1✔
142
            address: self.address,
1✔
143
            config: self.config,
1✔
144
            low_limit: self.low_limit,
1✔
145
            was_conversion_started: false,
1✔
146
            _ic: PhantomData,
1✔
147
            _mode: PhantomData,
1✔
148
        })
1✔
149
    }
1✔
150
}
151

152
impl<I2C, IC> Opt300x<I2C, IC, mode::Continuous>
153
where
154
    I2C: i2c::I2c,
155
{
156
    /// Read the result of the most recent light to digital conversion in lux
157
    pub fn read_lux(&mut self) -> Result<f32, Error<I2C::Error>> {
10✔
158
        let result = self.read_raw()?;
10✔
159
        Ok(raw_to_lux(result))
10✔
160
    }
10✔
161

162
    /// Read the result of the most recent light to digital conversion in
163
    /// raw format: (exponent, mantissa)
164
    pub fn read_raw(&mut self) -> Result<(u8, u16), Error<I2C::Error>> {
20✔
165
        let result = self.read_register(Register::RESULT)?;
20✔
166
        Ok(((result >> 12) as u8, result & 0xFFF))
20✔
167
    }
20✔
168
}
169

170
fn raw_to_lux(result: (u8, u16)) -> f32 {
11✔
171
    (f64::from(1 << result.0) * 0.01 * f64::from(result.1)) as f32
11✔
172
}
11✔
173

174
impl<I2C, IC> Opt300x<I2C, IC, mode::OneShot>
175
where
176
    I2C: i2c::I2c,
177
{
178
    /// Read the result of the most recent light to digital conversion in lux
179
    pub async fn read_lux(&mut self) -> Result<Measurement<f32>, Error<I2C::Error>> {
1✔
180
        let measurement = self.read_raw().await?;
1✔
181
        Ok(Measurement {
1✔
182
            result: raw_to_lux(measurement.result),
1✔
183
            status: measurement.status,
1✔
184
        })
1✔
185
    }
1✔
186

187
    /// Read the result of the most recent light to digital conversion in
188
    /// raw format: (exponent, mantissa)
189
    pub fn read_raw(
1✔
190
        &mut self,
1✔
191
    ) -> impl Future<Output = Result<Measurement<(u8, u16)>, Error<I2C::Error>>> + '_ {
1✔
192
        poll_fn(move |cx| {
3✔
193
            if self.was_conversion_started {
3✔
194
                let status = self.read_status()?;
2✔
195
                if status.conversion_ready {
2✔
196
                    let result = self.read_register(Register::RESULT)?;
1✔
197
                    self.was_conversion_started = false;
1✔
198
                    Poll::Ready(Ok(Measurement {
1✔
199
                        result: ((result >> 12) as u8, result & 0xFFF),
1✔
200
                        status,
1✔
201
                    }))
1✔
202
                } else {
203
                    cx.waker().wake_by_ref();
1✔
204
                    Poll::Pending
1✔
205
                }
206
            } else {
207
                let config = self.config.with_high(BitFlags::MODE0);
1✔
208
                self.write_register(Register::CONFIG, config.bits)?;
1✔
209
                self.was_conversion_started = true;
1✔
210
                cx.waker().wake_by_ref();
1✔
211
                Poll::Pending
1✔
212
            }
213
        })
3✔
214
    }
1✔
215
}
216

217
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
218
where
219
    I2C: i2c::I2c,
220
{
221
    /// Read the status of the conversion.
222
    ///
223
    /// Note that the conversion ready flag is cleared automatically
224
    /// after calling this method.
225
    pub fn read_status(&mut self) -> Result<Status, Error<I2C::Error>> {
8✔
226
        let config = self.read_register(Register::CONFIG)?;
8✔
227
        Ok(Status {
8✔
228
            has_overflown: (config & BitFlags::OVF) != 0,
8✔
229
            conversion_ready: (config & BitFlags::CRF) != 0,
8✔
230
            was_too_high: (config & BitFlags::FH) != 0,
8✔
231
            was_too_low: (config & BitFlags::FL) != 0,
8✔
232
        })
8✔
233
    }
8✔
234
}
235

236
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
237
where
238
    I2C: i2c::I2c,
239
{
240
    /// Set the fault count
241
    ///
242
    /// Note that the conversion ready flag is cleared automatically
243
    /// after calling this method.
244
    pub fn set_fault_count(&mut self, count: FaultCount) -> Result<(), Error<I2C::Error>> {
5✔
245
        let config = self.config.bits & !0b11;
5✔
246
        let config = match count {
5✔
247
            FaultCount::One => config,
1✔
248
            FaultCount::Two => config | 0b01,
1✔
249
            FaultCount::Four => config | 0b10,
1✔
250
            FaultCount::Eight => config | 0b11,
2✔
251
        };
252
        self.set_config(Config { bits: config })
5✔
253
    }
5✔
254

255
    /// Set the lux range.
256
    ///
257
    /// `Error::InvalidInputData` will be returned for manual values outside
258
    /// the valid range.
259
    ///
260
    /// Note that the conversion ready flag is cleared automatically
261
    /// after calling this method.
262
    pub fn set_lux_range(&mut self, range: LuxRange) -> Result<(), Error<I2C::Error>> {
4✔
263
        let value = match range {
3✔
264
            LuxRange::Auto => Ok(0b1100),
1✔
265
            LuxRange::Manual(rn) if rn >= 0b1100 => Err(Error::InvalidInputData),
3✔
266
            LuxRange::Manual(rn) => Ok(rn),
2✔
267
        }?;
1✔
268
        let config = self.config.bits & 0x0FFF;
3✔
269
        self.set_config(Config {
3✔
270
            bits: config | (u16::from(value) << 12),
3✔
271
        })
3✔
272
    }
4✔
273

274
    /// Set the integration (conversion) time.
275
    ///
276
    /// Note that the conversion ready flag is cleared automatically
277
    /// after calling this method.
278
    pub fn set_integration_time(&mut self, time: IntegrationTime) -> Result<(), Error<I2C::Error>> {
2✔
279
        let config = match time {
2✔
280
            IntegrationTime::Ms100 => self.config.with_low(BitFlags::CT),
1✔
281
            IntegrationTime::Ms800 => self.config.with_high(BitFlags::CT),
1✔
282
        };
283
        self.set_config(config)
2✔
284
    }
2✔
285

286
    /// Set the interrupt pin polarity
287
    ///
288
    /// Note that the conversion ready flag is cleared automatically
289
    /// after calling this method.
290
    pub fn set_interrupt_pin_polarity(
2✔
291
        &mut self,
2✔
292
        polarity: InterruptPinPolarity,
2✔
293
    ) -> Result<(), Error<I2C::Error>> {
2✔
294
        let config = match polarity {
2✔
295
            InterruptPinPolarity::Low => self.config.with_low(BitFlags::POL),
1✔
296
            InterruptPinPolarity::High => self.config.with_high(BitFlags::POL),
1✔
297
        };
298
        self.set_config(config)
2✔
299
    }
2✔
300

301
    /// Enable exponent masking.
302
    ///
303
    /// Note that the conversion ready flag is cleared automatically
304
    /// after calling this method.
305
    pub fn enable_exponent_masking(&mut self) -> Result<(), Error<I2C::Error>> {
1✔
306
        self.set_config(self.config.with_high(BitFlags::ME))
1✔
307
    }
1✔
308

309
    /// Disable exponent masking (default).
310
    ///
311
    /// Note that the conversion ready flag is cleared automatically
312
    /// after calling this method.
313
    pub fn disable_exponent_masking(&mut self) -> Result<(), Error<I2C::Error>> {
1✔
314
        self.set_config(self.config.with_low(BitFlags::ME))
1✔
315
    }
1✔
316

317
    /// Set result comparison mode for interrupt reporting
318
    ///
319
    /// Note that the conversion ready flag is cleared automatically
320
    /// after calling this method.
321
    pub fn set_comparison_mode(&mut self, mode: ComparisonMode) -> Result<(), Error<I2C::Error>> {
2✔
322
        let config = match mode {
2✔
323
            ComparisonMode::LatchedWindow => self.config.with_high(BitFlags::L),
1✔
324
            ComparisonMode::TransparentHysteresis => self.config.with_low(BitFlags::L),
1✔
325
        };
326
        self.set_config(config)
2✔
327
    }
2✔
328

329
    /// Set the lux low limit in raw format (exponent, mantissa).
330
    ///
331
    /// Returns `Error::InvalidInputData` for an exponent value greater than
332
    /// 11 or a mantissa value greater than 4095.
333
    ///
334
    /// Note that this disables the end-of-conversion mode.
335
    pub fn set_low_limit_raw(
5✔
336
        &mut self,
5✔
337
        exponent: u8,
5✔
338
        mantissa: u16,
5✔
339
    ) -> Result<(), Error<I2C::Error>> {
5✔
340
        if exponent > 0b1011 || mantissa > 0xFFF {
5✔
341
            return Err(Error::InvalidInputData);
2✔
342
        }
3✔
343
        let limit = u16::from(exponent) << 12 | mantissa;
3✔
344
        self.write_register(Register::LOW_LIMIT, limit)?;
3✔
345
        self.low_limit = limit;
3✔
346
        Ok(())
3✔
347
    }
5✔
348

349
    /// Set the lux high limit in raw format (exponent, mantissa).
350
    ///
351
    /// Returns `Error::InvalidInputData` for an exponent value greater than
352
    /// 11 or a mantissa value greater than 4095.
353
    pub fn set_high_limit_raw(
3✔
354
        &mut self,
3✔
355
        exponent: u8,
3✔
356
        mantissa: u16,
3✔
357
    ) -> Result<(), Error<I2C::Error>> {
3✔
358
        if exponent > 0b1011 || mantissa > 0xFFF {
3✔
359
            return Err(Error::InvalidInputData);
2✔
360
        }
1✔
361
        let limit = u16::from(exponent) << 12 | mantissa;
1✔
362
        self.write_register(Register::HIGH_LIMIT, limit)
1✔
363
    }
3✔
364

365
    /// Enable end-of-conversion mode
366
    ///
367
    /// Note that this changes the two highest bits of the lux low limit exponent.
368
    /// Please see the device datasheet for further details.
369
    pub fn enable_end_of_conversion_mode(&mut self) -> Result<(), Error<I2C::Error>> {
2✔
370
        let limit = self.low_limit | 0b1100 << 12;
2✔
371
        self.write_register(Register::LOW_LIMIT, limit)
2✔
372
    }
2✔
373

374
    /// Disable end-of-conversion mode
375
    ///
376
    /// Note that this restores the two highest bits of the lux low limit
377
    /// exponent to the last value set before enabling the end-of-conversion
378
    /// mode (0b00 by default).
379
    pub fn disable_end_of_conversion_mode(&mut self) -> Result<(), Error<I2C::Error>> {
2✔
380
        self.write_register(Register::LOW_LIMIT, self.low_limit)
2✔
381
    }
2✔
382
}
383

384
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
385
where
386
    I2C: i2c::I2c,
387
{
388
    /// Read the manifacturer ID
389
    pub fn get_manufacturer_id(&mut self) -> Result<u16, Error<I2C::Error>> {
1✔
390
        self.read_register(Register::MANUFACTURER_ID)
1✔
391
    }
1✔
392
}
393

394
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
395
where
396
    I2C: i2c::I2c,
397
    IC: marker::WithDeviceId,
398
{
399
    /// Read the device ID
400
    pub fn get_device_id(&mut self) -> Result<u16, Error<I2C::Error>> {
1✔
401
        self.read_register(Register::DEVICE_ID)
1✔
402
    }
1✔
403
}
404

405
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE> {
406
    /// Reset the internal state of this driver to the default values.
407
    ///
408
    /// *Note:* This does not alter the state or configuration of the device.
409
    ///
410
    /// This resets the cached configuration register value in this driver to
411
    /// the power-up (reset) configuration of the device.
412
    ///
413
    /// This needs to be called after performing a reset on the device, for
414
    /// example through an I2C general-call Reset command, which was not done
415
    /// through this driver to ensure that the configurations in the device
416
    /// and in the driver match.
417
    pub fn reset_internal_driver_state(&mut self) {
1✔
418
        self.config = Config::default();
1✔
419
        self.low_limit = 0;
1✔
420
        self.was_conversion_started = false;
1✔
421
    }
1✔
422
}
423

424
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
425
where
426
    I2C: i2c::I2c,
427
{
428
    fn read_register(&mut self, register: u8) -> Result<u16, Error<I2C::Error>> {
31✔
429
        let mut data = [0, 0];
31✔
430
        self.i2c
31✔
431
            .write_read(self.address, &[register], &mut data)
31✔
432
            .map_err(Error::I2C)
31✔
433
            .and(Ok(u16::from(data[0]) << 8 | u16::from(data[1])))
31✔
434
    }
31✔
435
}
436

437
impl<I2C, IC, MODE> Opt300x<I2C, IC, MODE>
438
where
439
    I2C: i2c::I2c,
440
{
441
    fn set_config(&mut self, config: Config) -> Result<(), Error<I2C::Error>> {
39✔
442
        self.write_register(Register::CONFIG, config.bits)?;
39✔
443
        self.config = config;
39✔
444
        Ok(())
39✔
445
    }
39✔
446

447
    fn write_register(&mut self, register: u8, value: u16) -> Result<(), Error<I2C::Error>> {
48✔
448
        let data = [register, (value >> 8) as u8, value as u8];
48✔
449
        self.i2c.write(self.address, &data).map_err(Error::I2C)
48✔
450
    }
48✔
451
}
452

453
#[cfg(test)]
454
mod tests {
455
    use core::convert::Infallible;
456

457
    use super::*;
458

459
    impl embedded_hal::i2c::ErrorType for I2cMock {
460
        type Error = Infallible;
461
    }
462

463
    struct I2cMock;
464
    impl i2c::I2c for I2cMock {
465
        fn transaction(
2✔
466
            &mut self,
2✔
467
            _address: u8,
2✔
468
            _operations: &mut [i2c::Operation<'_>],
2✔
469
        ) -> Result<(), Self::Error> {
2✔
470
            Ok(())
2✔
471
        }
2✔
472
    }
473

474
    #[test]
475
    fn can_reset_driver_state() {
1✔
476
        let mut device = Opt300x::new_opt3001(I2cMock {}, SlaveAddr::default());
1✔
477
        device.set_fault_count(FaultCount::Eight).unwrap();
1✔
478
        device.set_low_limit_raw(1, 2).unwrap();
1✔
479
        assert_ne!(device.config, Config::default());
1✔
480
        assert_ne!(device.low_limit, 0);
1✔
481
        device.reset_internal_driver_state();
1✔
482
        assert_eq!(device.config, Config::default());
1✔
483
        assert_eq!(device.low_limit, 0);
1✔
484
    }
1✔
485
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc