vexide_devices/
controller.rs

1//! V5 Controller
2//!
3//! This module allows you to read from the buttons and joysticks on the controller and write to the controller's display.
4
5use alloc::{
6    ffi::{CString, NulError},
7    string::{String, ToString},
8};
9use core::{cell::RefCell, future::Future, task::Poll, time::Duration};
10
11use snafu::{ensure, Snafu};
12use vex_sdk::{
13    vexControllerConnectionStatusGet, vexControllerGet, vexControllerTextSet, V5_ControllerId,
14    V5_ControllerIndex, V5_ControllerStatus,
15};
16use vexide_core::competition::{self, CompetitionMode};
17
18/// Represents the state of a button on the controller.
19#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
20pub struct ButtonState {
21    prev_is_pressed: bool,
22    is_pressed: bool,
23}
24
25impl ButtonState {
26    /// Returns `true` if this button is currently being pressed.
27    #[must_use]
28    pub const fn is_pressed(&self) -> bool {
29        self.is_pressed
30    }
31
32    /// Returns `true` if this button is currently released (not being pressed).
33    #[must_use]
34    pub const fn is_released(&self) -> bool {
35        !self.is_pressed
36    }
37
38    /// Returns `true` if the button state was released in the previous call to [`Controller::state`], but is now pressed.
39    #[must_use]
40    pub const fn is_now_pressed(&self) -> bool {
41        !self.prev_is_pressed && self.is_pressed
42    }
43
44    /// Returns `true` if the button state was pressed in the previous call to [`Controller::state`], but is now released.
45    #[must_use]
46    pub const fn is_now_released(&self) -> bool {
47        self.prev_is_pressed && !self.is_pressed
48    }
49}
50
51/// Stores how far the joystick is away from the center (at *(0, 0)*) from -1 to 1.
52/// On the x axis left is negative, and right is positive.
53/// On the y axis down is negative, and up is positive.
54#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
55pub struct JoystickState {
56    x_raw: i8,
57    y_raw: i8,
58}
59
60impl JoystickState {
61    /// Returns the value of the joystick position on its x-axis from [-1, 1].
62    #[must_use]
63    pub fn x(&self) -> f64 {
64        f64::from(self.x_raw) / 127.0
65    }
66    /// Returns the value of the joystick position on its y-axis from [-1, 1].
67    #[must_use]
68    pub fn y(&self) -> f64 {
69        f64::from(self.y_raw) / 127.0
70    }
71
72    /// The raw value of the joystick position on its x-axis from [-127, 127].
73    #[must_use]
74    pub const fn x_raw(&self) -> i8 {
75        self.x_raw
76    }
77    /// The raw value of the joystick position on its x-axis from [-127, 127].
78    #[must_use]
79    pub const fn y_raw(&self) -> i8 {
80        self.y_raw
81    }
82}
83
84/// Holds a snapshot of the state of the controller.
85/// Returned by [`Controller::state`].
86#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
87pub struct ControllerState {
88    /// Left Joystick
89    pub left_stick: JoystickState,
90    /// Right Joystick
91    pub right_stick: JoystickState,
92
93    /// Button A
94    pub button_a: ButtonState,
95    /// Button B
96    pub button_b: ButtonState,
97    /// Button X
98    pub button_x: ButtonState,
99    /// Button Y
100    pub button_y: ButtonState,
101
102    /// Button Up
103    pub button_up: ButtonState,
104    /// Button Down
105    pub button_down: ButtonState,
106    /// Button Left
107    pub button_left: ButtonState,
108    /// Button Right
109    pub button_right: ButtonState,
110
111    /// Top Left Bumper
112    pub button_l1: ButtonState,
113    /// Bottom Left Bumper
114    pub button_l2: ButtonState,
115    /// Top Right Bumper
116    pub button_r1: ButtonState,
117    /// Bottom Right Bumper
118    pub button_r2: ButtonState,
119
120    /// Center Power Button
121    pub button_power: ButtonState,
122}
123
124/// This type stores the "pressed" states of every controller button.
125///
126/// This exists to efficiently cache previous button states with `Controller::update`, since
127/// each `ButtonState` needs to know about its previous state from the last `Controller::update`
128/// call in order to allow for `ButtonState::is_now_pressed` and `ButtonState::is_now_released`.
129#[derive(Default, Clone, Debug, Eq, PartialEq)]
130#[allow(
131    clippy::struct_excessive_bools,
132    reason = "not being used as state machine"
133)]
134struct ButtonStates {
135    a: bool,
136    b: bool,
137    x: bool,
138    y: bool,
139    up: bool,
140    down: bool,
141    left: bool,
142    right: bool,
143    power: bool,
144    l1: bool,
145    l2: bool,
146    r1: bool,
147    r2: bool,
148}
149
150fn validate_connection(id: ControllerId) -> Result<(), ControllerError> {
151    if unsafe {
152        vexControllerConnectionStatusGet(id.into()) == V5_ControllerStatus::kV5ControllerOffline
153    } {
154        return OfflineSnafu.fail();
155    }
156
157    Ok(())
158}
159
160enum ControllerScreenWriteFutureState<'a> {
161    /// Waiting for the controller to be ready to accept a new write.
162    WaitingForIdle {
163        /// The line to write to.
164        /// This is indexed like the SDK, with the first onscreen line being 1.
165        line: u8,
166        /// The column to write to.
167        /// This **NOT** is indexed like the SDK. The first onscreen column is 1.
168        column: u8,
169        /// The text to write.
170        text: Result<CString, NulError>,
171        /// The controller to write to.
172        controller: &'a mut ControllerScreen,
173        /// Whether or not to enforce that this line is on screen.
174        enforce_visible: bool,
175    },
176    /// The write has been completed.
177    Complete {
178        /// The result of the write.
179        result: Result<(), ControllerError>,
180    },
181}
182
183/// A future that completes once a write to the controller screen has been performed.
184///
185/// This future waits until the controller is able to accept a new write
186/// and fails if the controller is disconnected or if the requested write is bad.
187pub struct ControllerScreenWriteFuture<'a> {
188    state: ControllerScreenWriteFutureState<'a>,
189}
190
191impl<'a> ControllerScreenWriteFuture<'a> {
192    fn new(
193        line: u8,
194        column: u8,
195        text: String,
196        controller: &'a mut ControllerScreen,
197        enforce_visible: bool,
198    ) -> Self {
199        Self {
200            state: ControllerScreenWriteFutureState::WaitingForIdle {
201                line,
202                column,
203                text: CString::new(text),
204                controller,
205                enforce_visible,
206            },
207        }
208    }
209}
210
211impl Future for ControllerScreenWriteFuture<'_> {
212    type Output = Result<(), ControllerError>;
213
214    fn poll(
215        self: core::pin::Pin<&mut Self>,
216        cx: &mut core::task::Context<'_>,
217    ) -> core::task::Poll<Self::Output> {
218        let state = &mut self.get_mut().state;
219
220        if let ControllerScreenWriteFutureState::WaitingForIdle {
221            line,
222            column,
223            text,
224            controller,
225            enforce_visible,
226        } = state
227        {
228            if *enforce_visible {
229                assert!(
230                    (*line != 0 && *line <= ControllerScreen::MAX_LINES as u8),
231                    "Invalid line number ({line}) is greater than the maximum number of lines ({})",
232                    ControllerScreen::MAX_LINES
233                );
234            }
235
236            assert!(
237                *column != 0 && *column <= ControllerScreen::MAX_COLUMNS as u8,
238                "Invalid column number ({column}) is greater than the maximum number of columns ({})",
239                ControllerScreen::MAX_COLUMNS
240            );
241
242            let text = text
243                .as_deref()
244                .map_err(Clone::clone)
245                .expect("A NUL (0x00) character was found in the text input string.");
246
247            match validate_connection(controller.id) {
248                Ok(()) => {
249                    let id = V5_ControllerId::from(controller.id);
250
251                    let result = unsafe {
252                        vexControllerTextSet(
253                            u32::from(id.0),
254                            u32::from(*line),
255                            u32::from(*column - 1),
256                            text.as_ptr().cast(),
257                        )
258                    };
259
260                    if result != 1 {
261                        *state = ControllerScreenWriteFutureState::Complete { result: Ok(()) }
262                    }
263
264                    cx.waker().wake_by_ref();
265                }
266                Err(err) => {
267                    *state = ControllerScreenWriteFutureState::Complete { result: Err(err) }
268                }
269            }
270        }
271
272        if let ControllerScreenWriteFutureState::Complete { result } = state {
273            Poll::Ready(result.clone())
274        } else {
275            Poll::Pending
276        }
277    }
278}
279
280/// Controller LCD Console
281#[derive(Debug, Eq, PartialEq)]
282pub struct ControllerScreen {
283    id: ControllerId,
284}
285
286impl ControllerScreen {
287    /// Maximum number of characters that can be drawn to a text line.
288    pub const MAX_COLUMNS: usize = 19;
289
290    /// Number of available text lines on the controller before clearing the screen.
291    pub const MAX_LINES: usize = 3;
292
293    /// Clears the contents of a specific text line, waiting until the controller
294    /// successfully clears the line.
295    /// Lines are 1-indexed.
296    ///
297    /// <section class="warning">
298    ///
299    /// Controller text setting is a slow process, so calls to this function at intervals
300    /// faster than 10ms on wired connection or 50ms over VEXnet will take longer to complete.
301    ///
302    /// </section>
303    ///
304    /// # Errors
305    ///
306    /// - A [`ControllerError::Offline`] error is returned if the controller is
307    ///   not connected.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// use vexide::prelude::*;
313    ///
314    /// #[vexide::main]
315    /// async fn main(peripherals: Peripherals) {
316    ///     let mut controller = peripherals.primary_controller;
317    ///
318    ///     // Write to line 0
319    ///     _ = controller.screen.set_text("Hello, world!", 0, 0).await;
320    ///
321    ///     sleep(Duration::from_millis(500)).await;
322    ///
323    ///     // Clear line 0
324    ///     _ = controller.screen.clear_line(0).await;
325    /// }
326    /// ```
327    #[must_use]
328    pub fn clear_line(&mut self, line: u8) -> ControllerScreenWriteFuture<'_> {
329        ControllerScreenWriteFuture::new(line, 1, String::new(), self, true)
330    }
331
332    /// Attempts to clear the contents of a specific text line.
333    /// Lines are 1-indexed.
334    /// Unlike [`clear_line`](ControllerScreen::clear_line) this function will fail if the controller screen is busy.
335    ///
336    /// <section class="warning">
337    ///
338    /// Controller text setting is a slow process, so updates faster than 10ms when on a
339    /// wired connection or 50ms over VEXnet will not be applied to the controller.
340    ///
341    /// </section>
342    ///
343    /// # Errors
344    ///
345    /// - A [`ControllerError::Offline`] error is returned if the controller is
346    ///   not connected.
347    /// - A [`ControllerError::WriteBusy`] error is returned if a screen write
348    ///   occurred too quickly after the previous write attempt.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use vexide::prelude::*;
354    ///
355    /// #[vexide::main]
356    /// async fn main(peripherals: Peripherals) {
357    ///     let mut controller = peripherals.primary_controller;
358    ///
359    ///     // Write to line 0
360    ///     _ = controller.screen.set_text("Hello, world!", 0, 0).await;
361    ///
362    ///     sleep(Duration::from_millis(500)).await;
363    ///
364    ///     // Clear line 0
365    ///     _ = controller.screen.try_clear_line(0);
366    /// }
367    /// ```
368    pub fn try_clear_line(&mut self, line: u8) -> Result<(), ControllerError> {
369        //TODO: Older versions of VexOS clear the controller by setting the line to "                   ".
370        //TODO: We should check the version and change behavior based on it.
371        self.try_set_text("", line, 1)?;
372
373        Ok(())
374    }
375
376    /// Clears the whole screen, waiting until the controller successfully clears the screen.
377    ///
378    /// This includes the default widget displayed by the controller if it hasn't already been cleared.
379    ///
380    /// <section class="warning">
381    ///
382    /// Controller text setting is a slow process, so calls to this function at intervals
383    /// faster than 10ms on wired connection or 50ms over VEXnet will take longer to complete.
384    ///
385    /// </section>
386    ///
387    /// # Errors
388    ///
389    /// - A [`ControllerError::Offline`] error is returned if the controller is
390    ///   not connected.
391    ///
392    /// # Examples
393    ///
394    /// ```
395    /// use vexide::prelude::*;
396    ///
397    /// #[vexide::main]
398    /// async fn main(peripherals: Peripherals) {
399    ///     let mut controller = peripherals.primary_controller;
400    ///
401    ///     // Remove the default widget on the controller screen that displays match time.
402    ///     _ = controller.screen.clear_screen().await;
403    /// }
404    /// ```
405    #[must_use]
406    pub fn clear_screen(&mut self) -> ControllerScreenWriteFuture<'_> {
407        ControllerScreenWriteFuture::new(0, 1, String::new(), self, false)
408    }
409
410    /// Clears the whole screen, including the default widget displayed by the controller if
411    /// it hasn't already been cleared.
412    /// Unlike [`clear_screen`](ControllerScreen::clear_screen) this function will fail if the controller screen is busy.
413    ///
414    /// <section class="warning">
415    ///
416    /// Controller text setting is a slow process, so updates faster than 10ms when on a
417    /// wired connection or 50ms over VEXnet will not be applied to the controller.
418    ///
419    /// </section>
420    ///
421    /// # Errors
422    ///
423    /// - A [`ControllerError::Offline`] error is returned if the controller is
424    ///   not connected.
425    /// - A [`ControllerError::WriteBusy`] error is returned if a screen write
426    ///   occurred too quickly after the previous write attempt.
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// use vexide::prelude::*;
432    ///
433    /// #[vexide::main]
434    /// async fn main(peripherals: Peripherals) {
435    ///     let mut controller = peripherals.primary_controller;
436    ///
437    ///     // Remove the default widget on the controller screen that displays match time.
438    ///     _ = controller.screen.try_clear_screen();
439    /// }
440    /// ```
441    pub fn try_clear_screen(&mut self) -> Result<(), ControllerError> {
442        validate_connection(self.id)?;
443
444        let id: V5_ControllerId = self.id.into();
445
446        if unsafe { vexControllerTextSet(u32::from(id.0), 0, 0, c"".as_ptr().cast()) } != 1 {
447            return Err(ControllerError::WriteBusy);
448        }
449
450        Ok(())
451    }
452
453    /// Set the text contents at a specific row/column offset, waiting until the controller
454    /// successfully writes the text.
455    /// Both lines and columns are 1-indexed.
456    ///
457    /// <section class="warning">
458    ///
459    /// Controller text setting is a slow process, so calls to this function at intervals
460    /// faster than 10ms on wired connection or 50ms over VEXnet will take longer to complete.
461    ///
462    /// </section>
463    ///
464    /// # Panics
465    ///
466    /// - Panics if `line` is greater than or equal to [`Self::MAX_LINES`].
467    /// - Panics if `col` is greater than or equal to [`Self::MAX_COLUMNS`].
468    /// - Panics if a NUL (0x00) character was found anywhere in the specified text.
469    ///
470    /// # Errors
471    ///
472    /// - A [`ControllerError::Offline`] error is returned if the controller is
473    ///   not connected.
474    ///
475    /// # Examples
476    ///
477    /// ```
478    /// use vexide::prelude::*;
479    ///
480    /// #[vexide::main]
481    /// async fn main(peripherals: Peripherals) {
482    ///     let mut controller = peripherals.primary_controller;
483    ///     _ = controller.screen.set_text("Hello, world!", 0, 0).await;
484    ///     _ = controller.screen.set_text("Hello, world!", 1, 0).await;
485    /// }
486    /// ```
487    #[must_use]
488    pub fn set_text(
489        &mut self,
490        text: impl AsRef<str>,
491        line: u8,
492        col: u8,
493    ) -> ControllerScreenWriteFuture<'_> {
494        ControllerScreenWriteFuture::new(line, col, text.as_ref().to_string(), self, true)
495    }
496
497    /// Set the text contents at a specific row/column offset.
498    /// Both lines and columns are 1-indexed.
499    /// Unlike [`set_text`](ControllerScreen::set_text) this function will fail if the controller screen is busy.
500    ///
501    /// <section class="warning">
502    ///
503    /// Controller text setting is a slow process, so updates faster than 10ms when on a
504    /// wired connection or 50ms over VEXnet will not be applied to the controller.
505    ///
506    /// </section>
507    ///
508    /// # Panics
509    ///
510    /// - Panics if `line` is greater than or equal to [`Self::MAX_LINES`].
511    /// - Panics if `col` is greater than or equal to [`Self::MAX_COLUMNS`].
512    /// - Panics if a NUL (0x00) character was found anywhere in the specified text.
513    ///
514    /// # Errors
515    ///
516    /// - A [`ControllerError::Offline`] error is returned if the controller is
517    ///   not connected.
518    /// - A [`ControllerError::WriteBusy`] error is returned if a screen write
519    ///   occurred too quickly after the previous write attempt.
520    ///
521    /// # Examples
522    ///
523    /// ```
524    /// use vexide::prelude::*;
525    ///
526    /// #[vexide::main]
527    /// async fn main(peripherals: Peripherals) {
528    ///     let mut controller = peripherals.primary_controller;
529    ///
530    ///     _ = controller.try_set_text("Hello, world!", 0, 0);
531    /// }
532    /// ```
533    pub fn try_set_text(
534        &mut self,
535        text: impl AsRef<str>,
536        line: u8,
537        column: u8,
538    ) -> Result<(), ControllerError> {
539        validate_connection(self.id)?;
540
541        assert!(
542            column <= Self::MAX_COLUMNS as u8 && column != 0,
543            "Invalid column number ({column}) is greater than the maximum number of columns ({})",
544            ControllerScreen::MAX_COLUMNS
545        );
546        assert!(
547            line <= Self::MAX_LINES as u8 && line != 0,
548            "Invalid line number ({line}) is greater than the maximum number of line ({})",
549            ControllerScreen::MAX_LINES
550        );
551
552        let id: V5_ControllerId = self.id.into();
553        let text = CString::new(text.as_ref())
554            .expect("A NUL (0x00) character was found in the text input string.");
555
556        if unsafe {
557            vexControllerTextSet(
558                u32::from(id.0),
559                u32::from(line),
560                u32::from(column),
561                text.as_ptr().cast(),
562            )
563        } != 1
564        {
565            return Err(ControllerError::WriteBusy);
566        }
567
568        Ok(())
569    }
570}
571
572/// Represents an identifier for one of the two possible controllers
573/// connected to the V5 Brain.
574#[derive(Debug, Clone, Copy, PartialEq, Eq)]
575pub enum ControllerId {
576    /// Primary ("Master") Controller
577    /// This is the controller that is connected to the Brain.
578    Primary,
579
580    /// Partner Controller
581    /// This is the controller that is connected to the [primary](ControllerId::Primary) controller.
582    Partner,
583}
584
585impl From<ControllerId> for V5_ControllerId {
586    fn from(id: ControllerId) -> Self {
587        match id {
588            ControllerId::Primary => V5_ControllerId::kControllerMaster,
589            ControllerId::Partner => V5_ControllerId::kControllerPartner,
590        }
591    }
592}
593
594/// Represents the state of a controller's connection.
595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
596pub enum ControllerConnection {
597    /// No controller is connected.
598    Offline,
599
600    /// Controller is tethered through a wired Smart Port connection.
601    Tethered,
602
603    /// Controller is wirelessly connected over a VEXNet radio
604    VexNet,
605}
606
607impl From<V5_ControllerStatus> for ControllerConnection {
608    fn from(value: V5_ControllerStatus) -> Self {
609        match value {
610            V5_ControllerStatus::kV5ControllerOffline => Self::Offline,
611            V5_ControllerStatus::kV5ControllerTethered => Self::Tethered,
612            V5_ControllerStatus::kV5ControllerVexnet => Self::VexNet,
613            _ => unreachable!(),
614        }
615    }
616}
617
618impl From<ControllerConnection> for V5_ControllerStatus {
619    fn from(value: ControllerConnection) -> Self {
620        match value {
621            ControllerConnection::Offline => Self::kV5ControllerOffline,
622            ControllerConnection::Tethered => Self::kV5ControllerTethered,
623            ControllerConnection::VexNet => Self::kV5ControllerVexnet,
624        }
625    }
626}
627
628/// The basic type for a controller.
629/// Used to get the state of its joysticks and controllers.
630#[derive(Debug, Eq, PartialEq)]
631pub struct Controller {
632    id: ControllerId,
633    prev_button_states: RefCell<ButtonStates>,
634
635    /// Controller Screen
636    pub screen: ControllerScreen,
637}
638
639impl Controller {
640    /// The update rate of the controller.
641    pub const UPDATE_INTERVAL: Duration = Duration::from_millis(25);
642
643    /// Create a new controller.
644    ///
645    /// # Safety
646    ///
647    /// Creating new `Controller`s is inherently unsafe due to the possibility of constructing
648    /// more than one screen at once allowing multiple mutable references to the same
649    /// hardware device. Prefer using [`Peripherals`](crate::peripherals::Peripherals) to register devices if possible.
650    #[must_use]
651    pub const unsafe fn new(id: ControllerId) -> Self {
652        Self {
653            id,
654            prev_button_states: RefCell::new(ButtonStates {
655                a: false,
656                b: false,
657                x: false,
658                y: false,
659                up: false,
660                down: false,
661                left: false,
662                right: false,
663                l1: false,
664                l2: false,
665                r1: false,
666                r2: false,
667                power: false,
668            }),
669            screen: ControllerScreen { id },
670        }
671    }
672
673    /// Returns the [identifier](ControllerId) of this controller.
674    ///
675    /// # Examples
676    ///
677    /// Perform a different action based on the controller ID.
678    /// ```
679    /// use vexide::prelude::*;
680    ///
681    /// fn print_a_pressed(controller: &Controller) {
682    ///     let state = controller.state().unwrap_or_default();
683    ///     if state.button_a.is_pressed() {
684    ///         match controller.id() {
685    ///             ControllerId::Primary => println!("Primary Controller A Pressed"),
686    ///             ControllerId::Partner => println!("Partner Controller A Pressed"),
687    ///         }
688    ///     }
689    /// }
690    /// ```
691    #[must_use]
692    pub const fn id(&self) -> ControllerId {
693        self.id
694    }
695
696    /// Returns the current state of all buttons and joysticks on the controller.
697    ///
698    /// # Note
699    ///
700    /// If the current competition mode is not driver control, this function will error.
701    ///
702    /// # Errors
703    ///
704    /// - A [`ControllerError::CompetitionControl`] error is returned if access to
705    ///   the controller data is being restricted by competition control.
706    /// - A [`ControllerError::Offline`] error is returned if the controller is
707    ///   not connected.
708    ///
709    /// # Examples
710    ///
711    /// ```
712    /// use vexide::prelude::*;
713    ///
714    /// #[vexide::main]
715    /// async fn main(peripherals: Peripherals) {
716    ///     let controller = peripherals.primary_controller;
717    ///
718    ///     loop {
719    ///         let state = controller.state().unwrap_or_default();
720    ///         println("Left Stick X: {}", state.left_stick.x());
721    ///         if state.button_a.is_now_pressed() {
722    ///             println!("Button A was just pressed!");
723    ///         }
724    ///         if state.button_x.is_pressed() {
725    ///             println!("Button X is pressed!");
726    ///         }
727    ///         if state.button_b.is_released() {
728    ///             println!("Button B is released!");
729    ///         }
730    ///         sleep(Controller::UPDATE_INTERVAL).await;
731    ///     }
732    /// }
733    /// ```
734    pub fn state(&self) -> Result<ControllerState, ControllerError> {
735        ensure!(
736            competition::mode() == CompetitionMode::Driver,
737            CompetitionControlSnafu
738        );
739        validate_connection(self.id)?;
740
741        // Get all current button states
742        let raw_id = self.id.into();
743        let button_states = ButtonStates {
744            a: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonA) } != 0,
745            b: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonB) } != 0,
746            x: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonX) } != 0,
747            y: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonY) } != 0,
748            up: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonUp) } != 0,
749            down: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonDown) } != 0,
750            left: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonLeft) } != 0,
751            right: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonRight) } != 0,
752            l1: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonL1) } != 0,
753            l2: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonL2) } != 0,
754            r1: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonR1) } != 0,
755            r2: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonR2) } != 0,
756            power: unsafe { vexControllerGet(raw_id, V5_ControllerIndex::ButtonSEL) } != 0,
757        };
758
759        // Swap the current button states with the previous states, getting the previous states in the process.
760        let prev_button_states = self.prev_button_states.replace(button_states.clone());
761
762        Ok(ControllerState {
763            left_stick: JoystickState {
764                x_raw: unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::Axis4) as _ },
765                y_raw: unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::Axis3) as _ },
766            },
767            right_stick: JoystickState {
768                x_raw: unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::Axis1) as _ },
769                y_raw: unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::Axis2) as _ },
770            },
771            button_a: ButtonState {
772                is_pressed: button_states.a,
773                prev_is_pressed: prev_button_states.a,
774            },
775            button_b: ButtonState {
776                is_pressed: button_states.b,
777                prev_is_pressed: prev_button_states.b,
778            },
779            button_x: ButtonState {
780                is_pressed: button_states.x,
781                prev_is_pressed: prev_button_states.x,
782            },
783            button_y: ButtonState {
784                is_pressed: button_states.y,
785                prev_is_pressed: prev_button_states.y,
786            },
787            button_up: ButtonState {
788                is_pressed: button_states.up,
789                prev_is_pressed: prev_button_states.up,
790            },
791            button_down: ButtonState {
792                is_pressed: button_states.down,
793                prev_is_pressed: prev_button_states.down,
794            },
795            button_left: ButtonState {
796                is_pressed: button_states.left,
797                prev_is_pressed: prev_button_states.left,
798            },
799            button_right: ButtonState {
800                is_pressed: button_states.right,
801                prev_is_pressed: prev_button_states.right,
802            },
803            button_l1: ButtonState {
804                is_pressed: button_states.l1,
805                prev_is_pressed: prev_button_states.l1,
806            },
807            button_l2: ButtonState {
808                is_pressed: button_states.l2,
809                prev_is_pressed: prev_button_states.l2,
810            },
811            button_r1: ButtonState {
812                is_pressed: button_states.r1,
813                prev_is_pressed: prev_button_states.r1,
814            },
815            button_r2: ButtonState {
816                is_pressed: button_states.r2,
817                prev_is_pressed: prev_button_states.r2,
818            },
819            button_power: ButtonState {
820                is_pressed: button_states.power,
821                prev_is_pressed: prev_button_states.power,
822            },
823        })
824    }
825
826    /// Returns the controller's connection type.
827    ///
828    /// # Examples
829    ///
830    /// Print less information over a slow and unreliable VEXnet connection:
831    ///
832    /// ```
833    /// use vexide::prelude::*;
834    ///
835    /// #[vexide::main]
836    /// async fn main(peripherals: Peripherals) {
837    ///     let controller = peripherals.primary_controller;
838    ///     if controller.connection() != ControllerConnection::VexNet {
839    ///         println!("A big info dump");
840    ///     }
841    /// }
842    /// ```
843    #[must_use]
844    pub fn connection(&self) -> ControllerConnection {
845        unsafe { vexControllerConnectionStatusGet(self.id.into()) }.into()
846    }
847
848    /// Returns the controller's battery capacity as an f64 in the interval
849    /// [0.0, 1.0].
850    ///
851    /// # Errors
852    ///
853    /// - A [`ControllerError::Offline`] error is returned if the controller is
854    ///   not connected.
855    ///
856    /// # Examples
857    ///
858    /// Print the controller's battery capacity:
859    ///
860    /// ```
861    /// use vexide::prelude::*;
862    ///
863    /// #[vexide::main]
864    /// async fn main(peripherals: Peripherals) {
865    ///     let controller = peripherals.primary_controller;
866    ///     println!("Controller battery capacity: {}", controller.battery_capacity().unwrap_or(0.0));
867    /// }
868    /// ```
869    pub fn battery_capacity(&self) -> Result<f64, ControllerError> {
870        validate_connection(self.id)?;
871
872        Ok(f64::from(unsafe {
873            vexControllerGet(self.id.into(), V5_ControllerIndex::BatteryCapacity)
874        }) / 100.0)
875    }
876
877    /// Returns the controller's battery level.
878    ///
879    /// # Errors
880    ///
881    /// - A [`ControllerError::Offline`] error is returned if the controller is
882    ///   not connected.
883    ///
884    /// # Examples
885    ///
886    /// Print a warning if the controller battery is low:
887    ///
888    /// ```
889    /// use vexide::prelude::*;
890    ///
891    /// #[vexide::main]
892    /// async fn main(peripherals: Peripherals) {
893    ///     let controller = peripherals.primary_controller;
894    ///     loop {
895    ///         // If the controller isn't connected, it may as well be dead.
896    ///         let battery_level = controller.battery_level().unwrap_or(0);
897    ///         if battery_level < 10 {
898    ///             println!("WARNING: Controller battery is low!");
899    ///         }
900    ///         sleep(Controller::UPDATE_INTERVAL).await;
901    ///     }
902    /// }
903    /// ```
904    pub fn battery_level(&self) -> Result<i32, ControllerError> {
905        validate_connection(self.id)?;
906
907        Ok(unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::BatteryLevel) })
908    }
909
910    /// Returns the controller's flags.
911    ///
912    /// # Errors
913    ///
914    /// - A [`ControllerError::Offline`] error is returned if the controller is
915    ///   not connected.
916    pub fn flags(&self) -> Result<i32, ControllerError> {
917        validate_connection(self.id)?;
918
919        Ok(unsafe { vexControllerGet(self.id.into(), V5_ControllerIndex::Flags) })
920    }
921
922    /// Send a rumble pattern to the controller's vibration motor.
923    ///
924    /// This function takes a string consisting of the characters '.', '-', and ' ', where
925    /// dots are short rumbles, dashes are long rumbles, and spaces are pauses. Maximum
926    /// supported length is 8 characters.
927    ///
928    /// # Errors
929    ///
930    /// - A [`ControllerError::Offline`] error is returned if the controller is
931    ///   not connected.
932    ///
933    /// # Panics
934    ///
935    /// - Panics if a NUL (0x00) character was found anywhere in the specified text.
936    ///
937    /// # Examples
938    ///
939    /// ```
940    /// use vexide::prelude::*;
941    ///
942    /// #[vexide::main]
943    /// async fn main(peripherals: Peripherals) {
944    ///     let mut controller = peripherals.primary_controller;
945    ///     let _ = controller.rumble(". -. -.").await;
946    /// }
947    /// ```
948    pub fn rumble(&mut self, pattern: impl AsRef<str>) -> ControllerScreenWriteFuture<'_> {
949        ControllerScreenWriteFuture::new(
950            4,
951            1,
952            pattern.as_ref().to_string(),
953            &mut self.screen,
954            false,
955        )
956    }
957
958    /// Send a rumble pattern to the controller's vibration motor.
959    /// Unlike [`rumble`](Controller::rumble) this function will fail if the controller screen is busy.
960    ///
961    /// This function takes a string consisting of the characters '.', '-', and ' ', where
962    /// dots are short rumbles, dashes are long rumbles, and spaces are pauses. Maximum
963    /// supported length is 8 characters.
964    ///
965    /// # Errors
966    ///
967    /// - A [`ControllerError::Offline`] error is returned if the controller is
968    ///   not connected.
969    ///
970    /// # Panics
971    ///
972    /// - Panics if a NUL (0x00) character was found anywhere in the specified text.
973    ///
974    /// # Examples
975    ///
976    /// ```
977    /// use vexide::prelude::*;
978    ///
979    /// #[vexide::main]
980    /// async fn main(peripherals: Peripherals) {
981    ///     let mut controller = peripherals.primary_controller;
982    ///     let _ = controller.try_rumble(". -. -.");
983    /// }
984    /// ```
985    pub fn try_rumble(&mut self, pattern: impl AsRef<str>) -> Result<(), ControllerError> {
986        self.screen.try_set_text(pattern, 4, 1)
987    }
988}
989
990/// Errors that can occur when interacting with the controller.
991#[derive(Clone, Debug, Snafu)]
992pub enum ControllerError {
993    /// The controller is not connected to the Brain.
994    Offline,
995
996    /// Access to controller data is restricted by competition control.
997    ///
998    /// When this error occurs, the requested data is not available outside of
999    /// driver control mode.
1000    CompetitionControl,
1001
1002    /// Attempted to write a buffer to the controller's screen before the previous buffer was sent.
1003    WriteBusy,
1004}