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}