person_sensor/
lib.rs

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
//! # Useful Things Person Sensor
//!
//! A small driver for the Useful Things Person Sensor.
//!
//! Original [developer guide](https://usfl.ink/ps_dev)
//!
//! This driver has been tested with v1.1 of the sensor, but should also work with v1 and v2.
//! If you're able to validate the other board revisions, please open a pr to update this message :)
//!
//! ## Usage
//!
//! The sensor offers two modes: continuous and single shot.
//! It can be converted between the two modes, and the compiler will prevent you from misusing
//! functionality that is only available in a specific mode, or when an interrupt pin is provided.
//!
//! ```ignore
//! use person_sensor::PersonSensorBuilder;
//!
//! let i2c = /* ... */;
//! let interrupt_pin = /* ... */;
//!
//! // The driver can be initialized with or without the interrupt pin using the builder
//! let mut person_sensor = PersonSensorBuilder::new_standby(i2c, true)
//!     .with_interrupt(interrupt_pin) // optional
//!     .build()
//!     .await
//!     .unwrap();
//!
//! let detections = person_sensor.capture_once().await.unwrap();
//!
//! // ERROR: an interrupt pin was provided, but person_sensor is in standby mode
//! // person_sensor.wait_for_person().await.unwrap();
//!
//! // To use the functionality in continuous mode, convert the sensor like below,
//! // or use the builder with new_continuous(...)
//! let mut person_sensor = sensor.into_continuous_mode();
//!
//! // Now we meet all the requirements to wait for the next detection using the interrupt
//! _ = person_sensor.wait_for_person().await.unwrap();
//! // Read the latest detections.
//! // Note wait_for_person() does not automatically read the detections
//! let detections = person_sensor.get_detections().await.unwrap();
//! ```
//!
//! ## Examples
//!
//! To run the examples on a pi pico, it should be sufficient to enter bootloader mode and run:
//!
//! ```bash
//! cd examples
//! cargo run --bin <example_name> --release
//! ```

#![no_std]

mod person_sensor;
mod person_sensor_builder;

pub use person_sensor::PersonSensor;
pub use person_sensor::ReadError;
pub use person_sensor_builder::PersonSensorBuilder;

/// The number of detections returned by the sensor.
const MAX_DETECTIONS: usize = 4;

#[repr(C, packed)]
#[derive(Debug, Clone, PartialEq)]
pub struct Face {
    /// Confidence of the box prediction, ranges from 1 to 100.
    pub box_confidence: u8,
    pub box_left: u8,
    pub box_top: u8,
    pub box_right: u8,
    pub box_bottom: u8,
    /// The confidence of "calibrated" identities ranges from 1 to 100, and will be 0 or less if
    /// this face is not recognized as any of the calibrated identities or is not the largest face
    /// in the frame.
    pub id_confidence: i8,
    /// The ID number of the face, if it is recognized as any of the
    /// calibrated identities *and* is the largest face in the frame.
    /// By default, the sensor will not run any recognition until calibration has been performed.
    /// After at least one person has been calibrated, the sensor will always run recognition on
    /// the largest face present, and assign an ID number if it’s recognized as one that it has been calibrated on.
    pub id: Option<PersonID>,
    /// Indicates if somebody is looking directly at the device
    /// > Note: ID works most reliably when the face is straight on to the sensor
    pub is_facing: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PersonIDError {
    /// IDs can only range from 0 to 7.
    InvalidId,
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PersonID(u8);

impl PersonID {
    pub fn new(id: u8) -> Result<Self, PersonIDError> {
        if id < 8 {
            Ok(PersonID(id))
        } else {
            Err(PersonIDError::InvalidId)
        }
    }

    /// Create a new person ID without checking the value
    pub fn new_unchecked(id: u8) -> Self {
        PersonID(id)
    }
}

impl TryFrom<u8> for PersonID {
    type Error = PersonIDError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl From<PersonID> for u8 {
    fn from(id: PersonID) -> u8 {
        id.0
    }
}