apds9253 1.0.0

Driver for APDS9253 light sensor
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
// Copyright Open Logistics Foundation
//
// Licensed under the Open Logistics Foundation License 1.3.
// For details on the licensing terms, see the LICENSE file.
// SPDX-License-Identifier: OLFL-1.3

#![no_std]
#![warn(missing_docs)]

//! This crate implements the driver for the APDS9253 RGB, ambient and IR light sensor from Broadcom. The sensor can be initialized in RGB mode, where all color channels and the infrared channel is available or in ALS mode, where only the ambient light and the infrared light channel is available.
//!
//! # Resources
//!
//! [Broadcom APDS9253](https://www.broadcom.com/products/optical-sensors/ambient-light-photo-sensors/apds-9253-001)
//!
//! [APDS9253 Datasheet](https://docs.broadcom.com/doc/APDS-9253-001-DS)
//!
//! # Features
//!
//! - Up to 20-bit resolution
//! - I2C compatible interface with dedicated interrupt pin
//! - Individual channels for red (R), green (G), blue (B) and infrared (IR)
//! - Approximate human eye response with green channel
//! - ALS and RGB sensing with integrated IR-blocking filter
//! - Low power consumption
//!
//! # Examples
//!
//! The APDS has to be configured in RGB or ALS mode.
//!
//! ALS mode:
//! ```rust, no_run
//!# use embedded_hal_mock::*;
//!# let expectations = [];
//!# let mut i2c = i2c::Mock::new(&expectations);
//!#
//! use apds9253::*;
//!
//! let mut sensor = Apds9253::new_als(i2c);
//! sensor.init().unwrap();
//!
//! let ambient = sensor.read_ambient_light();
//! let infrared = sensor.read_infrared();
//!
//! log::info!("Ambient: {ambient:?}, Infrared: {infrared:?}");
//!
//! ```
//!
//! RGB mode:
//! ```rust, no_run
//!# use embedded_hal_mock::*;
//!# let expectations = [];
//!# let mut i2c = i2c::Mock::new(&expectations);
//!#
//! use apds9253::*;
//!
//! let mut sensor = Apds9253::new_rgb(i2c);
//! sensor.init().unwrap();
//!
//! let red = sensor.read_red_channel();
//! let green = sensor.read_green_channel();
//! let blue = sensor.read_blue_channel();
//! let infrared = sensor.read_infrared();
//!
//! log::info!("Red: {red:?}, Green: {green:?}, Blue: {blue:?}, Infrared: {infrared:?}");
//!
//! ```
//!
//! # License
//!
//! Open Logistics Foundation License\
//! Version 1.3, January 2023
//!
//! See the LICENSE file in the top-level directory.
//!
//! # Contact Information
//!
//! Fraunhofer IML Embedded Rust Group - <embedded-rust@iml.fraunhofer.de>

use embedded_hal::blocking::i2c;
pub use kind::*;

/// Fix I2C address of the APDS9253 light sensor
const I2C_ADDRESS: u8 = 0x52;

/// Fix PartID of the light sensor
const APDS9253_ID: u8 = 0xC2;

/// Register addresses
#[derive(Debug, Copy, Clone)]
enum Register {
    /// Register for the main control of the sensor (SleepAfterInterrupt, Reset, Mode, Enable)
    MainControl = 0x00,
    /// Controls the timing of the periodic measurement of the light sensor
    MeasurementRate = 0x04,
    /// Register for the gain of the light sensor channels
    Gain = 0x05,
    /// Register of the sensor ID
    PartId = 0x06,
    /// Status register (PowerOnStatus, InterruptStatus, DataStatus)
    MainStatus = 0x07,
    /// Least significant data byte of the IR channel; Followed by intervening (DataIr+1) and most significant byte (DataIr+2).
    DataIr = 0x0A,
    /// Least significant data byte of the green channel; Followed by intervening (DataGreen+1) and most significant byte (DataGreen+2).
    DataGreen = 0x0D,
    /// Least significant data byte of the blue channel; Followed by intervening (DataBlue+1) and most significant byte (DataBlue+2).
    DataBlue = 0x10,
    /// Least significant data byte of the red channel; Followed by intervening (DataRed+1) and most significant byte (DataRed+2).
    DataRed = 0x13,
    /// Register to setup the interrupt (Channel, VarMode, Enable)
    InterruptConfiguration = 0x19,
    /// Register for the persistance of the interrupt
    InterruptPersistance = 0x1A,
    /// Register of the least significant data byte of the upper interrupt threshold
    ThresholdUpper0 = 0x21,
    /// Register of the intervening data byte of the upper interrupt threshold
    ThresholdUpper1 = 0x22,
    /// Register of the most significant data byte of the upper interrupt threshold
    ThresholdUpper2 = 0x23,
    /// Register of the least significant data byte of the lower interrupt threshold
    ThresholdLower0 = 0x24,
    /// Register of the intervening data byte of the lower interrupt threshold
    ThresholdLower1 = 0x25,
    /// Register of the most significant data byte of the lower interrupt threshold
    ThresholdLower2 = 0x26,
    /// Register for the variance threshold
    ThresholdVariance = 0x27,
    /// Readable register for the DarkCount (valid, value)
    CountStorage = 0x29,
}

/// State for activating or deactivating functions of the sensor
#[derive(Debug, Copy, Clone)]
pub enum State {
    /// Enable/On
    Enable = 1,
    /// Disable/Off
    Disable = 0,
}

/// Light source channel. Ambient light channel is equivalent to the green channel.
#[derive(Default, Debug, Copy, Clone)]
pub enum Channel {
    /// Infrared channel
    Ir = 0,
    /// Green channel
    #[default]
    Green = 1,
    /// Red channel
    Red = 2,
    /// Blue channel
    Blue = 3,
}

/// Mode of the sensor, selects which channels are activated
#[derive(Default, Debug, Copy, Clone)]
pub enum RgbMode {
    /// Only the ambient light (green) and the IR (infrared) channel is activated
    #[default]
    AlsIr = 0,
    /// All channels (red, green, blue and infrared) are activated
    RgbIr = 1,
}

/// Gain of the sensor readings
///
/// Default value is Gain3
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum Gain {
    Gain1 = 0,
    #[default]
    Gain3 = 1,
    Gain6 = 2,
    Gain9 = 3,
    Gain18 = 4,
}

/// Variance for the interrupt capability
///
/// Throws an interrupt if the light reading value varies by X (8-1024) counts compared to the previous value
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum Variance {
    #[default]
    Var8 = 0,
    Var16 = 1,
    Var32 = 2,
    Var64 = 3,
    Var128 = 4,
    Var256 = 5,
    Var512 = 6,
    Var1024 = 7,
}

/// The resolution of the sensor defines the duration in which a measurement takes place.
/// A 20 bit resolution results in 400ms measurement duration. From 20 bit down to 13 bit this results
/// in 400, 200, 100 (default), 50, 25, 3.125ms.
/// If this duration is greater than the MeasurementRate, the MeasurementRate will be lowered.
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum Resolution {
    Res20Bit = 0,
    Res19Bit = 1,
    #[default]
    Res18Bit = 2,
    Res17Bit = 3,
    Res16Bit = 4,
    Res13Bit = 5,
}

/// The measurement rate of the sensor controls the timing of the periodic measurements in active mode. Once the measurement is done
/// the sensor will go back to inactive mode. If the MeasurementRate is smaller than the duration defined by the Resolution,
/// the measurement rate will be lowered accordingly.
/// Unit is milliseconds
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum MeasurementRate {
    Rate25ms = 0,
    Rate50ms = 1,
    #[default]
    Rate100ms = 2,
    Rate200ms = 3,
    Rate500ms = 4,
    Rate1000ms = 5,
    Rate2000ms = 6,
}

/// Representation of the sensors dark count register
#[derive(Debug, Copy, Clone)]
pub struct DarkCount {
    /// DarkCount valid
    pub valid: bool,
    /// DarkCount value
    pub value: u8,
}

/// Sensor Status as reported via the MAIN_STATUS register
#[derive(Debug, Copy, Clone)]
pub struct SensorStatus {
    /// PowerStatus states if the sensor went through a power-up event (turned on or power supply disturbance)
    pub powered_up: bool,
    /// InterruptStatus states if an interrupt occurred. Will be cleared after reading.
    pub interrupt_occurred: bool,
    /// DataStatus states if new/unread data is available. Will be cleared after reading.
    pub data_available: bool,
}

/// Configuration for the sensors interrupt, can either be Threshold or Variance based
#[derive(Debug, Copy, Clone)]
pub enum InterruptConfig {
    /// Threshold based interrupt with lower and upper thresholds (20 bit values), persistance value (1-16) and source channel
    Threshold(u32, u32, u8, Channel),
    /// Variance based interrupt with variance threshold, persistance (1-16) value and source channel
    Variance(Variance, u8, Channel),
}

/// Implementation for the interrupt configuration
impl InterruptConfig {
    /// Creates a new configuration in threshold mode with lower and upper thresholds (20 bit values), the persistance value (1-16) and given interrupt channel
    pub fn new_threshold(lower: u32, upper: u32, persist: u8, channel: Channel) -> InterruptConfig {
        InterruptConfig::Threshold(lower, upper, persist, channel)
    }
    /// Creates a new configuration in variance mode with variance threshold, the persistance (1-16) and given interrupt channel
    pub fn new_variance(variance: Variance, persist: u8, channel: Channel) -> InterruptConfig {
        InterruptConfig::Variance(variance, persist, channel)
    }
}

/// Possible errors in this sensor
#[derive(Debug, Copy, Clone)]
pub enum Error<E> {
    /// An I2C Error occurred
    I2C(E),
    /// Sensor ID wrong
    ID,
}

/// Module containing markers to decide between RGB or ALS sensor mode
pub mod kind {
    /// Trait marking the different sensor kinds. Requires you to define
    pub trait Apds9253Sensor {}
    /// Light sensor with RGB and IR channel active
    pub struct Apds9253RgbIr {}
    impl Apds9253Sensor for Apds9253RgbIr {}
    /// Light sensor with ALS and IR channel active
    pub struct Apds9253AlsIr;
    impl Apds9253Sensor for Apds9253AlsIr {}
}

/// Representation of an APDS9253
pub struct Apds9253<I2C, Kind>
where
    I2C: i2c::WriteRead + i2c::Write + i2c::Read,
    Kind: kind::Apds9253Sensor,
{
    i2c: I2C,
    _kind: core::marker::PhantomData<Kind>,
}

impl<I2C, I2cErr, Kind> Apds9253<I2C, Kind>
where
    I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr> + i2c::Read<Error = I2cErr>,
    Kind: kind::Apds9253Sensor,
{
    /// Creates a new instance of the light sensor
    pub fn new(i2c: I2C) -> Self {
        Self {
            i2c,
            _kind: core::marker::PhantomData,
        }
    }

    /// Destroy the sensor and return the hardware peripherals
    pub fn destroy(self) -> I2C {
        self.i2c
    }

    /// Sets a register value of the sensor
    fn set_register_value(&mut self, reg: Register, value: u8) -> Result<(), Error<I2cErr>> {
        self.i2c
            .write(I2C_ADDRESS, &[reg as u8, value])
            .map_err(|e| Error::I2C(e))
    }

    /// Reads a register value of the sensor
    fn read_register_value(&mut self, reg: Register) -> Result<u8, Error<I2cErr>> {
        let mut buf = [0; 1];

        self.i2c
            .write_read(I2C_ADDRESS, &[reg as u8], &mut buf)
            .map_err(|e| Error::I2C(e))?;
        Ok(buf[0])
    }

    /// Changes a specific bit in a register with consideration of the register value beforehand
    fn change_register_bit(
        &mut self,
        register: Register,
        bit: u8,
        value: bool,
    ) -> Result<(), Error<I2cErr>> {
        let mut read_buf = self.read_register_value(register)?;

        read_buf = match value {
            true => read_buf | bit,
            false => read_buf & !(bit),
        };

        self.set_register_value(register, read_buf)
    }

    /// Check whether the configured Sensor returns its correct ID
    ///
    /// Returns an Error if the ID is incorrect or if the I2C communication fails.
    pub fn check_id(&mut self) -> Result<(), Error<I2cErr>> {
        let value = self.read_register_value(Register::PartId)?;

        match value {
            APDS9253_ID => Ok(()),
            _ => Err(Error::ID),
        }
    }

    /// Read light sensor value of given channel. Private because not all channels are available always.
    /// Functions for reading the different channels are in the mode specific sensor implementations.
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    fn read_light_channel(&mut self, channel: Channel) -> Result<u32, Error<I2cErr>> {
        let mut buf = [0; 3];

        let channel_register = match channel {
            Channel::Red => Register::DataRed,
            Channel::Green => Register::DataGreen,
            Channel::Blue => Register::DataBlue,
            Channel::Ir => Register::DataIr,
        };

        match self
            .i2c
            .write_read(I2C_ADDRESS, &[channel_register as u8], &mut buf)
        {
            Ok(()) => Ok(((buf[2] & 0x0F) as u32) << 16 | (buf[1] as u32) << 8 | (buf[0] as u32)),
            Err(e) => Err(Error::I2C(e)),
        }
    }

    /// Read light sensor value of infrared channel, which is available in both RGB and ALS mode.
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    pub fn read_infrared(&mut self) -> Result<u32, Error<I2cErr>> {
        self.read_light_channel(Channel::Ir)
    }

    /// Status register with information about power-on, interrupt and data-ready
    ///
    /// Returns if the sensor is powered up, if an interrupt occurred and if data is available to read.
    pub fn read_status(&mut self) -> Result<SensorStatus, Error<I2cErr>> {
        let read_buf = self.read_register_value(Register::MainStatus)?;
        Ok(SensorStatus {
            powered_up: (read_buf << 5) != 0,
            interrupt_occurred: (read_buf << 4) != 0,
            data_available: (read_buf << 3) != 0,
        })
    }

    /// Resets the light sensor
    ///
    /// Software reset will be triggered immediately once the bit is set - no I2C response here
    pub fn reset(&mut self) -> Result<(), Error<I2cErr>> {
        self.set_register_value(Register::MainControl, 0x10)
    }

    /// Enables or disables the sensor. Disabling means that the sensor will enter standby mode.
    pub fn enable(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(Register::MainControl, 0x02, enable)
    }

    /// Enables or disables the SAI (SleepAfterInterrupt)
    ///
    /// If enabled, the light sensor returns to standby once an interrupt occurs
    pub fn enable_sai(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(Register::MainControl, 0x20, enable)
    }

    /// Light sensor analog gain
    ///
    /// Gain value can be set to Gain1, Gain3 (default), Gain6, Gain9 and Gain18.
    /// Writing to this register resets the sensor state machine and starts a new measurement.
    pub fn set_gain(&mut self, gain: Gain) -> Result<(), Error<I2cErr>> {
        self.set_register_value(Register::Gain, gain as u8)
    }

    /// Light sensor measurement rate and resolution
    ///
    /// Resolution from 20 bit (400ms) down to 13 bit (3.125ms) possible.
    /// Measurement rate from 25ms up to 2000ms possible.
    /// Writing to this register resets the sensor state machine and starts a new measurement.
    pub fn set_measurement_rate(
        &mut self,
        resolution: Resolution,
        rate: MeasurementRate,
    ) -> Result<(), Error<I2cErr>> {
        self.set_register_value(
            Register::MeasurementRate,
            (rate as u8) | (resolution as u8) << 4,
        )
    }

    /// Sets the channel for the interrupt
    fn set_interrupt_source(&mut self, source_channel: Channel) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(
            Register::InterruptConfiguration,
            0x10,
            (source_channel as u8) != 0,
        )
    }

    /// Sets the mode of the interrupt. Can be threshold or variance based
    fn set_interrupt_mode(&mut self, config: InterruptConfig) -> Result<(), Error<I2cErr>> {
        let mode = match config {
            InterruptConfig::Threshold(_, _, _, _) => false,
            InterruptConfig::Variance(_, _, _) => true,
        };
        self.change_register_bit(Register::InterruptConfiguration, 0x08, mode)
    }

    /// Sets the number (1-16) of similar consecutive interrupt events that must occur before an interrupt is asserted
    fn set_interrupt_persist(&mut self, persist: u8) -> Result<(), Error<I2cErr>> {
        let persist_constrained = match persist {
            // Sensor needs values from 0-15, but to be more user-friendly the given value will be subtracted by one at this point.
            p if p >= 16 => 15,
            0 => persist,
            _ => persist - 1,
        };

        self.set_register_value(Register::InterruptPersistance, persist_constrained << 5)
    }

    /// Interrupt variance threshold
    ///
    /// Interrupt will be fired when light sensor is set to variance mode and value varies by 'variance' counts
    /// compared to the previous value.
    /// Can be set to 8, 16, 32, 64, ... 1024 counts. Default is 8.
    fn set_variance(&mut self, variance: Variance) -> Result<(), Error<I2cErr>> {
        self.set_register_value(Register::ThresholdVariance, variance as u8)
    }

    /// Set both lower and upper interrupt threshold values (both 20 bit values)
    ///
    /// Takes the lower and upper threshold values as u32 but as the sensor only takes 20 bit values for the thresholds,
    /// the upper 12 bits (starting from the MSB) of the given u32 will be set to zero.
    /// If InterruptMode is set to Threshold, these values will define the lower and upper boundaries.
    /// So if the light value of the selected channel is lower than threshold.lower or higher than threshold.upper,
    /// the interrupt register will be set to 1 (depending on the persistance).
    fn set_thresholds(&mut self, lower: u32, upper: u32) -> Result<(), Error<I2cErr>> {
        let threshold_lower_20bit = lower & 0x0FFFFF;
        self.set_register_value(Register::ThresholdLower0, (threshold_lower_20bit) as u8)?;
        self.set_register_value(
            Register::ThresholdLower1,
            (threshold_lower_20bit >> 8) as u8,
        )?;
        self.set_register_value(
            Register::ThresholdLower2,
            (threshold_lower_20bit >> 16) as u8,
        )?;

        let threshold_upper_20bit = upper & 0x0FFFFF;
        self.set_register_value(Register::ThresholdUpper0, (threshold_upper_20bit) as u8)?;
        self.set_register_value(
            Register::ThresholdUpper1,
            (threshold_upper_20bit >> 8) as u8,
        )?;
        self.set_register_value(
            Register::ThresholdUpper2,
            (threshold_upper_20bit >> 16) as u8,
        )
    }

    /// Configuration of the sensors interrupt
    ///
    /// Configures the interrupt source channel, the persistance value and the thresholds lower and upper when in
    /// Threshold mode or the variance threshold value when in Variance mode.
    /// Lower and Upper thresholds are 20 bit values. The upper 12 bits (starting from the MSB) of both values will be set to zero.
    /// Persistance defines the number (1-16) of similar consecutive interrupt events that must occur before an interrupt is asserted.
    /// Everything above 16 will be replaced by the maximal value of 16. A minimal value of 1 is necessary.
    pub fn configure_interrupt(&mut self, config: InterruptConfig) -> Result<(), Error<I2cErr>> {
        self.set_interrupt_mode(config)?;
        match config {
            InterruptConfig::Threshold(lower, upper, persist, channel) => {
                self.set_interrupt_source(channel)?;
                self.set_thresholds(lower, upper)?;
                self.set_interrupt_persist(persist)?;
            }
            InterruptConfig::Variance(variance, persist, channel) => {
                self.set_interrupt_source(channel)?;
                self.set_variance(variance)?;
                self.set_interrupt_persist(persist)?;
            }
        }

        Ok(())
    }

    /// Enables or disables the interrupt
    pub fn enable_interrupt(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(Register::InterruptConfiguration, 0x04, enable)
    }

    /// Reads the dark count storage of a light channel
    ///
    /// Dark count of green channel is tested at 400ms and Gain18.
    /// Returns if the dark count is valid and the dark_count value in a custom type DarkCount.
    pub fn read_dark_count_storage(&mut self) -> Result<DarkCount, Error<I2cErr>> {
        let read_buf = self.read_register_value(Register::CountStorage)?;
        let dark_count_valid = (read_buf << 3) != 0;
        let dark_count_value = read_buf & 0x07;

        Ok(DarkCount {
            valid: dark_count_valid,
            value: dark_count_value,
        })
    }
}

/// Only available in RgbIr mode of the sensor
impl<I2C, I2cErr> Apds9253<I2C, kind::Apds9253RgbIr>
where
    I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr> + i2c::Read<Error = I2cErr>,
{
    /// Creates a new instance of the light sensor in RGB mode. The mode will be set in the init() function.
    pub fn new_rgb(i2c: I2C) -> Self {
        Self::new(i2c)
    }

    /// Initializes the light sensor in RGB mode. Red, Green, Blue and IR channel are active.
    pub fn init(&mut self) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(Register::MainControl, 0x04, (RgbMode::RgbIr as u8) != 0)
    }

    /// Read light sensor value of Red channel
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    pub fn read_red_channel(&mut self) -> Result<u32, Error<I2cErr>> {
        self.read_light_channel(Channel::Red)
    }

    /// Read light sensor value of Green channel
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    pub fn read_green_channel(&mut self) -> Result<u32, Error<I2cErr>> {
        self.read_light_channel(Channel::Green)
    }

    /// Read light sensor value of Blue channel
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    pub fn read_blue_channel(&mut self) -> Result<u32, Error<I2cErr>> {
        self.read_light_channel(Channel::Blue)
    }
}

/// Only available in AlsIr mode of the sensor
impl<I2C, I2cErr> Apds9253<I2C, kind::Apds9253AlsIr>
where
    I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr> + i2c::Read<Error = I2cErr>,
{
    /// Creates a new instance of the light sensor in ALS mode. The mode will be set in the init() function.
    pub fn new_als(i2c: I2C) -> Self {
        Self::new(i2c)
    }

    /// Initializes the light sensor in ALS mode. ALS (green) and IR channel are active.
    pub fn init(&mut self) -> Result<(), Error<I2cErr>> {
        self.change_register_bit(Register::MainControl, 0x04, (RgbMode::AlsIr as u8) != 0)
    }

    /// Read light sensor value of ALS (equivalent to green) channel
    ///
    /// Returns u32 value or Error, if the I2C communication fails
    pub fn read_ambient_light(&mut self) -> Result<u32, Error<I2cErr>> {
        self.read_light_channel(Channel::Green)
    }
}