ev3dev_rs/pupdevices/
infrared_sensor.rs

1use std::collections::HashSet;
2
3use crate::{
4    attribute::AttributeName,
5    error::{Ev3Error, Ev3Result},
6    parameters::{Button, SensorPort},
7    sensor_driver::{SensorDriver, SensorMode, SensorType},
8};
9
10/// Stock EV3 Infrared Sensor
11///
12/// Note that this sensor does not support direct measurement of distance
13/// and that the `proximity()` percentage does not scale linearly with distance.
14///
15/// If you want to get an accurate distance measurement, you should use an`UltrasonicSensor`.
16pub struct InfraredSensor {
17    driver: SensorDriver,
18}
19
20impl InfraredSensor {
21    /// Find an `InfraredSensor` on the given port.
22    ///
23    /// Will return `SensorNotFound` if no sensor is found
24    /// or `IncorrectSensorType` if the found sensor is not an `InfraredSensor`.
25    pub fn new(port: SensorPort) -> Ev3Result<Self> {
26        let driver = SensorDriver::new(SensorType::Infrared, port)?;
27        Ok(Self { driver })
28    }
29
30    /// Get the proximity value of the sensor as a percentage (0 to 100).
31    ///
32    /// 100% is approximately 70cm/27in.
33    ///
34    /// Note that this sensor does not support direct measurement of distance
35    /// and that the percentage does not scale linearly with distance.
36    ///
37    /// If you want to get an accurate distance measurement, you should use an `UltrasonicSensor`.
38    pub fn proximity(&self) -> Ev3Result<u8> {
39        if self.driver.mode.get() != SensorMode::InfraredProximity {
40            self.driver.set_mode(SensorMode::InfraredProximity)?;
41        }
42        Ok(self.driver.read_attribute(AttributeName::Value0)?.parse()?)
43    }
44
45    #[inline]
46    /// Get a `HashSet` of buttons currently pressed on the remote control channel 1.
47    ///
48    /// Note that the set will be empty if three or more buttons are pressed.
49    pub fn get_remote_channel_1_buttons(&self) -> Ev3Result<HashSet<Button>> {
50        self.get_remote_buttons(AttributeName::Value0)
51    }
52
53    /// Get a `HashSet` of buttons currently pressed on the remote control channel 2.
54    ///
55    /// Note that the set will be empty if three or more buttons are pressed.
56    #[inline]
57    pub fn get_remote_channel_2_buttons(&self) -> Ev3Result<HashSet<Button>> {
58        self.get_remote_buttons(AttributeName::Value1)
59    }
60
61    /// Get a `HashSet` of buttons currently pressed on the remote control channel 3.
62    ///
63    /// Note that the set will be empty if three or more buttons are pressed.
64    #[inline]
65    pub fn get_remote_channel_3_buttons(&self) -> Ev3Result<HashSet<Button>> {
66        self.get_remote_buttons(AttributeName::Value2)
67    }
68
69    /// Get a `HashSet` of buttons currently pressed on the remote control channel 4.
70    ///
71    /// Note that the set will be empty if three or more buttons are pressed.
72    #[inline]
73    pub fn get_remote_channel_4_buttons(&self) -> Ev3Result<HashSet<Button>> {
74        self.get_remote_buttons(AttributeName::Value3)
75    }
76
77    /// Seeks a remote control in beacon mode on channel 1.
78    ///
79    /// The first value is heading (-25 to 25),
80    /// and the second value is distance as a percentage (-128 and 0 to 100).
81    ///
82    /// The distance is -128 when the remote is out of range.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// let (heading, distance) = infrared_sensor.seek_channel_1()?;
88    /// ```
89    #[inline]
90    pub fn seek_channel_1(&self) -> Ev3Result<(i8, i8)> {
91        self.seek(AttributeName::Value0, AttributeName::Value1)
92    }
93
94    /// Seeks a remote control in beacon mode on channel 2.
95    ///
96    /// The first value is heading (-25 to 25),
97    /// and the second value is distance as a percentage (-128 and 0 to 100).
98    ///
99    /// The distance is -128 when the remote is out of range.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// let (heading, distance) = infrared_sensor.seek_channel_2()?;
105    /// ```
106    #[inline]
107    pub fn seek_channel_2(&self) -> Ev3Result<(i8, i8)> {
108        self.seek(AttributeName::Value2, AttributeName::Value3)
109    }
110
111    /// Seeks a remote control in beacon mode on channel 3.
112    ///
113    /// The first value is heading (-25 to 25),
114    /// and the second value is distance as a percentage (-128 and 0 to 100).
115    ///
116    /// The distance is -128 when the remote is out of range.
117    ///
118    /// # Examples
119    ///
120    /// ``` no_run
121    /// let (heading, distance) = infrared_sensor.seek_channel_3()?;
122    /// ```
123    #[inline]
124    pub fn seek_channel_3(&self) -> Ev3Result<(i8, i8)> {
125        self.seek(AttributeName::Value4, AttributeName::Value5)
126    }
127
128    /// Seeks a remote control in beacon mode on channel 4.
129    ///
130    /// The first value is heading (-25 to 25),
131    /// and the second value is distance as a percentage (-128 and 0 to 100).
132    ///
133    /// The distance is -128 when the remote is out of range.
134    ///
135    /// # Examples
136    ///
137    /// ``` no_run
138    /// let (heading, distance) = infrared_sensor.seek_channel_4()?;
139    /// ```
140    #[inline]
141    pub fn seek_channel_4(&self) -> Ev3Result<(i8, i8)> {
142        self.seek(AttributeName::Value6, AttributeName::Value7)
143    }
144
145    fn get_remote_buttons(&self, attr: AttributeName) -> Ev3Result<HashSet<Button>> {
146        if self.driver.mode.get() != SensorMode::InfraredRemote {
147            self.driver.set_mode(SensorMode::InfraredRemote)?;
148        }
149
150        let val = self.driver.read_attribute(attr)?;
151        let mut set = HashSet::new();
152
153        match val.parse::<u8>()? {
154            0 => (),
155            1 => _ = set.insert(Button::RedUp),
156            2 => _ = set.insert(Button::RedDown),
157            3 => _ = set.insert(Button::BlueUp),
158            4 => _ = set.insert(Button::BlueDown),
159            5 => {
160                _ = set.insert(Button::RedUp);
161                _ = set.insert(Button::BlueUp);
162            }
163            6 => {
164                _ = set.insert(Button::RedUp);
165                _ = set.insert(Button::BlueDown);
166            }
167            7 => {
168                _ = set.insert(Button::RedDown);
169                _ = set.insert(Button::BlueUp);
170            }
171            8 => {
172                _ = set.insert(Button::RedDown);
173                _ = set.insert(Button::BlueDown);
174            }
175            9 => _ = set.insert(Button::BeaconOn),
176            10 => {
177                _ = set.insert(Button::RedUp);
178                _ = set.insert(Button::RedDown);
179            }
180            11 => {
181                _ = set.insert(Button::BlueUp);
182                _ = set.insert(Button::BlueDown);
183            }
184            _ => {
185                return Err(Ev3Error::InvalidValue {
186                    func: "InfraredSensor::get_remote_buttons".into(),
187                    value: val,
188                });
189            }
190        };
191
192        Ok(set)
193    }
194
195    fn seek(&self, attr1: AttributeName, attr2: AttributeName) -> Ev3Result<(i8, i8)> {
196        if self.driver.mode.get() != SensorMode::InfraredSeek {
197            self.driver.set_mode(SensorMode::InfraredSeek)?;
198        }
199        Ok((
200            self.driver.read_attribute(attr1)?.parse()?,
201            self.driver.read_attribute(attr2)?.parse()?,
202        ))
203    }
204}