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 async fn proximity(&self) -> Ev3Result<u8> {
39 self.driver.set_mode(SensorMode::InfraredProximity).await?;
40
41 Ok(self.driver.read_attribute(AttributeName::Value0).await?.parse()?)
42 }
43
44 #[inline]
45 /// Get a `HashSet` of buttons currently pressed on the remote control channel 1.
46 ///
47 /// Note that the set will be empty if three or more buttons are pressed.
48 pub async fn get_remote_channel_1_buttons(&self) -> Ev3Result<HashSet<Button>> {
49 self.get_remote_buttons(AttributeName::Value0).await
50 }
51
52 /// Get a `HashSet` of buttons currently pressed on the remote control channel 2.
53 ///
54 /// Note that the set will be empty if three or more buttons are pressed.
55 #[inline]
56 pub async fn get_remote_channel_2_buttons(&self) -> Ev3Result<HashSet<Button>> {
57 self.get_remote_buttons(AttributeName::Value1).await
58 }
59
60 /// Get a `HashSet` of buttons currently pressed on the remote control channel 3.
61 ///
62 /// Note that the set will be empty if three or more buttons are pressed.
63 #[inline]
64 pub async fn get_remote_channel_3_buttons(&self) -> Ev3Result<HashSet<Button>> {
65 self.get_remote_buttons(AttributeName::Value2).await
66 }
67
68 /// Get a `HashSet` of buttons currently pressed on the remote control channel 4.
69 ///
70 /// Note that the set will be empty if three or more buttons are pressed.
71 #[inline]
72 pub async fn get_remote_channel_4_buttons(&self) -> Ev3Result<HashSet<Button>> {
73 self.get_remote_buttons(AttributeName::Value3).await
74 }
75
76 /// Seeks a remote control in beacon mode on channel 1.
77 ///
78 /// The first value is heading (-25 to 25),
79 /// and the second value is distance as a percentage (-128 and 0 to 100).
80 ///
81 /// The distance is -128 when the remote is out of range.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// let (heading, distance) = infrared_sensor.seek_channel_1().await?;
87 /// ```
88 #[inline]
89 pub async fn seek_channel_1(&self) -> Ev3Result<(i8, i8)> {
90 self.seek(AttributeName::Value0, AttributeName::Value1).await
91 }
92
93 /// Seeks a remote control in beacon mode on channel 2.
94 ///
95 /// The first value is heading (-25 to 25),
96 /// and the second value is distance as a percentage (-128 and 0 to 100).
97 ///
98 /// The distance is -128 when the remote is out of range.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// let (heading, distance) = infrared_sensor.seek_channel_2().await?;
104 /// ```
105 #[inline]
106 pub async fn seek_channel_2(&self) -> Ev3Result<(i8, i8)> {
107 self.seek(AttributeName::Value2, AttributeName::Value3).await
108 }
109
110 /// Seeks a remote control in beacon mode on channel 3.
111 ///
112 /// The first value is heading (-25 to 25),
113 /// and the second value is distance as a percentage (-128 and 0 to 100).
114 ///
115 /// The distance is -128 when the remote is out of range.
116 ///
117 /// # Examples
118 ///
119 /// ``` no_run
120 /// let (heading, distance) = infrared_sensor.seek_channel_3().await?;
121 /// ```
122 #[inline]
123 pub async fn seek_channel_3(&self) -> Ev3Result<(i8, i8)> {
124 self.seek(AttributeName::Value4, AttributeName::Value5).await
125 }
126
127 /// Seeks a remote control in beacon mode on channel 4.
128 ///
129 /// The first value is heading (-25 to 25),
130 /// and the second value is distance as a percentage (-128 and 0 to 100).
131 ///
132 /// The distance is -128 when the remote is out of range.
133 ///
134 /// # Examples
135 ///
136 /// ``` no_run
137 /// let (heading, distance) = infrared_sensor.seek_channel_4().await?;
138 /// ```
139 #[inline]
140 pub async fn seek_channel_4(&self) -> Ev3Result<(i8, i8)> {
141 self.seek(AttributeName::Value6, AttributeName::Value7).await
142 }
143
144 async fn get_remote_buttons(&self, attr: AttributeName) -> Ev3Result<HashSet<Button>> {
145
146 self.driver.set_mode(SensorMode::InfraredRemote).await?;
147
148
149 let val = self.driver.read_attribute(attr).await?;
150 let mut set = HashSet::new();
151
152 match val.parse::<u8>()? {
153 0 => (),
154 1 => _ = set.insert(Button::RedUp),
155 2 => _ = set.insert(Button::RedDown),
156 3 => _ = set.insert(Button::BlueUp),
157 4 => _ = set.insert(Button::BlueDown),
158 5 => {
159 _ = set.insert(Button::RedUp);
160 _ = set.insert(Button::BlueUp);
161 }
162 6 => {
163 _ = set.insert(Button::RedUp);
164 _ = set.insert(Button::BlueDown);
165 }
166 7 => {
167 _ = set.insert(Button::RedDown);
168 _ = set.insert(Button::BlueUp);
169 }
170 8 => {
171 _ = set.insert(Button::RedDown);
172 _ = set.insert(Button::BlueDown);
173 }
174 9 => _ = set.insert(Button::BeaconOn),
175 10 => {
176 _ = set.insert(Button::RedUp);
177 _ = set.insert(Button::RedDown);
178 }
179 11 => {
180 _ = set.insert(Button::BlueUp);
181 _ = set.insert(Button::BlueDown);
182 }
183 _ => {
184 return Err(Ev3Error::InvalidValue {
185 func: "InfraredSensor::get_remote_buttons".into(),
186 value: val,
187 });
188 }
189 };
190
191 Ok(set)
192 }
193
194 async fn seek(&self, attr1: AttributeName, attr2: AttributeName) -> Ev3Result<(i8, i8)> {
195
196 self.driver.set_mode(SensorMode::InfraredSeek).await?;
197
198 Ok((
199 self.driver.read_attribute(attr1).await?.parse()?,
200 self.driver.read_attribute(attr2).await?.parse()?,
201 ))
202 }
203}