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}