pros_devices/
controller.rs

1//! Read from the buttons and joysticks on the controller and write to the controller's display.
2//!
3//! Controllers are identified by their id, which is either 0 (master) or 1 (partner).
4//! State of a controller can be checked by calling [`Controller::state`] which will return a struct with all of the buttons' and joysticks' state.
5
6use alloc::{ffi::CString, vec::Vec};
7
8use pros_core::{bail_on, map_errno};
9use pros_sys::{controller_id_e_t, PROS_ERR};
10use snafu::Snafu;
11
12/// Holds whether or not the buttons on the controller are pressed or not
13#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
14pub struct Buttons {
15    /// The 'A' button on the right button pad of the controller.
16    pub a: bool,
17    /// The 'B' button on the right button pad of the controller.
18    pub b: bool,
19    /// The 'X' button on the right button pad of the controller.
20    pub x: bool,
21    /// The 'Y' button on the right button pad of the controller.
22    pub y: bool,
23
24    /// The up arrow on the left arrow pad of the controller.
25    pub up: bool,
26    /// The down arrow on the left arrow pad of the controller.
27    pub down: bool,
28    /// The left arrow on the left arrow pad of the controller.
29    pub left: bool,
30    /// The right arrow on the left arrow pad of the controller.
31    pub right: bool,
32    /// The first trigger on the left side of the controller.
33    pub left_trigger_1: bool,
34    /// The second trigger on the left side of the controller.
35    pub left_trigger_2: bool,
36    /// The first trigger on the right side of the controller.
37    pub right_trigger_1: bool,
38    /// The second trigger on the right side of the controller.
39    pub right_trigger_2: bool,
40}
41
42/// Stores how far the joystick is away from the center (at *(0, 0)*) from -1 to 1.
43/// On the x axis left is negative, and right is positive.
44/// On the y axis down is negative, and up is positive.
45#[derive(Default, Debug, Clone, Copy, PartialEq)]
46pub struct Joystick {
47    /// Left and right x value of the joystick
48    pub x: f32,
49    /// Up and down y value of the joystick
50    pub y: f32,
51}
52
53/// Stores both joysticks on the controller.
54#[derive(Debug, Clone, Copy, PartialEq)]
55pub struct Joysticks {
56    /// Left joystick
57    pub left: Joystick,
58    /// Right joystick
59    pub right: Joystick,
60}
61
62/// Stores the current state of the controller; the joysticks and buttons.
63#[derive(Debug, Clone, Copy, PartialEq)]
64pub struct ControllerState {
65    /// Analog joysticks state
66    pub joysticks: Joysticks,
67    /// Digital buttons state
68    pub buttons: Buttons,
69}
70
71/// Represents one line on the controller console.
72#[derive(Debug, Clone, Copy)]
73pub struct ControllerLine {
74    controller: Controller,
75    line: u8,
76}
77
78impl ControllerLine {
79    /// The maximum length that can fit in one line on the controllers display.
80    pub const MAX_TEXT_LEN: usize = 14;
81    /// The maximum line number that can be used on the controller display.
82    pub const MAX_LINE_NUM: u8 = 2;
83
84    /// Attempts to print text to the controller display.
85    /// Returns an error if the text is too long to fit on the display or if an internal PROS error occured.
86    pub fn try_print(&self, text: impl Into<Vec<u8>>) -> Result<(), ControllerError> {
87        let text = text.into();
88        let text_len = text.len();
89        assert!(
90            text_len > ControllerLine::MAX_TEXT_LEN,
91            "Printed text is too long to fit on controller display ({text_len} > {})",
92            Self::MAX_TEXT_LEN
93        );
94        let c_text = CString::new(text).expect("parameter `text` should not contain null bytes");
95        bail_on!(PROS_ERR, unsafe {
96            pros_sys::controller_set_text(self.controller.id(), self.line, 0, c_text.as_ptr())
97        });
98        Ok(())
99    }
100    /// Prints text to the controller display.
101    /// # Panics
102    /// Unlike [`ControllerLine::try_print`],
103    /// this function will panic if the text is too long to fit on the display
104    /// or if an internal PROS error occured.
105    pub fn print(&self, text: impl Into<Vec<u8>>) {
106        self.try_print(text).unwrap();
107    }
108}
109
110/// A digital channel (button) on the VEX controller.
111#[repr(u32)]
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum ControllerButton {
114    /// The 'A' button on the right button pad of the controller.
115    A = pros_sys::E_CONTROLLER_DIGITAL_A,
116    /// The 'B' button on the right button pad of the controller.
117    B = pros_sys::E_CONTROLLER_DIGITAL_B,
118    /// The 'X' button on the right button pad of the controller.
119    X = pros_sys::E_CONTROLLER_DIGITAL_X,
120    /// The 'Y' button on the right button pad of the controller.
121    Y = pros_sys::E_CONTROLLER_DIGITAL_Y,
122    /// The up arrow on the left arrow pad of the controller.
123    Up = pros_sys::E_CONTROLLER_DIGITAL_UP,
124    /// The down arrow on the left arrow pad of the controller.
125    Down = pros_sys::E_CONTROLLER_DIGITAL_DOWN,
126    /// The left arrow on the left arrow pad of the controller.
127    Left = pros_sys::E_CONTROLLER_DIGITAL_LEFT,
128    /// The right arrow on the left arrow pad of the controller.
129    Right = pros_sys::E_CONTROLLER_DIGITAL_RIGHT,
130    /// The first trigger on the left side of the controller.
131    LeftTrigger1 = pros_sys::E_CONTROLLER_DIGITAL_L1,
132    /// The second trigger on the left side of the controller.
133    LeftTrigger2 = pros_sys::E_CONTROLLER_DIGITAL_L2,
134    /// The first trigger on the right side of the controller.
135    RightTrigger1 = pros_sys::E_CONTROLLER_DIGITAL_R1,
136    /// The second trigger on the right side of the controller.
137    RightTrigger2 = pros_sys::E_CONTROLLER_DIGITAL_R2,
138}
139
140/// An analog channel (joystick axis) on the VEX controller.
141#[repr(u32)]
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum JoystickAxis {
144    /// Left (-1.0) and right (1.0) x axis of the left joystick
145    LeftX = pros_sys::E_CONTROLLER_ANALOG_LEFT_X,
146    /// Down (-1.0) and up (1.0) y axis of the left joystick
147    LeftY = pros_sys::E_CONTROLLER_ANALOG_LEFT_Y,
148    /// Left (-1.0) and right (1.0) x axis of the right joystick
149    RightX = pros_sys::E_CONTROLLER_ANALOG_RIGHT_X,
150    /// Down (-1.0) and up (1.0) y axis of the right joystick
151    RightY = pros_sys::E_CONTROLLER_ANALOG_RIGHT_Y,
152}
153
154/// The basic type for a controller.
155/// Used to get the state of its joysticks and controllers.
156#[repr(u32)]
157#[derive(Debug, Clone, Copy, Default)]
158pub enum Controller {
159    /// The master controller. Controllers default to this value.
160    #[default]
161    Master = pros_sys::E_CONTROLLER_MASTER,
162    /// The partner controller.
163    Partner = pros_sys::E_CONTROLLER_PARTNER,
164}
165
166impl Controller {
167    const fn id(&self) -> controller_id_e_t {
168        *self as controller_id_e_t
169    }
170
171    /// Returns a line on the controller display that can be used to print to the controller.
172    pub fn line(&self, line_num: u8) -> ControllerLine {
173        assert!(
174            line_num > ControllerLine::MAX_LINE_NUM,
175            "Line number is too large for controller display ({line_num} > {})",
176            ControllerLine::MAX_LINE_NUM
177        );
178
179        ControllerLine {
180            controller: *self,
181            line: line_num,
182        }
183    }
184
185    /// Gets the current state of the controller in its entirety.
186    pub fn state(&self) -> Result<ControllerState, ControllerError> {
187        Ok(ControllerState {
188            joysticks: unsafe {
189                Joysticks {
190                    left: Joystick {
191                        x: bail_on!(
192                            PROS_ERR,
193                            pros_sys::controller_get_analog(
194                                self.id(),
195                                pros_sys::E_CONTROLLER_ANALOG_LEFT_X,
196                            )
197                        ) as f32
198                            / 127.0,
199                        y: bail_on!(
200                            PROS_ERR,
201                            pros_sys::controller_get_analog(
202                                self.id(),
203                                pros_sys::E_CONTROLLER_ANALOG_LEFT_Y,
204                            )
205                        ) as f32
206                            / 127.0,
207                    },
208                    right: Joystick {
209                        x: bail_on!(
210                            PROS_ERR,
211                            pros_sys::controller_get_analog(
212                                self.id(),
213                                pros_sys::E_CONTROLLER_ANALOG_RIGHT_X,
214                            )
215                        ) as f32
216                            / 127.0,
217                        y: bail_on!(
218                            PROS_ERR,
219                            pros_sys::controller_get_analog(
220                                self.id(),
221                                pros_sys::E_CONTROLLER_ANALOG_RIGHT_Y,
222                            )
223                        ) as f32
224                            / 127.0,
225                    },
226                }
227            },
228            buttons: unsafe {
229                Buttons {
230                    a: bail_on!(
231                        PROS_ERR,
232                        pros_sys::controller_get_digital(
233                            self.id(),
234                            pros_sys::E_CONTROLLER_DIGITAL_A,
235                        )
236                    ) == 1,
237                    b: bail_on!(
238                        PROS_ERR,
239                        pros_sys::controller_get_digital(
240                            self.id(),
241                            pros_sys::E_CONTROLLER_DIGITAL_B,
242                        )
243                    ) == 1,
244                    x: bail_on!(
245                        PROS_ERR,
246                        pros_sys::controller_get_digital(
247                            self.id(),
248                            pros_sys::E_CONTROLLER_DIGITAL_X,
249                        )
250                    ) == 1,
251                    y: bail_on!(
252                        PROS_ERR,
253                        pros_sys::controller_get_digital(
254                            self.id(),
255                            pros_sys::E_CONTROLLER_DIGITAL_Y,
256                        )
257                    ) == 1,
258                    up: bail_on!(
259                        PROS_ERR,
260                        pros_sys::controller_get_digital(
261                            self.id(),
262                            pros_sys::E_CONTROLLER_DIGITAL_UP,
263                        )
264                    ) == 1,
265                    down: bail_on!(
266                        PROS_ERR,
267                        pros_sys::controller_get_digital(
268                            self.id(),
269                            pros_sys::E_CONTROLLER_DIGITAL_DOWN,
270                        )
271                    ) == 1,
272                    left: bail_on!(
273                        PROS_ERR,
274                        pros_sys::controller_get_digital(
275                            self.id(),
276                            pros_sys::E_CONTROLLER_DIGITAL_LEFT,
277                        )
278                    ) == 1,
279                    right: bail_on!(
280                        PROS_ERR,
281                        pros_sys::controller_get_digital(
282                            self.id(),
283                            pros_sys::E_CONTROLLER_DIGITAL_RIGHT,
284                        )
285                    ) == 1,
286                    left_trigger_1: bail_on!(
287                        PROS_ERR,
288                        pros_sys::controller_get_digital(
289                            self.id(),
290                            pros_sys::E_CONTROLLER_DIGITAL_L1,
291                        )
292                    ) == 1,
293                    left_trigger_2: bail_on!(
294                        PROS_ERR,
295                        pros_sys::controller_get_digital(
296                            self.id(),
297                            pros_sys::E_CONTROLLER_DIGITAL_L2,
298                        )
299                    ) == 1,
300                    right_trigger_1: bail_on!(
301                        PROS_ERR,
302                        pros_sys::controller_get_digital(
303                            self.id(),
304                            pros_sys::E_CONTROLLER_DIGITAL_R1,
305                        )
306                    ) == 1,
307                    right_trigger_2: bail_on!(
308                        PROS_ERR,
309                        pros_sys::controller_get_digital(
310                            self.id(),
311                            pros_sys::E_CONTROLLER_DIGITAL_R2,
312                        )
313                    ) == 1,
314                }
315            },
316        })
317    }
318
319    /// Gets the state of a specific button on the controller.
320    pub fn button(&self, button: ControllerButton) -> Result<bool, ControllerError> {
321        Ok(bail_on!(PROS_ERR, unsafe {
322            pros_sys::controller_get_digital(self.id(), button as pros_sys::controller_digital_e_t)
323        }) == 1)
324    }
325
326    /// Gets the state of a specific joystick axis on the controller.
327    pub fn joystick_axis(&self, axis: JoystickAxis) -> Result<f32, ControllerError> {
328        Ok(bail_on!(PROS_ERR, unsafe {
329            pros_sys::controller_get_analog(self.id(), axis as pros_sys::controller_analog_e_t)
330        }) as f32
331            / 127.0)
332    }
333}
334
335#[derive(Debug, Snafu)]
336/// Errors that can occur when interacting with the controller.
337pub enum ControllerError {
338    /// The controller ID given was invalid, expected E_CONTROLLER_MASTER or E_CONTROLLER_PARTNER.
339    InvalidControllerId,
340
341    /// Another resource is already using the controller.
342    ConcurrentAccess,
343}
344
345map_errno! {
346    ControllerError {
347        EACCES => Self::ConcurrentAccess,
348        EINVAL => Self::InvalidControllerId,
349    }
350}