use core::marker::PhantomData;
use crc16::MCRF4XX;
use embedded_hal_async::{digital::Wait, i2c::I2c};
use crate::{Face, PersonID, MAX_DETECTIONS};
const PERSON_SENSOR_I2C_ADDRESS: u8 = 0x62;
#[repr(u8)]
pub(crate) enum PersonSensorMode {
Standby = 0x00,
Continuous = 0x01,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum IDMode {
LabelAndLocation = 0x01,
LocationOnly = 0x00,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ReadError<E> {
ChecksumMismatch,
I2CError(E),
}
impl<E> From<E> for ReadError<E> {
fn from(error: E) -> Self {
Self::I2CError(error)
}
}
pub struct ContinuousCaptureMode;
pub struct StandbyMode;
#[derive(Debug)]
pub struct PersonSensor<I2C, INT, MODE> {
pub(crate) i2c: I2C,
pub(crate) interrupt: INT,
pub(crate) mode: PhantomData<MODE>,
pub(crate) validate_checksum: bool,
}
impl<I2C, INT, MODE> PersonSensor<I2C, INT, MODE>
where
I2C: I2c,
{
async fn latest_results(
&mut self,
) -> Result<heapless::Vec<Face, MAX_DETECTIONS>, ReadError<I2C::Error>> {
let mut buffer = [0u8; 39];
self.i2c
.read(PERSON_SENSOR_I2C_ADDRESS, &mut buffer)
.await?;
if self.validate_checksum {
let checksum = crc16::State::<MCRF4XX>::calculate(&buffer[..37]);
if u16::from_le_bytes([buffer[37], buffer[38]]) != checksum {
return Err(ReadError::<I2C::Error>::ChecksumMismatch);
}
}
let mut faces = heapless::Vec::<Face, MAX_DETECTIONS>::new();
let num_faces = buffer[4];
#[expect(clippy::cast_possible_wrap)]
for face_num in 0..num_faces {
let face_start_offset = 5 + face_num as usize * 8;
let id_confidence = buffer[face_start_offset + 5] as i8;
let person_id = match id_confidence {
0 => None,
_ => Some(PersonID::new_unchecked(buffer[face_start_offset + 6])),
};
let face = Face {
box_confidence: buffer[face_start_offset],
box_left: buffer[face_start_offset + 1],
box_top: buffer[face_start_offset + 2],
box_right: buffer[face_start_offset + 3],
box_bottom: buffer[face_start_offset + 4],
id_confidence,
id: person_id,
is_facing: buffer[face_start_offset + 7] > 0,
};
match faces.push(face) {
Ok(()) => {}
Err(_) => break,
};
}
Ok(faces)
}
pub(crate) async fn set_mode(&mut self, mode: PersonSensorMode) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x01, mode as u8])
.await
}
pub async fn set_id_mode(&mut self, mode: IDMode) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x02, mode as u8])
.await
}
#[deprecated(
since = "0.3.1",
note = "Please use `set_id_mode` instead. This method will be removed in a future release."
)]
pub async fn enable_id_model(&mut self, enable: bool) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x02, u8::from(enable)])
.await
}
pub fn set_checksum_enabled(&mut self, enable: bool) {
self.validate_checksum = enable;
}
pub async fn label_next_id(&mut self, id: PersonID) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x04, id.into()])
.await
}
pub async fn set_persist_ids(&mut self, persist: bool) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x05, u8::from(persist)])
.await
}
pub async fn erase_ids(&mut self) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x06, 0x00])
.await
}
pub async fn set_indicator(&mut self, enabled: bool) -> Result<(), I2C::Error> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x07, u8::from(enabled)])
.await
}
}
impl<I2C, INT> PersonSensor<I2C, INT, StandbyMode>
where
I2C: I2c,
{
pub async fn capture_once(
&mut self,
) -> Result<heapless::Vec<Face, MAX_DETECTIONS>, ReadError<I2C::Error>> {
self.i2c
.write(PERSON_SENSOR_I2C_ADDRESS, &[0x03, 0x00])
.await?;
self.latest_results().await
}
pub async fn into_continuous_mode(
self,
) -> Result<PersonSensor<I2C, INT, ContinuousCaptureMode>, I2C::Error> {
let mut sensor = self;
sensor.set_mode(PersonSensorMode::Continuous).await?;
Ok(PersonSensor {
i2c: sensor.i2c,
interrupt: sensor.interrupt,
mode: PhantomData,
validate_checksum: sensor.validate_checksum,
})
}
}
impl<I2C, INT> PersonSensor<I2C, INT, ContinuousCaptureMode>
where
I2C: I2c,
{
pub async fn into_standby_mode(
self,
) -> Result<PersonSensor<I2C, INT, StandbyMode>, I2C::Error> {
let mut sensor = self;
sensor.set_mode(PersonSensorMode::Standby).await?;
Ok(PersonSensor {
i2c: sensor.i2c,
interrupt: sensor.interrupt,
mode: PhantomData,
validate_checksum: sensor.validate_checksum,
})
}
pub async fn get_detections(
&mut self,
) -> Result<heapless::Vec<Face, MAX_DETECTIONS>, ReadError<I2C::Error>> {
self.latest_results().await
}
}
impl<I2C, INT> PersonSensor<I2C, INT, ContinuousCaptureMode>
where
INT: Wait,
{
pub async fn wait_for_person(&mut self) -> Result<(), INT::Error> {
self.interrupt.wait_for_high().await
}
}