person_sensor/
lib.rs

1//! # Useful Things Person Sensor
2//!
3//! A small driver for the Useful Things Person Sensor.
4//!
5//! Original [developer guide](https://usfl.ink/ps_dev)
6//!
7//! This driver has been tested with v1.1 of the sensor, but should also work with v1 and v2.
8//! If you're able to validate the other board revisions, please open a PR to update this message :)
9//!
10//! ## Usage
11//!
12//! The sensor offers two modes: continuous and single shot.
13//! It can be converted between the two modes, and the compiler will prevent you from misusing
14//! functionality that is only available in a specific mode, or when an interrupt pin is provided.
15//!
16//! ```ignore
17//! use person_sensor::PersonSensorBuilder;
18//!
19//! let i2c = /* ... */;
20//! let interrupt_pin = /* ... */;
21//!
22//! // The driver can be initialized with or without the interrupt pin using the builder
23//! let mut person_sensor = PersonSensorBuilder::new_standby(i2c, true)
24//!     .with_interrupt(interrupt_pin) // optional
25//!     .build()
26//!     .await
27//!     .unwrap();
28//!
29//! let detections = person_sensor.capture_once().await.unwrap();
30//!
31//! // ERROR: an interrupt pin was provided, but person_sensor is in standby mode
32//! // person_sensor.wait_for_person().await.unwrap();
33//!
34//! // To use the functionality in continuous mode, convert the sensor like below,
35//! // or use the builder with new_continuous(...)
36//! let mut person_sensor = sensor.into_continuous_mode();
37//!
38//! // Now we meet all the requirements to wait for the next detection using the interrupt
39//! _ = person_sensor.wait_for_person().await.unwrap();
40//! // Read the latest detections.
41//! // Note wait_for_person() does not automatically read the detections
42//! let detections = person_sensor.get_detections().await.unwrap();
43//! ```
44//!
45//! ## Examples
46//!
47//! To run the examples on a pi pico, it should be sufficient to enter bootloader mode and run:
48//!
49//! ```bash
50//! cd examples
51//! cargo run --bin <example_name> --release
52//! ```
53
54#![no_std]
55
56mod person_sensor;
57mod person_sensor_builder;
58
59pub use person_sensor::IDMode;
60pub use person_sensor::PersonSensor;
61pub use person_sensor::ReadError;
62pub use person_sensor_builder::PersonSensorBuilder;
63
64/// The number of detections returned by the sensor.
65const MAX_DETECTIONS: usize = 4;
66
67#[repr(C, packed)]
68#[derive(Debug, Clone, PartialEq)]
69pub struct Face {
70    /// Confidence of the box prediction, ranges from 1 to 100.
71    pub box_confidence: u8,
72    pub box_left: u8,
73    pub box_top: u8,
74    pub box_right: u8,
75    pub box_bottom: u8,
76    /// The confidence of "calibrated" identities ranges from 1 to 100, and will be 0 or less if
77    /// this face is not recognized as any of the calibrated identities or is not the largest face
78    /// in the frame.
79    pub id_confidence: i8,
80    /// The ID number of the face, if it is recognized as any of the
81    /// calibrated identities *and* is the largest face in the frame.
82    /// By default, the sensor will not run any recognition until calibration has been performed.
83    /// After at least one person has been calibrated, the sensor will always run recognition on
84    /// the largest face present, and assign an ID number if it’s recognized as one that it has been calibrated on.
85    pub id: Option<PersonID>,
86    /// Indicates if somebody is looking directly at the device
87    /// > Note: ID works most reliably when the face is straight on to the sensor
88    pub is_facing: bool,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq)]
92pub enum PersonIDError {
93    /// IDs can only range from 0 to 7.
94    InvalidId,
95}
96
97#[repr(transparent)]
98#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
99pub struct PersonID(u8);
100
101impl PersonID {
102    pub fn new(id: u8) -> Result<Self, PersonIDError> {
103        if id < 8 {
104            Ok(PersonID(id))
105        } else {
106            Err(PersonIDError::InvalidId)
107        }
108    }
109
110    /// Create a new person ID without checking the value
111    #[must_use]
112    pub fn new_unchecked(id: u8) -> Self {
113        PersonID(id)
114    }
115}
116
117impl TryFrom<u8> for PersonID {
118    type Error = PersonIDError;
119    fn try_from(value: u8) -> Result<Self, Self::Error> {
120        Self::new(value)
121    }
122}
123
124impl From<PersonID> for u8 {
125    fn from(id: PersonID) -> u8 {
126        id.0
127    }
128}