1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Sensor;
6
7use super::{
8 raw_from_milli_lux, register, Configuration, ConversionTime, Mode, Reading, DEVICE_ID,
9 MANUFACTURER_ID, RANGE_AUTOMATIC,
10};
11use crate::driver::{read_word, write_word};
12use crate::error::{DriverError, SensorError};
13
14pub const POLL_INTERVAL_MILLIS: u32 = 10;
17
18pub const STATUS_POLLS: u8 = 100;
23
24#[derive(Debug)]
53pub struct Opt3001<I2C, D> {
54 bus: I2C,
55 delay: D,
56 address: u8,
57 conversion_time: ConversionTime,
58 range_number: u8,
59 initialized: bool,
60}
61
62impl<I2C, D> Opt3001<I2C, D> {
63 pub fn new(bus: I2C, address: u8, delay: D) -> Self {
77 Opt3001 {
78 bus,
79 delay,
80 address,
81 conversion_time: ConversionTime::Ms800,
82 range_number: RANGE_AUTOMATIC,
83 initialized: false,
84 }
85 }
86
87 pub fn with_conversion_time(mut self, conversion_time: ConversionTime) -> Self {
97 self.conversion_time = conversion_time;
98 self.initialized = false;
99 self
100 }
101
102 pub fn with_range(mut self, range_number: u8) -> Self {
113 self.range_number = range_number;
114 self.initialized = false;
115 self
116 }
117
118 pub fn configuration(&self) -> Configuration {
120 self.configuration_in(Mode::Shutdown)
121 }
122
123 pub fn release(self) -> (I2C, D) {
129 (self.bus, self.delay)
130 }
131
132 fn configuration_in(&self, mode: Mode) -> Configuration {
133 Configuration {
134 range_number: self.range_number,
135 conversion_time: self.conversion_time,
136 mode,
137 ..Configuration::default()
138 }
139 }
140}
141
142impl<I2C: I2c, D: DelayNs> Opt3001<I2C, D> {
143 pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
150 if self.read(register::MANUFACTURER_ID)? != MANUFACTURER_ID
151 || self.read(register::DEVICE_ID)? != DEVICE_ID
152 {
153 return Err(SensorError::Identity.into());
154 }
155 self.write(register::CONFIGURATION, self.configuration().bits())?;
156 self.initialized = true;
157 Ok(())
158 }
159
160 pub fn measure(&mut self) -> Result<Reading, DriverError<I2C::Error>> {
173 if !self.initialized {
174 self.init()?;
175 }
176 let single_shot = self.configuration_in(Mode::SingleShot);
177 self.write(register::CONFIGURATION, single_shot.bits())?;
178 self.delay
179 .delay_ms(u32::from(self.conversion_time.millis()));
180
181 let mut ready = false;
182 for _ in 0..STATUS_POLLS {
183 let status = Configuration::from_bits(self.read(register::CONFIGURATION)?);
184 if status.conversion_ready {
185 ready = true;
186 break;
187 }
188 self.delay.delay_ms(POLL_INTERVAL_MILLIS);
189 }
190 if !ready {
191 return Err(DriverError::Timeout);
192 }
193 Ok(Reading::new(self.read(register::RESULT)?))
194 }
195
196 pub fn set_limits(
207 &mut self,
208 low_milli_lux: u32,
209 high_milli_lux: u32,
210 ) -> Result<(), DriverError<I2C::Error>> {
211 self.write(register::LOW_LIMIT, raw_from_milli_lux(low_milli_lux))?;
212 self.write(register::HIGH_LIMIT, raw_from_milli_lux(high_milli_lux))
213 }
214
215 fn read(&mut self, register: u8) -> Result<u16, DriverError<I2C::Error>> {
216 read_word(&mut self.bus, self.address, register).map_err(DriverError::Bus)
217 }
218
219 fn write(&mut self, register: u8, value: u16) -> Result<(), DriverError<I2C::Error>> {
220 write_word(&mut self.bus, self.address, register, value).map_err(DriverError::Bus)
221 }
222}
223
224impl<I2C, D> Sensor for Opt3001<I2C, D>
225where
226 I2C: I2c + Send,
227 I2C::Error: core::fmt::Debug,
228 D: DelayNs + Send,
229{
230 type Reading = Reading;
231
232 async fn read(&mut self) -> pamoja_core::Result<Reading> {
233 self.measure().map_err(pamoja_core::Error::from)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::opt3001::{I2C_ADDRESS_GND, I2C_ADDRESS_SDA};
241 use alloc::vec;
242 use alloc::vec::Vec;
243 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
244 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
245
246 const CONVERSION_READY: u16 = 1 << 7;
247
248 fn word(address: u8, register: u8, value: u16) -> I2cStep {
249 let [high, low] = value.to_be_bytes();
250 I2cStep::write(address, [register, high, low])
251 }
252
253 fn reply(address: u8, register: u8, value: u16) -> I2cStep {
254 I2cStep::write_read(address, [register], value.to_be_bytes())
255 }
256
257 fn init_steps(address: u8, shutdown: u16) -> Vec<I2cStep> {
258 vec![
259 reply(address, register::MANUFACTURER_ID, MANUFACTURER_ID),
260 reply(address, register::DEVICE_ID, DEVICE_ID),
261 word(address, register::CONFIGURATION, shutdown),
262 ]
263 }
264
265 #[test]
266 fn a_single_shot_conversion_follows_the_datasheet_sequence() {
267 let raw = raw_from_milli_lux(320_000);
268 let mut steps = init_steps(I2C_ADDRESS_GND, 0xC810);
269 steps.extend([
270 word(I2C_ADDRESS_GND, register::CONFIGURATION, 0xCA10),
271 reply(
272 I2C_ADDRESS_GND,
273 register::CONFIGURATION,
274 0xC810 | CONVERSION_READY,
275 ),
276 reply(I2C_ADDRESS_GND, register::RESULT, raw),
277 ]);
278 let mut sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_GND, DelayLog::new());
279 let reading = sensor.measure().unwrap();
280 assert_eq!(reading.raw(), raw);
281 assert_eq!(reading.milli_lux(), super::super::milli_lux(raw));
282 let (bus, delay) = sensor.release();
283 assert!(bus.done());
284 assert_eq!(delay.waits_ns(), [800_000_000]);
285 }
286
287 #[test]
288 fn the_integration_time_and_range_reach_the_configuration_and_the_wait() {
289 let configuration = Configuration {
290 range_number: 5,
291 conversion_time: ConversionTime::Ms100,
292 mode: Mode::Shutdown,
293 ..Configuration::default()
294 };
295 let shutdown = configuration.bits();
296 let single_shot = Configuration {
297 mode: Mode::SingleShot,
298 ..configuration
299 }
300 .bits();
301 let mut steps = init_steps(I2C_ADDRESS_SDA, shutdown);
302 steps.extend([
303 word(I2C_ADDRESS_SDA, register::CONFIGURATION, single_shot),
304 reply(I2C_ADDRESS_SDA, register::CONFIGURATION, shutdown),
305 reply(
306 I2C_ADDRESS_SDA,
307 register::CONFIGURATION,
308 shutdown | CONVERSION_READY,
309 ),
310 reply(I2C_ADDRESS_SDA, register::RESULT, 0x0C80),
311 ]);
312 let mut sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_SDA, DelayLog::new())
313 .with_conversion_time(ConversionTime::Ms100)
314 .with_range(5);
315 assert_eq!(sensor.configuration(), configuration);
316 assert_eq!(sensor.measure().unwrap().milli_lux(), 32_000);
317 let (bus, delay) = sensor.release();
318 assert!(bus.done());
319 assert_eq!(delay.waits_ns(), [100_000_000, 10_000_000]);
320 }
321
322 #[test]
323 fn a_part_with_the_wrong_id_is_refused() {
324 let steps = [
325 reply(I2C_ADDRESS_GND, register::MANUFACTURER_ID, MANUFACTURER_ID),
326 reply(I2C_ADDRESS_GND, register::DEVICE_ID, 0x3002),
327 ];
328 let mut sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_GND, DelayLog::new());
329 assert_eq!(
330 sensor.init(),
331 Err(DriverError::Sensor(SensorError::Identity))
332 );
333 }
334
335 #[test]
336 fn a_conversion_that_never_finishes_is_a_timeout() {
337 let mut steps = init_steps(I2C_ADDRESS_GND, 0xC810);
338 steps.push(word(I2C_ADDRESS_GND, register::CONFIGURATION, 0xCA10));
339 for _ in 0..STATUS_POLLS {
340 steps.push(reply(I2C_ADDRESS_GND, register::CONFIGURATION, 0xC810));
341 }
342 let mut sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_GND, DelayLog::new());
343 assert_eq!(sensor.measure(), Err(DriverError::Timeout));
344 let (bus, delay) = sensor.release();
345 assert!(bus.done());
346 assert_eq!(delay.total_millis(), 800 + 10 * u64::from(STATUS_POLLS));
347 }
348
349 #[test]
350 fn a_bus_fault_surfaces_as_a_bus_error() {
351 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
352 let mut sensor = Opt3001::new(
353 I2cScript::new([I2cStep::fault(I2C_ADDRESS_GND, kind)]),
354 I2C_ADDRESS_GND,
355 DelayLog::new(),
356 );
357 assert!(matches!(sensor.init(), Err(DriverError::Bus(_))));
358 }
359
360 #[test]
361 fn limits_are_written_in_the_result_format() {
362 let mut steps = init_steps(I2C_ADDRESS_GND, 0xC810);
363 steps.push(word(
364 I2C_ADDRESS_GND,
365 register::LOW_LIMIT,
366 raw_from_milli_lux(10_000),
367 ));
368 steps.push(word(
369 I2C_ADDRESS_GND,
370 register::HIGH_LIMIT,
371 raw_from_milli_lux(5_000_000),
372 ));
373 let mut sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_GND, DelayLog::new());
374 sensor.init().unwrap();
375 sensor.set_limits(10_000, 5_000_000).unwrap();
376 assert!(sensor.release().0.done());
377 }
378
379 #[test]
380 fn as_a_sensor_it_reads_and_maps_to_lux() {
381 let mut steps = init_steps(I2C_ADDRESS_GND, 0xC810);
382 steps.extend([
383 word(I2C_ADDRESS_GND, register::CONFIGURATION, 0xCA10),
384 reply(
385 I2C_ADDRESS_GND,
386 register::CONFIGURATION,
387 0xC810 | CONVERSION_READY,
388 ),
389 reply(I2C_ADDRESS_GND, register::RESULT, 0x0C80),
390 ]);
391 let sensor = Opt3001::new(I2cScript::new(steps), I2C_ADDRESS_GND, DelayLog::new());
392 let mut lux = sensor.map(|reading| reading.lux());
393 assert!((block_on(lux.read()).unwrap() - 32.0).abs() < 1e-3);
394 }
395
396 #[test]
397 fn identity_and_one_conversion_make_the_transfers_the_datasheet_gives() {
398 const PART: u8 = I2C_ADDRESS_GND;
399 let result = crate::opt3001::raw_from_milli_lux(320_000).to_be_bytes();
400 let bus = I2cScript::new([
401 I2cStep::write_read(PART, [0x7E], [0x54, 0x49]),
402 I2cStep::write_read(PART, [0x7F], [0x30, 0x01]),
403 I2cStep::write(PART, [0x01, 0xC8, 0x10]),
404 I2cStep::write(PART, [0x01, 0xCA, 0x10]),
405 I2cStep::write_read(PART, [0x01], [0xC8, 0x90]),
406 I2cStep::write_read(PART, [0x00], result),
407 ]);
408
409 let sensor = Opt3001::new(bus, PART, DelayLog::new());
410 let mut lux = sensor.map(|reading| reading.lux());
411 let reading = block_on(Sensor::read(&mut lux)).expect("the scripted part answers");
412 assert!((reading - 320.0).abs() < 0.5);
413 }
414}