1use core::result::Result::{self, Ok};
14
15use embedded_hal_async::delay::DelayNs;
16use embedded_hal_async::i2c::I2c;
17
18const POWER_DOWN: u8 = 0x00;
20const POWER_ON: u8 = 0x01;
21const RESET: u8 = 0x07;
22
23const MTREG_HIGH: u8 = 0x40;
26const MTREG_LOW: u8 = 0x60;
27
28pub const MTREG_MIN: u8 = 31;
30pub const MTREG_MAX: u8 = 254;
32const DEFAULT_MTREG: u8 = 69; #[derive(Debug, Copy, Clone)]
36pub enum Bh1750Error<E> {
37 I2c(E),
39 ContinuousMeasurementNotStarted,
44}
45
46impl<E> From<E> for Bh1750Error<E> {
47 fn from(e: E) -> Self {
48 Bh1750Error::I2c(e)
49 }
50}
51
52#[derive(Debug, Clone, Copy)]
57pub enum Address {
58 Low = 0x23,
60 High = 0x5C,
62}
63
64#[derive(Debug, Clone, Copy)]
66pub enum Resolution {
67 High,
71 High2,
75 Low,
79}
80
81impl Resolution {
82 #[inline]
83 const fn continuous_measurement_opcode(self) -> u8 {
84 match self {
85 Self::High => 0x10,
86 Self::High2 => 0x11,
87 Self::Low => 0x13,
88 }
89 }
90
91 #[inline]
92 const fn one_time_measurement_opcode(self) -> u8 {
93 match self {
94 Self::High => 0x20,
95 Self::High2 => 0x21,
96 Self::Low => 0x23,
97 }
98 }
99
100 #[inline]
101 const fn default_measurement_time_ms(self) -> u32 {
102 match self {
105 Self::High | Self::High2 => 120,
106 Self::Low => 16,
107 }
108 }
109
110 #[inline]
111 const fn default_resolution_lx_count(self) -> f32 {
112 match self {
115 Self::High => 1.0,
116 Self::High2 => 0.5,
117 Self::Low => 4.0,
118 }
119 }
120}
121
122pub struct Bh1750<I2C, D>
124where
125 D: DelayNs,
126{
127 i2c: I2C,
128 delay: D,
129 address: Address,
130 mtreg: u8,
131 continuous_resolution: Option<Resolution>,
132}
133
134impl<I2C, E, D> Bh1750<I2C, D>
135where
136 I2C: I2c<u8, Error = E>,
137 D: DelayNs,
138{
139 #[must_use]
144 pub fn new(i2c: I2C, delay: D, address: Address) -> Self {
145 Self {
146 i2c,
147 delay,
148 address,
149 mtreg: DEFAULT_MTREG,
150 continuous_resolution: None,
151 }
152 }
153
154 pub async fn power_on(&mut self) -> Result<(), Bh1750Error<E>> {
160 self.send_instruction(POWER_ON).await
161 }
162
163 pub async fn power_down(&mut self) -> Result<(), Bh1750Error<E>> {
169 self.send_instruction(POWER_DOWN).await
170 }
171
172 pub async fn reset(&mut self) -> Result<(), Bh1750Error<E>> {
180 self.send_instruction(RESET).await
181 }
182
183 pub async fn set_mtreg(&mut self, mtreg: u8) -> Result<(), Bh1750Error<E>> {
191 let mt = mtreg.clamp(MTREG_MIN, MTREG_MAX);
192
193 let high = MTREG_HIGH | (mt >> 5);
195 let low = MTREG_LOW | (mt & 0x1F);
196
197 self.send_instruction(high).await?;
198 self.send_instruction(low).await?;
199
200 self.mtreg = mt;
201
202 Ok(())
203 }
204
205 pub async fn one_time_measurement(&mut self, res: Resolution) -> Result<f32, Bh1750Error<E>> {
213 self.start_one_time_measurement(res).await?;
214 self.delay.delay_ms(self.measurement_time_ms(res)).await;
215 let raw = self.read_raw().await?;
216
217 Ok(self.raw_to_lux(raw, res))
218 }
219
220 pub async fn start_continuous_measurement(
226 &mut self,
227 res: Resolution,
228 ) -> Result<(), Bh1750Error<E>> {
229 self.send_instruction(res.continuous_measurement_opcode())
230 .await?;
231 self.continuous_resolution = Some(res);
232
233 Ok(())
234 }
235
236 pub async fn read_continuous_measurement(&mut self) -> Result<f32, Bh1750Error<E>> {
244 let res = self
245 .continuous_resolution
246 .ok_or(Bh1750Error::ContinuousMeasurementNotStarted)?;
247
248 self.delay.delay_ms(self.measurement_time_ms(res)).await;
250
251 let raw = self.read_raw().await?;
252
253 Ok(self.raw_to_lux(raw, res))
254 }
255
256 async fn start_one_time_measurement(&mut self, res: Resolution) -> Result<(), Bh1750Error<E>> {
257 self.send_instruction(res.one_time_measurement_opcode())
258 .await
259 }
260
261 async fn read_raw(&mut self) -> Result<u16, E> {
262 let mut buf = [0u8; 2];
263 self.i2c.read(self.address as u8, &mut buf).await?;
264
265 Ok(u16::from_be_bytes(buf))
266 }
267
268 fn raw_to_lux(&self, raw: u16, res: Resolution) -> f32 {
269 f32::from(raw)
278 * res.default_resolution_lx_count()
279 * (f32::from(self.mtreg) / f32::from(DEFAULT_MTREG))
280 / 1.2
281 }
282
283 #[inline]
284 fn measurement_time_ms(&self, res: Resolution) -> u32 {
285 res.default_measurement_time_ms() * u32::from(self.mtreg) / u32::from(DEFAULT_MTREG)
289 }
290
291 #[inline]
292 async fn send_instruction(&mut self, instr: u8) -> Result<(), Bh1750Error<E>> {
293 self.i2c.write(self.address as u8, &[instr]).await?;
294
295 Ok(())
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 extern crate std;
304 use std::vec;
305
306 use embedded_hal_mock::eh1::delay::NoopDelay;
307 use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
308
309 fn raw_to_lux(raw: u16, res: Resolution, mtreg: u8) -> f32 {
310 f32::from(raw)
311 * res.default_resolution_lx_count()
312 * (f32::from(mtreg) / f32::from(DEFAULT_MTREG))
313 / 1.2
314 }
315
316 #[tokio::test]
317 async fn test_power_on() {
318 let expectations = [I2cTransaction::write(0x23, vec![0x01])]; let i2c = I2cMock::new(&expectations);
321 let delay = NoopDelay::new();
322 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
323
324 bh1750.power_on().await.unwrap();
325
326 bh1750.i2c.done();
327 }
328
329 #[tokio::test]
330 async fn test_power_down() {
331 let expectations = [I2cTransaction::write(0x23, vec![0x00])]; let i2c = I2cMock::new(&expectations);
334 let delay = NoopDelay::new();
335 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
336
337 bh1750.power_down().await.unwrap();
338
339 bh1750.i2c.done();
340 }
341
342 #[tokio::test]
343 async fn test_reset() {
344 let expectations = [I2cTransaction::write(0x23, vec![0x07])]; let i2c = I2cMock::new(&expectations);
347 let delay = NoopDelay::new();
348 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
349
350 bh1750.reset().await.unwrap();
351
352 bh1750.i2c.done();
353 }
354
355 #[tokio::test]
356 async fn test_set_mtreg_clamping_high() {
357 let high = 0x40 | (254 >> 5);
359 let low = 0x60 | (254 & 0x1F);
360 let expectations = [
361 I2cTransaction::write(0x23, vec![high]),
362 I2cTransaction::write(0x23, vec![low]),
363 ];
364
365 let i2c = I2cMock::new(&expectations);
366 let delay = NoopDelay::new();
367 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
368
369 bh1750.set_mtreg(255).await.unwrap();
370 assert_eq!(bh1750.mtreg, 254);
371
372 bh1750.i2c.done();
373 }
374
375 #[tokio::test]
376 async fn test_one_time_measurement() {
377 let expectations = [
380 I2cTransaction::write(0x23, vec![0x20]), I2cTransaction::read(0x23, vec![0x12, 0x34]),
382 ];
383
384 let i2c = I2cMock::new(&expectations);
385 let delay = NoopDelay::new();
386 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
387
388 let lux = bh1750.one_time_measurement(Resolution::High).await.unwrap();
389 assert!((lux - raw_to_lux(0x1234, Resolution::High, DEFAULT_MTREG)).abs() < f32::EPSILON);
390 bh1750.i2c.done();
391 }
392
393 #[tokio::test]
394 async fn test_continuous_measurement_flow() {
395 let expectations = [
398 I2cTransaction::write(0x23, vec![0x10]), I2cTransaction::read(0x23, vec![0x56, 0x78]),
400 ];
401
402 let i2c = I2cMock::new(&expectations);
403 let delay = NoopDelay::new();
404
405 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
406 bh1750
407 .start_continuous_measurement(Resolution::High)
408 .await
409 .unwrap();
410
411 let lux = bh1750.read_continuous_measurement().await.unwrap();
412 assert!((lux - raw_to_lux(0x5678, Resolution::High, DEFAULT_MTREG)).abs() < f32::EPSILON);
413
414 bh1750.i2c.done();
415 }
416
417 #[tokio::test]
418 async fn test_continuous_measurement_error_if_not_started() {
419 let i2c = I2cMock::new(&[]);
420 let delay = NoopDelay::new();
421 let mut bh1750 = Bh1750::new(i2c, delay, Address::Low);
422
423 let err = bh1750.read_continuous_measurement().await.unwrap_err();
424 matches!(err, Bh1750Error::ContinuousMeasurementNotStarted);
425
426 bh1750.i2c.done();
427 }
428}