pros_devices/adi/
mod.rs

1//! ADI (Triport) devices on the Vex V5.
2
3use pros_core::{bail_on, error::PortError, map_errno};
4use pros_sys::{adi_port_config_e_t, E_ADI_ERR, PROS_ERR};
5use snafu::Snafu;
6
7//TODO: much more in depth module documentation for device modules as well as this module.
8pub mod analog;
9pub mod digital;
10pub mod pwm;
11
12pub mod encoder;
13pub mod gyro;
14pub mod linetracker;
15pub mod motor;
16pub mod potentiometer;
17pub mod solenoid;
18pub mod switch;
19pub mod ultrasonic;
20
21pub use analog::AdiAnalogIn;
22pub use digital::{AdiDigitalIn, AdiDigitalOut};
23pub use encoder::AdiEncoder;
24pub use gyro::AdiGyro;
25pub use linetracker::AdiLineTracker;
26pub use motor::AdiMotor;
27pub use potentiometer::AdiPotentiometer;
28pub use solenoid::AdiSolenoid;
29pub use ultrasonic::AdiUltrasonic;
30
31/// Represents an ADI (three wire) port on a V5 Brain or V5 Three Wire Expander.
32#[derive(Debug, Eq, PartialEq)]
33pub struct AdiPort {
34    /// The index of the port (port number).
35    ///
36    /// Ports are indexed starting from 1.
37    index: u8,
38
39    /// The index of this port's associated [`AdiExpander`].
40    ///
41    /// If this port is not associated with an [`AdiExpander`] it should be set to `None`.
42    expander_index: Option<u8>,
43}
44
45impl AdiPort {
46    /// Create a new port.
47    ///
48    /// # Safety
49    ///
50    /// Creating new `AdiPort`s is inherently unsafe due to the possibility of constructing
51    /// more than one device on the same port index allowing multiple mutable references to
52    /// the same hardware device. Prefer using [`Peripherals`](crate::peripherals::Peripherals) to register devices if possible.
53    pub const unsafe fn new(index: u8, expander_index: Option<u8>) -> Self {
54        Self {
55            index,
56            expander_index,
57        }
58    }
59
60    /// Get the index of the port (port number).
61    ///
62    /// Ports are indexed starting from 1.
63    pub const fn index(&self) -> u8 {
64        self.index
65    }
66
67    /// Get the index of this port's associated [`AdiExpander`] smart port, or `None` if this port is not
68    /// associated with an expander.
69    pub const fn expander_index(&self) -> Option<u8> {
70        self.expander_index
71    }
72
73    /// Get the index of this port's associated [`AdiExpander`] smart port, or `pros_sys::adi::INTERNAL_ADI_PORT`
74    /// if this port is not associated with an expander.
75    pub(crate) fn internal_expander_index(&self) -> u8 {
76        self.expander_index
77            .unwrap_or(pros_sys::adi::INTERNAL_ADI_PORT as u8)
78    }
79
80    /// Get the type of device this port is currently configured as.
81    pub fn configured_type(&self) -> Result<AdiDeviceType, AdiError> {
82        bail_on!(PROS_ERR, unsafe {
83            pros_sys::ext_adi::ext_adi_port_get_config(self.internal_expander_index(), self.index())
84        })
85        .try_into()
86    }
87}
88
89/// Common functionality for a ADI (three-wire) devices.
90pub trait AdiDevice {
91    /// The type that port_index should return. This is usually `u8`, but occasionally `(u8, u8)`.
92    type PortIndexOutput;
93
94    /// Get the index of the [`AdiPort`] this device is registered on.
95    ///
96    /// Ports are indexed starting from 1.
97    fn port_index(&self) -> Self::PortIndexOutput;
98
99    /// Get the index of the [`AdiPort`] this device is registered on.
100    ///
101    /// Ports are indexed starting from 1.
102    fn expander_port_index(&self) -> Option<u8>;
103
104    /// Get the variant of [`AdiDeviceType`] that this device is associated with.
105    fn device_type(&self) -> AdiDeviceType;
106}
107
108/// Represents a possible type of device that can be registered on a [`AdiPort`].
109#[repr(i32)]
110#[derive(Debug, Clone, Copy, Eq, PartialEq)]
111pub enum AdiDeviceType {
112    /// Generic analog input.
113    AnalogIn = pros_sys::adi::E_ADI_ANALOG_IN,
114
115    /// Generic PWM output.
116    ///
117    /// This is actually equivalent `pros_sys::adi::E_ADI_ANALOG_OUT`, which is a misnomer.
118    /// "Analog Out" in reality outputs an 8-bit PWM value.
119    PwmOut = pros_sys::adi::E_ADI_ANALOG_OUT,
120
121    /// Generic digital input.
122    DigitalIn = pros_sys::adi::E_ADI_DIGITAL_IN,
123
124    /// Generic digital output.
125    DigitalOut = pros_sys::adi::E_ADI_DIGITAL_OUT,
126
127    /// Cortex-era yaw-rate gyroscope.
128    LegacyGyro = pros_sys::adi::E_ADI_LEGACY_GYRO,
129
130    /// Cortex-era servo motor.
131    LegacyServo = pros_sys::adi::E_ADI_LEGACY_SERVO,
132
133    /// MC29 Controller Output
134    ///
135    /// This differs from [`Self::PwmOut`] in that it is specifically designed for controlling
136    /// legacy ADI motors. Rather than taking a u8 for output, it takes a i8 allowing negative
137    /// values to be sent for controlling motors in reverse with a nicer API.
138    LegacyPwm = pros_sys::adi::E_ADI_LEGACY_PWM,
139
140    /// Cortex-era encoder.
141    LegacyEncoder = pros_sys::E_ADI_LEGACY_ENCODER,
142
143    /// Cortex-era ultrasonic sensor.
144    LegacyUltrasonic = pros_sys::E_ADI_LEGACY_ULTRASONIC,
145}
146
147impl TryFrom<adi_port_config_e_t> for AdiDeviceType {
148    type Error = AdiError;
149
150    fn try_from(value: adi_port_config_e_t) -> Result<Self, Self::Error> {
151        bail_on!(E_ADI_ERR, value);
152
153        match value {
154            pros_sys::E_ADI_ANALOG_IN => Ok(AdiDeviceType::AnalogIn),
155            pros_sys::E_ADI_ANALOG_OUT => Ok(AdiDeviceType::PwmOut),
156            pros_sys::E_ADI_DIGITAL_IN => Ok(AdiDeviceType::DigitalIn),
157            pros_sys::E_ADI_DIGITAL_OUT => Ok(AdiDeviceType::DigitalOut),
158
159            pros_sys::E_ADI_LEGACY_GYRO => Ok(AdiDeviceType::LegacyGyro),
160
161            pros_sys::E_ADI_LEGACY_SERVO => Ok(AdiDeviceType::LegacyServo),
162            pros_sys::E_ADI_LEGACY_PWM => Ok(AdiDeviceType::LegacyPwm),
163
164            pros_sys::E_ADI_LEGACY_ENCODER => Ok(AdiDeviceType::LegacyEncoder),
165            pros_sys::E_ADI_LEGACY_ULTRASONIC => Ok(AdiDeviceType::LegacyUltrasonic),
166
167            _ => Err(AdiError::UnknownDeviceType),
168        }
169    }
170}
171
172impl From<AdiDeviceType> for adi_port_config_e_t {
173    fn from(value: AdiDeviceType) -> Self {
174        value as _
175    }
176}
177
178#[derive(Debug, Snafu)]
179/// Errors that can occur when working with ADI devices.
180pub enum AdiError {
181    /// Another resource is currently trying to access the ADI.
182    AlreadyInUse,
183
184    /// PROS returned an unrecognized device type.
185    UnknownDeviceType,
186
187    /// The port specified has not been configured for the device type specified.
188    PortNotConfigured,
189
190    /// ADI devices may only be initialized from one expander port.
191    ExpanderPortMismatch,
192
193    /// A given value is not correct, or the buffer is null.
194    InvalidValue,
195
196    #[snafu(display("{source}"), context(false))]
197    /// An error occurred while interacting with a port.
198    Port {
199        /// The source of the error
200        source: PortError,
201    },
202}
203
204map_errno! {
205    AdiError {
206        EACCES => Self::AlreadyInUse,
207        EADDRINUSE => Self::PortNotConfigured,
208        EINVAL => Self::InvalidValue,
209    }
210    inherit PortError;
211}