ev3dev_lang_rust/sensors/
infrared_sensor.rs

1//! LEGO EV3 infrared sensor.
2
3use super::{Sensor, SensorPort};
4use crate::{sensor_mode, Attribute, Device, Driver, Ev3Error, Ev3Result};
5use std::cell::RefCell;
6use std::collections::HashSet;
7use std::fmt;
8use std::rc::Rc;
9
10/// LEGO EV3 infrared sensor.
11#[derive(Debug, Clone, Device, Sensor)]
12pub struct InfraredSensor {
13    driver: Driver,
14}
15
16impl InfraredSensor {
17    fn new(driver: Driver) -> Self {
18        Self { driver }
19    }
20
21    findable!(
22        "lego-sensor",
23        ["lego-ev3-ir"],
24        SensorPort,
25        "InfraredSensor",
26        "in"
27    );
28
29    sensor_mode!(
30        "IR-PROX",
31        MODE_IR_PROX,
32        "Proximity",
33        set_mode_ir_prox,
34        is_mode_ir_prox
35    );
36    sensor_mode!(
37        "IR-SEEK",
38        MODE_IR_SEEK,
39        "IR Seeker",
40        set_mode_ir_seek,
41        is_mode_ir_seek
42    );
43    sensor_mode!(
44        "IR-REMOTE",
45        MODE_IR_REMOTE,
46        "IR Remote Control",
47        set_mode_ir_remote,
48        is_mode_ir_remote
49    );
50    sensor_mode!(
51        "IR-REM-A",
52        MODE_IR_REM_A,
53        "IR Remote Control",
54        set_mode_ir_rem_a,
55        is_mode_ir_rem_a
56    );
57    sensor_mode!(
58        "IR-S-ALT",
59        MODE_IR_S_ALT,
60        "Alternate IR Seeker ???",
61        set_mode_ir_s_alt,
62        is_mode_ir_s_alt
63    );
64    sensor_mode!(
65        "IR-CAL",
66        MODE_IR_CAL,
67        "Calibration ???",
68        set_mode_ir_cal,
69        is_mode_ir_cal
70    );
71
72    /// Get the proximity distance, in the range 0-100 (pct).
73    pub fn get_distance(&self) -> Ev3Result<i32> {
74        self.get_value0()
75    }
76}
77
78struct RemoteControlHelper {
79    last_buttons: i32,
80    pressed_buttons: HashSet<String>,
81}
82
83impl RemoteControlHelper {
84    fn new() -> RemoteControlHelper {
85        RemoteControlHelper {
86            last_buttons: 0,
87            pressed_buttons: HashSet::new(),
88        }
89    }
90
91    fn contains(&self, button: &str) -> bool {
92        self.pressed_buttons.contains(button)
93    }
94}
95
96/// Seeks EV3 Remote Controller in beacon mode.
97#[derive(Clone)]
98pub struct RemoteControl {
99    sensor: InfraredSensor,
100    channel: u8,
101    helper: Rc<RefCell<RemoteControlHelper>>,
102}
103
104// Manually implement Debug cause `buffer_cache` does not implement Debug.
105impl fmt::Debug for RemoteControl {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        f.debug_struct("RemoteControl")
108            .field("sensor", &self.sensor)
109            .field("channel", &self.channel)
110            .finish()
111    }
112}
113
114impl RemoteControl {
115    /// Wrap a InfraredSensor into a BeaconSeeker
116    pub fn new(sensor: InfraredSensor, channel: u8) -> Ev3Result<RemoteControl> {
117        sensor.set_mode_ir_remote()?;
118
119        Ok(RemoteControl {
120            sensor,
121            channel: u8::max(1, u8::min(4, channel)) - 1,
122            helper: Rc::new(RefCell::new(RemoteControlHelper::new())),
123        })
124    }
125
126    /// Checks if `red_up` button is pressed.
127    pub fn is_red_up(&self) -> bool {
128        self.helper.borrow().contains("red_up")
129    }
130
131    /// Checks if `red_down` button is pressed.
132    pub fn is_red_down(&self) -> bool {
133        self.helper.borrow().contains("red_down")
134    }
135
136    /// Checks if `blue_up` button is pressed.
137    pub fn is_blue_up(&self) -> bool {
138        self.helper.borrow().contains("blue_up")
139    }
140
141    /// Checks if `blue_down` button is pressed.
142    pub fn is_blue_down(&self) -> bool {
143        self.helper.borrow().contains("blue_down")
144    }
145
146    /// Checks if `beacon` button is pressed.
147    pub fn is_beacon(&self) -> bool {
148        self.helper.borrow().contains("beacon")
149    }
150
151    /// Check for currently pressed buttons. If the new state differs from the
152    /// old state, call the appropriate button event handlers.
153    pub fn process(&self) -> Ev3Result<()> {
154        let buttons = self.sensor.get_value(self.channel)?;
155
156        let mut helper = self.helper.borrow_mut();
157
158        if helper.last_buttons != buttons {
159            helper.last_buttons = buttons;
160
161            helper.pressed_buttons.clear();
162
163            match buttons {
164                1 => {
165                    helper.pressed_buttons.insert("red_up".to_owned());
166                }
167                2 => {
168                    helper.pressed_buttons.insert("red_down".to_owned());
169                }
170                3 => {
171                    helper.pressed_buttons.insert("blue_up".to_owned());
172                }
173                4 => {
174                    helper.pressed_buttons.insert("blue_down".to_owned());
175                }
176                5 => {
177                    helper.pressed_buttons.insert("red_up".to_owned());
178                    helper.pressed_buttons.insert("blue_up".to_owned());
179                }
180                6 => {
181                    helper.pressed_buttons.insert("red_up".to_owned());
182                    helper.pressed_buttons.insert("blue_down".to_owned());
183                }
184                7 => {
185                    helper.pressed_buttons.insert("red_down".to_owned());
186                    helper.pressed_buttons.insert("blue_up".to_owned());
187                }
188                8 => {
189                    helper.pressed_buttons.insert("red_down".to_owned());
190                    helper.pressed_buttons.insert("blue_down".to_owned());
191                }
192                9 => {
193                    helper.pressed_buttons.insert("beacon".to_owned());
194                }
195                10 => {
196                    helper.pressed_buttons.insert("red_up".to_owned());
197                    helper.pressed_buttons.insert("red_down".to_owned());
198                }
199                11 => {
200                    helper.pressed_buttons.insert("blue_up".to_owned());
201                    helper.pressed_buttons.insert("blue_down".to_owned());
202                }
203                _ => {}
204            }
205        }
206        Ok(())
207    }
208}
209
210/// Seeks EV3 Remote Controller in beacon mode.
211#[derive(Debug, Clone)]
212pub struct BeaconSeeker {
213    sensor: InfraredSensor,
214    channel: u8,
215}
216
217impl BeaconSeeker {
218    /// Wrap a InfraredSensor into a BeaconSeeker
219    pub fn new(sensor: InfraredSensor, channel: u8) -> Ev3Result<BeaconSeeker> {
220        sensor.set_mode_ir_seek()?;
221
222        Ok(BeaconSeeker {
223            sensor,
224            channel: u8::max(1, u8::min(4, channel)) - 1,
225        })
226    }
227
228    /// Returns heading (-25, 25) to the beacon on the given channel.
229    pub fn get_heading(&self) -> Ev3Result<i32> {
230        self.sensor.get_value(self.channel * 2)
231    }
232
233    /// Returns distance (0, 100) to the beacon on the given channel.
234    /// Returns -128 when beacon is not found.
235    pub fn get_distance(&self) -> Ev3Result<i32> {
236        self.sensor.get_value(self.channel * 2 + 1)
237    }
238
239    /// Returns heading and distance to the beacon on the given channel as a
240    /// tuple.
241    pub fn get_heading_and_distance(&self) -> Ev3Result<(i32, i32)> {
242        Ok((
243            self.sensor.get_value(self.channel * 2)?,
244            self.sensor.get_value(self.channel * 2 + 1)?,
245        ))
246    }
247}