#![no_std] #[cfg(not(feature = "async"))] use embedded_hal::{ delay::DelayNs, i2c::{self}, }; #[cfg(feature = "async")] use embedded_hal_async::{ delay::DelayNs, i2c::{self}, }; use maybe_async::maybe_async; const AS7265X_ADDR: u8 = 0x49; const STATUS_REG: u8 = 0x00; const WRITE_REG: u8 = 0x01; const READ_REG: u8 = 0x02; const TX_VALID: u8 = 0x02; const RX_VALID: u8 = 0x01; const HW_VERSION_HIGH_ADDR: u8 = 0x00; const HW_VERSION_LOW_ADDR: u8 = 0x01; const FW_VERSION_HIGH_ADDR: u8 = 0x02; const FW_VERSION_LOW_ADDR: u8 = 0x03; const CONFIG_ADDR: u8 = 0x04; const INTEGRATION_ADDR: u8 = 0x05; const TEMP_ADDR: u8 = 0x06; const LED_ADDR: u8 = 0x07; const DEV_SELECT_ADDR: u8 = 0x4F; const SOFT_RESET: u8 = 0b10000000; const ENABLE_INTERRUPT: u8 = 0b01000000; const CONFIG_GAIN_MASK: u8 = 0x30; const CONFIG_GAIN_SHIFT: u8 = 4; const CONFIG_BANK_MASK: u8 = 0x0C; const CONFIG_BANK_SHIFT: u8 = 2; const CONFIG_DATA_RDY: u8 = 0x02; const LED_DRV_ICL_MASK: u8 = 0x30; const LED_DRV_ICL_SHIFT: u8 = 4; const LED_DRV_ENABLE: u8 = 0x08; const LED_IND_ICL_MASK: u8 = 0x06; const LED_IND_ICL_SHIFT: u8 = 1; const LED_IND_ENABLE: u8 = 0x01; #[derive(Debug, Clone, Copy)] pub enum Channel { /// 410nm - AS72653 A, /// 435nm - AS72653 B, /// 460nm - AS72653 C, /// 485nm - AS72653 D, /// 510nm - AS72653 E, /// 535nm - AS72653 F, /// 560nm - AS72652 G, /// 585nm - AS72652 H, /// 610nm - AS72651 R, /// 645nm - AS72652 I, /// 680nm - AS72651 S, /// 705nm - AS72652 J, /// 730nm - AS72651 T, /// 760nm - AS72651 U, /// 810nm - AS72651 V, /// 860nm - AS72651 W, /// 900nm - AS72652 K, /// 940nm - AS72652 L, } impl Channel { /// Each channel is only read from one of three sensors pub fn get_device_for_channel(&self) -> Device { match self { Channel::A | Channel::B | Channel::C | Channel::D | Channel::E | Channel::F => { Device::Uv } Channel::G | Channel::H | Channel::I | Channel::J | Channel::K | Channel::L => { Device::Visable } Channel::R | Channel::S | Channel::T | Channel::U | Channel::V | Channel::W => { Device::Nir } } } /// Get the virtual register address for the raw value of the channel fn get_raw_address(&self) -> u8 { match self { Channel::A | Channel::G | Channel::R => 0x08, Channel::B | Channel::H | Channel::S => 0x0A, Channel::C | Channel::I | Channel::T => 0x0C, Channel::D | Channel::J | Channel::U => 0x0E, Channel::E | Channel::K | Channel::V => 0x10, Channel::F | Channel::L | Channel::W => 0x12, } } /// Get the virtual register address for the calibrated value of the channel fn get_cal_address(&self) -> u8 { let raw = self.get_raw_address(); ((raw - 0x08) / 2) * 4 + 0x14 } } /// The AS7265X has three sensor devices #[derive(Debug, Clone, Copy)] pub enum Device { Visable = 0x00, Nir = 0x01, Uv = 0x02, } /// The three main LEDs on the device #[derive(Debug, Clone, Copy)] pub enum Led { White, Ir, Uv, } impl Led { /// Apparently the LEDs don't match the device the are for fn get_device(&self) -> Device { match self { Led::White => Device::Nir, Led::Ir => Device::Visable, Led::Uv => Device::Uv, } } } #[derive(Debug, Clone, Copy)] pub enum GainConfig { /// x1 Gain1x, /// x3.7 Gain3_7x, /// x16 Gain16x, /// x64 Gain64x, } impl GainConfig { /// To byte that can be written to the virtual register fn to_byte(self) -> u8 { match self { GainConfig::Gain1x => 0b00, GainConfig::Gain3_7x => 0b01, GainConfig::Gain16x => 0b10, GainConfig::Gain64x => 0b11, } } } #[derive(Debug, Clone, Copy)] pub enum MeasurementMode { /// 4 channels Mode0, /// 4 channels Mode1, /// All 6 channels Mode2Continuous, /// One-Shot operation of mode 2 Mode3OneShot, } impl MeasurementMode { /// To byte that can be written to the virtual register fn to_byte(self) -> u8 { match self { MeasurementMode::Mode0 => 0b00, MeasurementMode::Mode1 => 0b01, MeasurementMode::Mode2Continuous => 0b10, MeasurementMode::Mode3OneShot => 0b11, } } } #[derive(Debug, Clone)] pub struct AS7265XConfig { /// Enable the onboard interrupt pin pub enable_interrupt: bool, pub gain: GainConfig, pub measurement_mode: MeasurementMode, /// Integration time: value * 2.8ms pub integration_time: u8, } impl Default for AS7265XConfig { fn default() -> Self { Self { enable_interrupt: true, gain: GainConfig::Gain64x, measurement_mode: MeasurementMode::Mode3OneShot, integration_time: 49, } } } impl AS7265XConfig { /// Convert to byte to write to the config virtual register fn to_byte(&self) -> u8 { let mut byte = 0u8; if self.enable_interrupt { byte |= ENABLE_INTERRUPT; } byte |= (self.gain.to_byte() << CONFIG_GAIN_SHIFT) & CONFIG_GAIN_MASK; byte |= (self.measurement_mode.to_byte() << CONFIG_BANK_SHIFT) & CONFIG_BANK_MASK; byte } } #[derive(Debug, Clone, Copy)] pub enum LedDriverCurrent { /// 12.5 mA Ma12_5, /// 25 mA Ma25, /// 50 mA Ma50, /// 100 mA Ma100, } impl LedDriverCurrent { fn to_bits(self) -> u8 { match self { LedDriverCurrent::Ma12_5 => 0b00, LedDriverCurrent::Ma25 => 0b01, LedDriverCurrent::Ma50 => 0b10, LedDriverCurrent::Ma100 => 0b11, } } } #[derive(Debug, Clone, Copy)] pub enum LedIndicatorCurrent { /// 1 mA Ma1, /// 2 mA Ma2, /// 4 mA Ma4, /// 8 mA Ma8, } impl LedIndicatorCurrent { fn to_bits(self) -> u8 { match self { LedIndicatorCurrent::Ma1 => 0b00, LedIndicatorCurrent::Ma2 => 0b01, LedIndicatorCurrent::Ma4 => 0b10, LedIndicatorCurrent::Ma8 => 0b11, } } } #[derive(Debug, Clone, Copy)] pub struct LedConfig { pub drv_enabled: bool, pub drv_current: LedDriverCurrent, pub ind_enabled: bool, pub ind_current: LedIndicatorCurrent, } impl LedConfig { /// To byte that can be written to the virtual register fn to_byte(self) -> u8 { let mut byte = 0u8; if self.drv_enabled { byte |= LED_DRV_ENABLE; } byte |= (self.drv_current.to_bits() << LED_DRV_ICL_SHIFT) & LED_DRV_ICL_MASK; if self.ind_enabled { byte |= LED_IND_ENABLE; } byte |= (self.ind_current.to_bits() << LED_IND_ICL_SHIFT) & LED_IND_ICL_MASK; byte } } impl Default for LedConfig { fn default() -> Self { Self { drv_enabled: false, drv_current: LedDriverCurrent::Ma12_5, ind_enabled: false, ind_current: LedIndicatorCurrent::Ma8, } } } #[derive(Debug)] pub enum Error { I2c(E), Timeout, } impl From for Error { fn from(e: E) -> Self { Error::I2c(e) } } /// The AS7265X device pub struct AS7265X where I: i2c::I2c, D: DelayNs, { device: I, delay: D, config: AS7265XConfig, } #[maybe_async] impl AS7265X where I: i2c::I2c, D: DelayNs, { /// Create a new AS7265X device pub fn new(device: I, config: AS7265XConfig, delay: D) -> Self { Self { device, config, delay, } } /// Initialize the device. /// Writes config, integration time and led config. /// Ok to call multiple times & most likly not need but the cpp driver also does this. #[maybe_async] pub async fn init(&mut self) -> Result<(), Error> { self.write_config().await?; self.set_integration_time(self.config.integration_time) .await?; self.set_led_config( Led::White, LedConfig { ..Default::default() }, ) .await?; self.set_led_config( Led::Ir, LedConfig { ..Default::default() }, ) .await?; self.set_led_config( Led::Uv, LedConfig { ..Default::default() }, ) .await?; Ok(()) } /// Perform a soft reset #[maybe_async] pub async fn soft_reset(&mut self) -> Result<(), Error> { self.write_virtual_register(CONFIG_ADDR, SOFT_RESET).await?; self.delay.delay_ms(100).await; Ok(()) } /// Write the config to the device /// Also need to fire the oneshot mode again #[maybe_async] async fn write_config(&mut self) -> Result<(), Error> { let config_byte = self.config.to_byte(); self.write_virtual_register(CONFIG_ADDR, config_byte).await } /// Sets the integration time value /// value * 2.8ms #[maybe_async] pub async fn set_integration_time(&mut self, time: u8) -> Result<(), Error> { self.config.integration_time = time; self.write_virtual_register(INTEGRATION_ADDR, time).await } /// Write the LED specific config #[maybe_async] async fn set_led_config(&mut self, led: Led, config: LedConfig) -> Result<(), Error> { self.select_device(led.get_device()).await?; let config_byte = config.to_byte(); self.write_virtual_register(LED_ADDR, config_byte).await } /// Get the hardware info from the hw version register #[maybe_async] pub async fn get_device_type(&mut self) -> Result> { self.read_virtual_register(HW_VERSION_HIGH_ADDR).await } /// Get the hardware info from the hw version register #[maybe_async] pub async fn get_hardware_version(&mut self) -> Result> { self.read_virtual_register(HW_VERSION_LOW_ADDR).await } /// Alot of function only work if you have the correct device selected #[maybe_async] async fn select_device(&mut self, device: Device) -> Result<(), Error> { self.write_virtual_register(DEV_SELECT_ADDR, device as u8) .await } /// Read the integrated temperature sensor in °C #[maybe_async] pub async fn read_temperature(&mut self) -> Result> { let temp = self.read_virtual_register(TEMP_ADDR).await?; Ok(temp as i8) } /// Check if the internal register reports data ready #[maybe_async] pub async fn is_data_ready(&mut self) -> Result> { let config = self.read_virtual_register(CONFIG_ADDR).await?; Ok((config & CONFIG_DATA_RDY) != 0) } /// Run a messurement /// Only needed if messurement mode is set to One-Shot /// Basicly rewrites the config and waits the current integration time #[maybe_async] pub async fn measure(&mut self) -> Result<(), Error> { self.write_config().await?; let wait_time = (self.config.integration_time as u32) * 28 / 10 * 2; self.delay.delay_ms(wait_time).await; Ok(()) } /// Same as measure but enable LED befor and disable after #[maybe_async] pub async fn mesure_with_bulb(&mut self) -> Result<(), Error> { self.set_led_config( Led::White, LedConfig { drv_enabled: true, ..Default::default() }, ) .await?; self.set_led_config( Led::Ir, LedConfig { drv_enabled: true, ..Default::default() }, ) .await?; self.set_led_config( Led::Uv, LedConfig { drv_enabled: true, ..Default::default() }, ) .await?; self.measure().await?; self.set_led_config( Led::White, LedConfig { ..Default::default() }, ) .await?; self.set_led_config( Led::Ir, LedConfig { ..Default::default() }, ) .await?; self.set_led_config( Led::Uv, LedConfig { ..Default::default() }, ) .await?; Ok(()) } /// Read the raw value for a channel #[maybe_async] pub async fn read_raw_measurement(&mut self, channel: Channel) -> Result> { let dev = channel.get_device_for_channel(); self.select_device(dev).await?; self.read_raw_channel(channel.get_raw_address()).await } /// Read the calibrated value for a channel #[maybe_async] pub async fn read_calibrated_messurement( &mut self, channel: Channel, ) -> Result> { let dev = channel.get_device_for_channel(); self.select_device(dev).await?; self.read_calibrated_channel(channel.get_cal_address()) .await } /// Read u16 value from address #[maybe_async] async fn read_raw_channel(&mut self, high_byte_addr: u8) -> Result> { let high = self.read_virtual_register(high_byte_addr).await?; let low = self.read_virtual_register(high_byte_addr + 1).await?; Ok(((high as u16) << 8) | (low as u16)) } /// Read f32 value from address #[maybe_async] async fn read_calibrated_channel( &mut self, high_byte_addr: u8, ) -> Result> { let b0 = self.read_virtual_register(high_byte_addr).await?; let b1 = self.read_virtual_register(high_byte_addr + 1).await?; let b2 = self.read_virtual_register(high_byte_addr + 2).await?; let b3 = self.read_virtual_register(high_byte_addr + 3).await?; let bits = u32::from_be_bytes([b0, b1, b2, b3]); Ok(f32::from_bits(bits)) } /// Write to a virtual register #[maybe_async] async fn write_virtual_register( &mut self, virtual_addr: u8, data: u8, ) -> Result<(), Error> { const MAX_RETRIES: u8 = 100; for _ in 0..MAX_RETRIES { let mut status = [0u8; 1]; self.device .write_read(AS7265X_ADDR, &[STATUS_REG], &mut status) .await?; if (status[0] & TX_VALID) == 0 { break; } self.delay.delay_ms(5).await; } self.device .write(AS7265X_ADDR, &[WRITE_REG, 0x80 | virtual_addr]) .await?; for _ in 0..MAX_RETRIES { let mut status = [0u8; 1]; self.device .write_read(AS7265X_ADDR, &[STATUS_REG], &mut status) .await?; if (status[0] & TX_VALID) == 0 { break; } self.delay.delay_ms(5).await; } self.device.write(AS7265X_ADDR, &[WRITE_REG, data]).await?; Ok(()) } /// Read from a virtual register #[maybe_async] async fn read_virtual_register(&mut self, virtual_addr: u8) -> Result> { const MAX_RETRIES: u8 = 100; for _ in 0..MAX_RETRIES { let mut status = [0u8; 1]; self.device .write_read(AS7265X_ADDR, &[STATUS_REG], &mut status) .await?; if (status[0] & TX_VALID) == 0 { break; } self.delay.delay_ms(5).await; } self.device .write(AS7265X_ADDR, &[WRITE_REG, virtual_addr]) .await?; for _ in 0..MAX_RETRIES { let mut status = [0u8; 1]; self.device .write_read(AS7265X_ADDR, &[STATUS_REG], &mut status) .await?; if (status[0] & RX_VALID) != 0 { break; } self.delay.delay_ms(5).await; } let mut data = [0u8; 1]; self.device .write_read(AS7265X_ADDR, &[READ_REG], &mut data) .await?; Ok(data[0]) } }