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
127
128
129
130
131
132
133
134
135
136
137
138
//! # 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::PersonSensor;
//!
//! 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)
//!     .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();
//!
//! person_sensor.get_detections().await.unwrap();
//!
//! // 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_builder::PersonSensorBuilder;

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

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
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: i8,
    /// 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: u8,
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
struct ResultsHeader {
    reserved: [u8; 2],
    data_size: u16,
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct PersonSensorResults {
    header: ResultsHeader,
    pub num_faces: i8,
    pub faces: [Face; NUM_DETECTIONS],
    checksum: u16,
}

#[derive(Debug)]
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)
        }
    }
}

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
    }
}