pros_devices/smart/
optical.rs

1//! Optical sensor device
2
3use core::time::Duration;
4
5use pros_core::{bail_on, error::PortError, map_errno};
6use pros_sys::{OPT_GESTURE_ERR, PROS_ERR, PROS_ERR_F};
7use snafu::Snafu;
8
9use super::{SmartDevice, SmartDeviceType, SmartPort};
10
11/// Represents a smart port configured as a V5 optical sensor
12#[derive(Debug, Eq, PartialEq)]
13pub struct OpticalSensor {
14    port: SmartPort,
15    gesture_detection_enabled: bool,
16}
17
18impl OpticalSensor {
19    /// The smallest integration time you can set on an optical sensor.
20    pub const MIN_INTEGRATION_TIME: Duration = Duration::from_millis(3);
21
22    /// The largest integration time you can set on an optical sensor.
23    pub const MAX_INTEGRATION_TIME: Duration = Duration::from_millis(712);
24
25    /// The maximum value for the LED PWM.
26    pub const MAX_LED_PWM: u8 = 100;
27
28    /// Creates a new inertial sensor from a smart port index.
29    ///
30    /// Gesture detection features can be optionally enabled, allowing the use of [`Self::last_gesture_direction()`] and [`Self::last_gesture_direction()`].
31    pub fn new(port: SmartPort, gesture_detection_enabled: bool) -> Result<Self, OpticalError> {
32        let mut sensor = Self {
33            port,
34            gesture_detection_enabled,
35        };
36
37        if gesture_detection_enabled {
38            sensor.enable_gesture_detection()?;
39        } else {
40            sensor.disable_gesture_detection()?;
41        }
42
43        Ok(sensor)
44    }
45
46    /// Get the pwm value of the White LED. PWM value ranges from 0 to 100.
47    pub fn led_pwm(&self) -> Result<i32, OpticalError> {
48        unsafe {
49            Ok(bail_on!(
50                PROS_ERR,
51                pros_sys::optical_get_led_pwm(self.port.index())
52            ))
53        }
54    }
55
56    /// Sets the pwm value of the White LED. Valid values are in the range `0` `100`.
57    pub fn set_led_pwm(&mut self, value: u8) -> Result<(), OpticalError> {
58        if value > Self::MAX_LED_PWM {
59            return Err(OpticalError::InvalidLedPwm);
60        }
61        unsafe {
62            bail_on!(
63                PROS_ERR,
64                pros_sys::optical_set_led_pwm(self.port.index(), value)
65            );
66        }
67        Ok(())
68    }
69
70    /// Get integration time (update rate) of the optical sensor in milliseconds, with
71    /// minimum time being 3ms and the maximum time being 712ms.
72    pub fn integration_time(&self) -> Result<Duration, OpticalError> {
73        unsafe {
74            Ok(Duration::from_millis(bail_on!(
75                PROS_ERR_F,
76                pros_sys::optical_get_integration_time(self.port.index())
77            ) as u64))
78        }
79    }
80
81    /// Set integration time (update rate) of the optical sensor.
82    ///
83    /// Lower integration time results in faster update rates with lower accuracy
84    /// due to less available light being read by the sensor.
85    ///
86    /// Time value must be a [`Duration`] between 3 and 712 milliseconds. See
87    /// <https://www.vexforum.com/t/v5-optical-sensor-refresh-rate/109632/9> for
88    /// more information.
89    pub fn set_integration_time(&mut self, time: Duration) -> Result<(), OpticalError> {
90        if time < Self::MIN_INTEGRATION_TIME || time > Self::MAX_INTEGRATION_TIME {
91            return Err(OpticalError::InvalidIntegrationTime);
92        }
93
94        unsafe {
95            bail_on!(
96                PROS_ERR,
97                pros_sys::optical_set_integration_time(self.port.index(), time.as_millis() as f64)
98            );
99        }
100
101        Ok(())
102    }
103
104    /// Get the detected color hue.
105    ///
106    /// Hue has a range of `0` to `359.999`.
107    pub fn hue(&self) -> Result<f64, OpticalError> {
108        unsafe {
109            Ok(bail_on!(
110                PROS_ERR_F,
111                pros_sys::optical_get_hue(self.port.index())
112            ))
113        }
114    }
115
116    /// Gets the detected color saturation.
117    ///
118    /// Saturation has a range `0` to `1.0`.
119    pub fn saturation(&self) -> Result<f64, OpticalError> {
120        unsafe {
121            Ok(bail_on!(
122                PROS_ERR_F,
123                pros_sys::optical_get_saturation(self.port.index())
124            ))
125        }
126    }
127
128    /// Get the detected color brightness.
129    ///
130    /// Brightness values range from `0` to `1.0`.
131    pub fn brightness(&self) -> Result<f64, OpticalError> {
132        unsafe {
133            Ok(bail_on!(
134                PROS_ERR_F,
135                pros_sys::optical_get_brightness(self.port.index())
136            ))
137        }
138    }
139
140    /// Get the detected proximity value
141    ///
142    /// Proximity has a range of `0` to `255`.
143    pub fn proximity(&self) -> Result<i32, OpticalError> {
144        unsafe {
145            Ok(bail_on!(
146                PROS_ERR,
147                pros_sys::optical_get_proximity(self.port.index())
148            ))
149        }
150    }
151
152    /// Get the processed RGBC data from the sensor
153    pub fn rgbc(&self) -> Result<Rgbc, OpticalError> {
154        unsafe { pros_sys::optical_get_rgb(self.port.index()).try_into() }
155    }
156
157    /// Get the raw, unprocessed RGBC data from the sensor
158    pub fn rgbc_raw(&self) -> Result<RgbcRaw, OpticalError> {
159        unsafe { pros_sys::optical_get_raw(self.port.index()).try_into() }
160    }
161
162    /// Enables gesture detection features on the sensor.
163    ///
164    /// This allows [`Self::last_gesture_direction()`] and [`Self::last_gesture_direction()`] to be called without error, if
165    /// gesture detection wasn't already enabled.
166    pub fn enable_gesture_detection(&mut self) -> Result<(), OpticalError> {
167        bail_on!(PROS_ERR, unsafe {
168            pros_sys::optical_enable_gesture(self.port.index())
169        });
170
171        self.gesture_detection_enabled = true;
172        Ok(())
173    }
174
175    /// Disables gesture detection features on the sensor.
176    pub fn disable_gesture_detection(&mut self) -> Result<(), OpticalError> {
177        bail_on!(PROS_ERR, unsafe {
178            pros_sys::optical_disable_gesture(self.port.index())
179        });
180
181        self.gesture_detection_enabled = false;
182        Ok(())
183    }
184
185    /// Determine if gesture detection is enabled or not on the sensor.
186    pub const fn gesture_detection_enabled(&self) -> bool {
187        self.gesture_detection_enabled
188    }
189
190    /// Get the most recent gesture data from the sensor. Gestures will be cleared after 500mS.
191    ///
192    /// Will return [`OpticalError::GestureDetectionDisabled`] if the sensor is not
193    /// confgured to detect gestures.
194    pub fn last_gesture_direction(&self) -> Result<GestureDirection, OpticalError> {
195        if !self.gesture_detection_enabled {
196            return Err(OpticalError::GestureDetectionDisabled);
197        }
198
199        unsafe { pros_sys::optical_get_gesture(self.port.index()).try_into() }
200    }
201
202    /// Get the most recent raw gesture data from the sensor.
203    ///
204    /// Will return [`OpticalError::GestureDetectionDisabled`] if the sensor is not
205    /// confgured to detect gestures.
206    pub fn last_gesture_raw(&self) -> Result<GestureRaw, OpticalError> {
207        if !self.gesture_detection_enabled {
208            return Err(OpticalError::GestureDetectionDisabled);
209        }
210
211        unsafe { pros_sys::optical_get_gesture_raw(self.port.index()).try_into() }
212    }
213}
214
215impl SmartDevice for OpticalSensor {
216    fn port_index(&self) -> u8 {
217        self.port.index()
218    }
219
220    fn device_type(&self) -> SmartDeviceType {
221        SmartDeviceType::Optical
222    }
223}
224
225#[derive(Default, Debug, Clone, Copy, PartialEq)]
226/// Represents a gesture and its direction.
227pub enum GestureDirection {
228    /// Up gesture.
229    Up,
230    /// Down gesture.
231    Down,
232    /// Left gesture.
233    Left,
234    /// Right gesture.
235    Right,
236    /// Gesture error.
237    Error,
238    #[default]
239    /// No gesture detected.
240    NoGesture,
241}
242
243impl TryFrom<pros_sys::optical_direction_e_t> for GestureDirection {
244    type Error = OpticalError;
245
246    fn try_from(value: pros_sys::optical_direction_e_t) -> Result<GestureDirection, OpticalError> {
247        bail_on!(pros_sys::E_OPTICAL_DIRECTION_ERROR, value);
248
249        Ok(match value {
250            pros_sys::E_OPTICAL_DIRECTION_UP => Self::Up,
251            pros_sys::E_OPTICAL_DIRECTION_DOWN => Self::Down,
252            pros_sys::E_OPTICAL_DIRECTION_LEFT => Self::Left,
253            pros_sys::E_OPTICAL_DIRECTION_RIGHT => Self::Right,
254            pros_sys::E_OPTICAL_DIRECTION_NO_GESTURE => Self::NoGesture,
255            _ => unreachable!("Encountered unknown gesture direction code."),
256        })
257    }
258}
259
260#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
261/// Raw gesture data from an [`OpticalSensor`].
262pub struct GestureRaw {
263    /// Up value.
264    pub up: u8,
265    /// Down value.
266    pub down: u8,
267    /// Left value.
268    pub left: u8,
269    /// Right value.
270    pub right: u8,
271    /// Gesture type.
272    pub gesture_type: u8,
273    /// The count of the gesture.
274    pub count: u16,
275    /// The time of the gesture.
276    pub time: u32,
277}
278
279impl TryFrom<pros_sys::optical_gesture_s_t> for GestureRaw {
280    type Error = OpticalError;
281
282    fn try_from(value: pros_sys::optical_gesture_s_t) -> Result<GestureRaw, OpticalError> {
283        Ok(Self {
284            up: bail_on!(OPT_GESTURE_ERR as u8, value.udata),
285            down: value.ddata,
286            left: value.ldata,
287            right: value.rdata,
288            gesture_type: value.r#type,
289            count: value.count,
290            time: value.time,
291        })
292    }
293}
294
295#[derive(Default, Debug, Clone, Copy, PartialEq)]
296/// RGBC data from a [`OpticalSensor`].
297pub struct Rgbc {
298    /// The red value from the sensor.
299    pub red: f64,
300    /// The green value from the sensor.
301    pub green: f64,
302    /// The blue value from the sensor.
303    pub blue: f64,
304    /// The brightness value from the sensor.
305    pub brightness: f64,
306}
307
308impl TryFrom<pros_sys::optical_rgb_s_t> for Rgbc {
309    type Error = OpticalError;
310
311    fn try_from(value: pros_sys::optical_rgb_s_t) -> Result<Rgbc, OpticalError> {
312        Ok(Self {
313            red: bail_on!(PROS_ERR_F, value.red), // Docs incorrectly claim this is PROS_ERR
314            green: value.green,
315            blue: value.blue,
316            brightness: value.brightness,
317        })
318    }
319}
320
321#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
322/// Represents the raw RGBC data from the sensor.
323pub struct RgbcRaw {
324    /// The red value from the sensor.
325    pub red: u32,
326    /// The green value from the sensor.
327    pub green: u32,
328    /// The blue value from the sensor.
329    pub blue: u32,
330    /// The clear value from the sensor.
331    pub clear: u32,
332}
333
334impl TryFrom<pros_sys::optical_raw_s_t> for RgbcRaw {
335    type Error = OpticalError;
336
337    fn try_from(value: pros_sys::optical_raw_s_t) -> Result<RgbcRaw, OpticalError> {
338        Ok(Self {
339            clear: bail_on!(PROS_ERR_F as u32, value.clear),
340            red: value.red,
341            green: value.green,
342            blue: value.blue,
343        })
344    }
345}
346
347#[derive(Debug, Snafu)]
348/// Errors that can occur when interacting with an optical sensor.
349pub enum OpticalError {
350    /// Invalid LED PWM value, must be between 0 and 100.
351    InvalidLedPwm,
352
353    /// Integration time must be between 3 and 712 milliseconds.
354    ///
355    /// See <https://www.vexforum.com/t/v5-optical-sensor-refresh-rate/109632/9> for more information.
356    InvalidIntegrationTime,
357
358    /// Gesture detection is not enabled for this sensor.
359    GestureDetectionDisabled,
360
361    #[snafu(display("{source}"), context(false))]
362    /// Generic port related error.
363    Port {
364        /// The source of the error
365        source: PortError,
366    },
367}
368
369map_errno! {
370    OpticalError {} inherit PortError;
371}