ev3dev_lang_rust/
ev3.rs

1//! EV3 specific features
2
3use std::fs;
4
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::collections::HashSet;
8use std::fmt;
9use std::fs::File;
10use std::os::unix::io::AsRawFd;
11use std::path::Path;
12use std::rc::Rc;
13
14use paste::paste;
15
16use crate::driver::DRIVER_PATH;
17use crate::utils::OrErr;
18use crate::{Attribute, Ev3Result};
19
20/// Color type.
21pub type Color = (u8, u8);
22
23/// The led's on top of the EV3 brick.
24#[derive(Debug, Clone)]
25pub struct Led {
26    left_red: Attribute,
27    left_green: Attribute,
28    right_red: Attribute,
29    right_green: Attribute,
30}
31
32impl Led {
33    /// Led off.
34    pub const COLOR_OFF: Color = (0, 0);
35
36    /// Led color red
37    pub const COLOR_RED: Color = (255, 0);
38
39    /// Led color green.
40    pub const COLOR_GREEN: Color = (0, 255);
41
42    /// Led color amber.
43    pub const COLOR_AMBER: Color = (255, 255);
44
45    /// Led color orange.
46    pub const COLOR_ORANGE: Color = (255, 128);
47
48    /// LED color yellow.
49    pub const COLOR_YELLOW: Color = (25, 255);
50
51    /// Create a new instance of the `Led` struct.
52    pub fn new() -> Ev3Result<Led> {
53        let mut left_red_name = String::new();
54        let mut left_green_name = String::new();
55        let mut right_red_name = String::new();
56        let mut right_green_name = String::new();
57
58        let paths = fs::read_dir(Path::new(DRIVER_PATH).join("leds"))?;
59
60        for path in paths {
61            let file_name = path?.file_name();
62            let name = file_name.to_str().or_err()?.to_owned();
63
64            if name.contains(":brick-status") || name.contains(":ev3dev") {
65                if name.contains("led0:") || name.contains("left:") {
66                    if name.contains("red:") {
67                        left_red_name = name;
68                    } else if name.contains("green:") {
69                        left_green_name = name
70                    }
71                } else if name.contains("led1:") || name.contains("right:") {
72                    if name.contains("red:") {
73                        right_red_name = name
74                    } else if name.contains("green:") {
75                        right_green_name = name
76                    }
77                }
78            }
79        }
80
81        let left_red = Attribute::from_sys_class("leds", left_red_name.as_str(), "brightness")?;
82        let left_green = Attribute::from_sys_class("leds", left_green_name.as_str(), "brightness")?;
83        let right_red = Attribute::from_sys_class("leds", right_red_name.as_str(), "brightness")?;
84        let right_green =
85            Attribute::from_sys_class("leds", right_green_name.as_str(), "brightness")?;
86
87        Ok(Led {
88            left_red,
89            left_green,
90            right_red,
91            right_green,
92        })
93    }
94
95    /// Returns the current red value of the left led.
96    fn get_left_red(&self) -> Ev3Result<u8> {
97        self.left_red.get()
98    }
99
100    /// Sets the red value of the left led.
101    fn set_left_red(&self, brightness: u8) -> Ev3Result<()> {
102        self.left_red.set(brightness)
103    }
104
105    /// Returns the current green value of the left led.
106    fn get_left_green(&self) -> Ev3Result<u8> {
107        self.left_green.get()
108    }
109
110    /// Sets the green value of the left led.
111    fn set_left_green(&self, brightness: u8) -> Ev3Result<()> {
112        self.left_green.set(brightness)
113    }
114
115    /// Returns the current red value of the right led.
116    fn get_right_red(&self) -> Ev3Result<u8> {
117        self.right_red.get()
118    }
119
120    /// Sets the red value of the right led.
121    fn set_right_red(&self, brightness: u8) -> Ev3Result<()> {
122        self.right_red.set(brightness)
123    }
124
125    /// Returns the current green value of the right led.
126    fn get_right_green(&self) -> Ev3Result<u8> {
127        self.right_green.get()
128    }
129
130    /// Sets the green value of the right led.
131    fn set_right_green(&self, brightness: u8) -> Ev3Result<()> {
132        self.right_green.set(brightness)
133    }
134
135    /// Returns the current color value of the left led.
136    pub fn get_left_color(&self) -> Ev3Result<Color> {
137        let red = self.get_left_red()?;
138        let green = self.get_left_green()?;
139
140        Ok((red, green))
141    }
142
143    /// Sets the color value of the left led.
144    pub fn set_left_color(&self, color: Color) -> Ev3Result<()> {
145        self.set_left_red(color.0)?;
146        self.set_left_green(color.1)
147    }
148
149    /// Returns the current color value of the right led.
150    pub fn get_right_color(&self) -> Ev3Result<Color> {
151        let red = self.get_right_red()?;
152        let green = self.get_right_green()?;
153
154        Ok((red, green))
155    }
156
157    /// Sets the color value of the right led.
158    pub fn set_right_color(&self, color: Color) -> Ev3Result<()> {
159        self.set_right_red(color.0)?;
160        self.set_right_green(color.1)
161    }
162
163    /// Returns the color value of both leds or `None` if they are different.
164    pub fn get_color(&self) -> Ev3Result<Option<Color>> {
165        let left = self.get_left_color()?;
166        let right = self.get_right_color()?;
167
168        if left.0 == right.0 && left.1 == right.1 {
169            Ok(Some(left))
170        } else {
171            Ok(None)
172        }
173    }
174
175    /// Sets the color value of both leds.
176    pub fn set_color(&self, color: Color) -> Ev3Result<()> {
177        self.set_left_color(color)?;
178        self.set_right_color(color)
179    }
180}
181
182const KEY_BUF_LEN: usize = 96;
183const EVIOCGKEY: u32 = 2_153_792_792;
184
185/// Helper struct for ButtonFileHandler.
186struct FileMapEntry {
187    pub file: File,
188    pub buffer_cache: [u8; KEY_BUF_LEN],
189}
190// Manually implement Debug cause `buffer_cache` does not implement Debug.
191impl fmt::Debug for FileMapEntry {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        f.debug_struct("FileMapEntry")
194            .field("file", &self.file)
195            .finish()
196    }
197}
198
199/// Helper struct for ButtonFileHandler.
200#[derive(Debug)]
201struct ButtonMapEntry {
202    pub file_name: String,
203    pub key_code: u32,
204}
205
206type ButtonChangeHandler = Box<dyn Fn(HashSet<String>)>;
207type ButtonHandler = Box<dyn Fn(bool)>;
208
209/// This implementation depends on the availability of the EVIOCGKEY ioctl
210/// to be able to read the button state buffer. See Linux kernel source
211/// in /include/uapi/linux/input.h for details.
212struct ButtonFileHandler {
213    file_map: HashMap<String, FileMapEntry>,
214    button_map: HashMap<String, ButtonMapEntry>,
215    button_change_handler: Option<ButtonChangeHandler>,
216    button_handlers: HashMap<String, ButtonHandler>,
217    pressed_buttons: HashSet<String>,
218}
219
220impl std::fmt::Debug for ButtonFileHandler {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        f.debug_struct("ButtonFileHandler")
223            .field("file_map", &self.file_map)
224            .field("button_map", &self.button_map)
225            .field(
226                "button_change_handler",
227                &self.button_change_handler.is_some(),
228            )
229            .field("button_handlers", &self.button_map.keys())
230            .field("pressed_buttons", &self.pressed_buttons)
231            .finish()
232    }
233}
234
235impl ButtonFileHandler {
236    /// Create a new instance.
237    fn new() -> Self {
238        ButtonFileHandler {
239            file_map: HashMap::new(),
240            button_map: HashMap::new(),
241            button_change_handler: None,
242            button_handlers: HashMap::new(),
243            pressed_buttons: HashSet::new(),
244        }
245    }
246
247    /// Add a button to the file handler.
248    fn add_button(&mut self, name: &str, file_name: &str, key_code: u32) -> Ev3Result<()> {
249        if !self.file_map.contains_key(file_name) {
250            let file = File::open(file_name)?;
251            let buffer_cache = [0u8; KEY_BUF_LEN];
252
253            self.file_map
254                .insert(file_name.to_owned(), FileMapEntry { file, buffer_cache });
255        }
256
257        self.button_map.insert(
258            name.to_owned(),
259            ButtonMapEntry {
260                file_name: file_name.to_owned(),
261                key_code,
262            },
263        );
264
265        Ok(())
266    }
267
268    /// Sets an event handler for the given button.
269    fn set_button_handler(&mut self, name: &str, handler: Option<ButtonHandler>) {
270        if let Some(listener) = handler {
271            self.button_handlers.insert(name.to_owned(), listener);
272        } else {
273            self.button_handlers.remove(name);
274        }
275    }
276
277    /// Sets an event handler for any button state change.
278    fn set_button_change_handler(&mut self, handler: Option<ButtonChangeHandler>) {
279        self.button_change_handler = handler;
280    }
281
282    /// Gets a copy of the currently pressed buttons.
283    fn get_pressed_buttons(&self) -> HashSet<String> {
284        self.pressed_buttons.clone()
285    }
286
287    /// Check if a button is pressed.
288    fn get_button_state(&self, name: &str) -> bool {
289        self.pressed_buttons.contains(name)
290    }
291
292    /// Check for currently pressed buttons. If the new state differs from the
293    /// old state, call the appropriate button event handlers.
294    fn process(&mut self) {
295        for entry in self.file_map.values_mut() {
296            unsafe {
297                libc::ioctl(
298                    entry.file.as_raw_fd(),
299                    (EVIOCGKEY as i32).try_into().unwrap(),
300                    &mut entry.buffer_cache,
301                );
302            }
303        }
304
305        let old_pressed_buttons = self.pressed_buttons.clone();
306        self.pressed_buttons.clear();
307
308        for (
309            btn_name,
310            ButtonMapEntry {
311                file_name,
312                key_code,
313            },
314        ) in self.button_map.iter()
315        {
316            let buffer = &self.file_map[file_name].buffer_cache;
317
318            if (buffer[(key_code / 8) as usize] & 1 << (key_code % 8)) != 0 {
319                self.pressed_buttons.insert(btn_name.to_owned());
320            }
321        }
322
323        let difference = old_pressed_buttons.symmetric_difference(&self.pressed_buttons);
324
325        let mut difference_count = 0;
326        for button in difference {
327            difference_count += 1;
328            if self.button_handlers.contains_key(button) {
329                self.button_handlers[button](self.get_button_state(button));
330            }
331        }
332
333        if difference_count > 0 {
334            if let Some(ref handler) = self.button_change_handler {
335                handler(self.get_pressed_buttons());
336            }
337        }
338    }
339}
340
341/// Ev3 brick button handler. Opens the corresponding `/dev/input` file handlers.
342///
343/// This implementation depends on the availability of the EVIOCGKEY ioctl
344/// to be able to read the button state buffer. See Linux kernel source
345/// in /include/uapi/linux/input.h for details.
346///
347/// ```no_run
348/// use ev3dev_lang_rust::Button;
349/// use std::thread;
350/// use std::time::Duration;
351///
352/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
353/// let mut button = Button::new()?;
354///
355/// button.set_down_handler(|is_pressed| {
356///     println!("Is 'down' pressed: {is_pressed}");
357/// });
358///
359/// loop {
360///     button.process();
361///
362///     println!("Is 'up' pressed: {}", button.is_up());
363///     println!("Pressed buttons: {:?}", button.get_pressed_buttons());
364///
365///     thread::sleep(Duration::from_millis(100));
366/// }
367/// # }
368/// ```
369#[derive(Debug, Clone)]
370pub struct Button {
371    button_handler: Rc<RefCell<ButtonFileHandler>>,
372}
373
374impl Button {
375    /// Ev3 brick button handler. Opens the corresponding `/dev/input` file handlers.
376    pub fn new() -> Ev3Result<Self> {
377        let mut handler = ButtonFileHandler::new();
378
379        handler.add_button("up", "/dev/input/by-path/platform-gpio_keys-event", 103)?;
380        handler.add_button("down", "/dev/input/by-path/platform-gpio_keys-event", 108)?;
381        handler.add_button("left", "/dev/input/by-path/platform-gpio_keys-event", 105)?;
382        handler.add_button("right", "/dev/input/by-path/platform-gpio_keys-event", 106)?;
383        handler.add_button("enter", "/dev/input/by-path/platform-gpio_keys-event", 28)?;
384        handler.add_button(
385            "backspace",
386            "/dev/input/by-path/platform-gpio_keys-event",
387            14,
388        )?;
389
390        Ok(Self {
391            button_handler: Rc::new(RefCell::new(handler)),
392        })
393    }
394
395    /// Check for currently pressed buttons. If the new state differs from the
396    /// old state, call the appropriate button event handlers.
397    /// ```no_run
398    /// use ev3dev_lang_rust::Button;
399    /// use std::thread;
400    /// use std::time::Duration;
401    ///
402    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
403    /// let mut button = Button::new()?;
404    ///
405    /// button.set_down_handler(|is_pressed| {
406    ///     println!("Is 'down' pressed: {is_pressed}");
407    /// });
408    ///
409    /// loop {
410    ///     button.process();
411    ///
412    ///     println!("Is 'up' pressed: {}", button.is_up());
413    ///     println!("Pressed buttons: {:?}", button.get_pressed_buttons());
414    ///
415    ///     thread::sleep(Duration::from_millis(100));
416    /// }
417    /// # }
418    /// ```
419    pub fn process(&self) {
420        self.button_handler.borrow_mut().process()
421    }
422
423    /// Get all pressed buttons by name.
424    ///
425    /// ```no_run
426    /// use ev3dev_lang_rust::Button;
427    /// use std::thread;
428    /// use std::time::Duration;
429    ///
430    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
431    /// let button = Button::new()?;
432    ///
433    /// loop {
434    ///     button.process();
435    ///     println!("Pressed buttons: {:?}", button.get_pressed_buttons());
436    ///     thread::sleep(Duration::from_millis(100));
437    /// }
438    /// # }
439    /// ```
440    pub fn get_pressed_buttons(&self) -> HashSet<String> {
441        self.button_handler.borrow().get_pressed_buttons()
442    }
443
444    /// Set an event handler, that is called by `process()` if any button state changes.
445    /// Has a set of all pressed buttons as parameter.
446    ///
447    /// ```no_run
448    /// use ev3dev_lang_rust::Button;
449    /// use std::thread;
450    /// use std::time::Duration;
451    ///
452    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
453    /// let mut button = Button::new()?;
454    ///
455    /// button.set_change_handler(|pressed_buttons| {
456    ///     println!("pressed buttons: {:?}", pressed_buttons);
457    /// });
458    ///
459    /// loop {
460    ///     button.process();
461    ///     thread::sleep(Duration::from_millis(100));
462    /// }
463    /// # }
464    /// ```
465    pub fn set_change_handler(&mut self, handler: impl Fn(HashSet<String>) + 'static) {
466        self.button_handler
467            .borrow_mut()
468            .set_button_change_handler(Some(Box::new(handler)))
469    }
470
471    /// Removes the change event handler.
472    pub fn remove_change_handler(&mut self) {
473        self.button_handler
474            .borrow_mut()
475            .set_button_change_handler(None)
476    }
477
478    ev3_button_functions!(up);
479    ev3_button_functions!(down);
480    ev3_button_functions!(left);
481    ev3_button_functions!(right);
482    ev3_button_functions!(enter);
483    ev3_button_functions!(backspace);
484}