#![no_std]
#![warn(missing_docs)]
use embedded_hal::blocking::i2c;
pub use kind::*;
const I2C_ADDRESS: u8 = 0x52;
const APDS9253_ID: u8 = 0xC2;
#[derive(Debug, Copy, Clone)]
enum Register {
MainControl = 0x00,
MeasurementRate = 0x04,
Gain = 0x05,
PartId = 0x06,
MainStatus = 0x07,
DataIr = 0x0A,
DataGreen = 0x0D,
DataBlue = 0x10,
DataRed = 0x13,
InterruptConfiguration = 0x19,
InterruptPersistance = 0x1A,
ThresholdUpper0 = 0x21,
ThresholdUpper1 = 0x22,
ThresholdUpper2 = 0x23,
ThresholdLower0 = 0x24,
ThresholdLower1 = 0x25,
ThresholdLower2 = 0x26,
ThresholdVariance = 0x27,
CountStorage = 0x29,
}
#[derive(Debug, Copy, Clone)]
pub enum State {
Enable = 1,
Disable = 0,
}
#[derive(Default, Debug, Copy, Clone)]
pub enum Channel {
Ir = 0,
#[default]
Green = 1,
Red = 2,
Blue = 3,
}
#[derive(Default, Debug, Copy, Clone)]
pub enum RgbMode {
#[default]
AlsIr = 0,
RgbIr = 1,
}
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum Gain {
Gain1 = 0,
#[default]
Gain3 = 1,
Gain6 = 2,
Gain9 = 3,
Gain18 = 4,
}
#[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,
}
#[derive(Default, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum Resolution {
Res20Bit = 0,
Res19Bit = 1,
#[default]
Res18Bit = 2,
Res17Bit = 3,
Res16Bit = 4,
Res13Bit = 5,
}
#[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,
}
#[derive(Debug, Copy, Clone)]
pub struct DarkCount {
pub valid: bool,
pub value: u8,
}
#[derive(Debug, Copy, Clone)]
pub struct SensorStatus {
pub powered_up: bool,
pub interrupt_occurred: bool,
pub data_available: bool,
}
#[derive(Debug, Copy, Clone)]
pub enum InterruptConfig {
Threshold(u32, u32, u8, Channel),
Variance(Variance, u8, Channel),
}
impl InterruptConfig {
pub fn new_threshold(lower: u32, upper: u32, persist: u8, channel: Channel) -> InterruptConfig {
InterruptConfig::Threshold(lower, upper, persist, channel)
}
pub fn new_variance(variance: Variance, persist: u8, channel: Channel) -> InterruptConfig {
InterruptConfig::Variance(variance, persist, channel)
}
}
#[derive(Debug, Copy, Clone)]
pub enum Error<E> {
I2C(E),
ID,
}
pub mod kind {
pub trait Apds9253Sensor {}
pub struct Apds9253RgbIr {}
impl Apds9253Sensor for Apds9253RgbIr {}
pub struct Apds9253AlsIr;
impl Apds9253Sensor for Apds9253AlsIr {}
}
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,
{
pub fn new(i2c: I2C) -> Self {
Self {
i2c,
_kind: core::marker::PhantomData,
}
}
pub fn destroy(self) -> I2C {
self.i2c
}
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))
}
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])
}
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)
}
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),
}
}
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)),
}
}
pub fn read_infrared(&mut self) -> Result<u32, Error<I2cErr>> {
self.read_light_channel(Channel::Ir)
}
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,
})
}
pub fn reset(&mut self) -> Result<(), Error<I2cErr>> {
self.set_register_value(Register::MainControl, 0x10)
}
pub fn enable(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
self.change_register_bit(Register::MainControl, 0x02, enable)
}
pub fn enable_sai(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
self.change_register_bit(Register::MainControl, 0x20, enable)
}
pub fn set_gain(&mut self, gain: Gain) -> Result<(), Error<I2cErr>> {
self.set_register_value(Register::Gain, gain as u8)
}
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,
)
}
fn set_interrupt_source(&mut self, source_channel: Channel) -> Result<(), Error<I2cErr>> {
self.change_register_bit(
Register::InterruptConfiguration,
0x10,
(source_channel as u8) != 0,
)
}
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)
}
fn set_interrupt_persist(&mut self, persist: u8) -> Result<(), Error<I2cErr>> {
let persist_constrained = match persist {
p if p >= 16 => 15,
0 => persist,
_ => persist - 1,
};
self.set_register_value(Register::InterruptPersistance, persist_constrained << 5)
}
fn set_variance(&mut self, variance: Variance) -> Result<(), Error<I2cErr>> {
self.set_register_value(Register::ThresholdVariance, variance as u8)
}
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,
)
}
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(())
}
pub fn enable_interrupt(&mut self, enable: bool) -> Result<(), Error<I2cErr>> {
self.change_register_bit(Register::InterruptConfiguration, 0x04, enable)
}
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,
})
}
}
impl<I2C, I2cErr> Apds9253<I2C, kind::Apds9253RgbIr>
where
I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr> + i2c::Read<Error = I2cErr>,
{
pub fn new_rgb(i2c: I2C) -> Self {
Self::new(i2c)
}
pub fn init(&mut self) -> Result<(), Error<I2cErr>> {
self.change_register_bit(Register::MainControl, 0x04, (RgbMode::RgbIr as u8) != 0)
}
pub fn read_red_channel(&mut self) -> Result<u32, Error<I2cErr>> {
self.read_light_channel(Channel::Red)
}
pub fn read_green_channel(&mut self) -> Result<u32, Error<I2cErr>> {
self.read_light_channel(Channel::Green)
}
pub fn read_blue_channel(&mut self) -> Result<u32, Error<I2cErr>> {
self.read_light_channel(Channel::Blue)
}
}
impl<I2C, I2cErr> Apds9253<I2C, kind::Apds9253AlsIr>
where
I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr> + i2c::Read<Error = I2cErr>,
{
pub fn new_als(i2c: I2C) -> Self {
Self::new(i2c)
}
pub fn init(&mut self) -> Result<(), Error<I2cErr>> {
self.change_register_bit(Register::MainControl, 0x04, (RgbMode::AlsIr as u8) != 0)
}
pub fn read_ambient_light(&mut self) -> Result<u32, Error<I2cErr>> {
self.read_light_channel(Channel::Green)
}
}