vexide_devices/smart/
electromagnet.rs

1//! V5 Electromagnet
2//!
3//! The V5 electromagnet is a device unique to the V5 workcell kit. It is a simple
4//! device that produces a magnetic field at a provided power level.
5//! The power level does not have specific units.
6//!
7//! # Hardware Overview
8//!
9//! Not much information can be found on the V5 workcell electromagnet;
10//! however, the electromagnet is intended to be used to pick up V5 Workcell colored disks.
11//! We can assume that the lower bound on the electromagnet's strength is the weight of a V5 Workcell colored disk.
12//! Assuming that the plastic part of the disk is made of ABS plastic and the metal part is solid iron,
13//! the electromagnet can lift at least ≈0.24oz based off of the CAD model files for the V5 Workcell kit provided by VEX.
14
15use core::time::Duration;
16
17use vex_sdk::{
18    vexDeviceMagnetCurrentGet, vexDeviceMagnetPowerGet, vexDeviceMagnetPowerSet,
19    vexDeviceMagnetStatusGet, vexDeviceMagnetTemperatureGet, V5_DeviceT,
20};
21
22use super::{SmartDevice, SmartDeviceType, SmartPort};
23use crate::PortError;
24
25/// An electromagnet plugged into a smart port.
26#[derive(Debug, Eq, PartialEq)]
27pub struct Electromagnet {
28    port: SmartPort,
29    device: V5_DeviceT,
30}
31
32// SAFETY: Required because we store a raw pointer to the device handle to avoid it getting from the
33// SDK each device function. Simply sharing a raw pointer across threads is not inherently unsafe.
34unsafe impl Send for Electromagnet {}
35unsafe impl Sync for Electromagnet {}
36
37impl Electromagnet {
38    /// Maximum duration that the magnet can be powered for.
39    pub const MAX_POWER_DURATION: Duration = Duration::from_secs(2);
40
41    /// Creates a new electromagnet from a [`SmartPort`].
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use vexide::prelude::*;
47    /// use core::time::Duration;
48    ///
49    /// #[vexide::main]
50    /// async fn main(peripherals: Peripherals) {
51    ///     let mut electromagnet = Electromagnet::new(peripherals.port_1);
52    ///     // Use the electromagnet
53    ///     _ = electromagnet.set_power(1.0, Electromagnet::MAX_POWER_DURATION);
54    ///     _ = electromagnet.set_power(-0.2, Duration::from_secs(1));
55    /// }
56    /// ```
57    #[must_use]
58    pub fn new(port: SmartPort) -> Self {
59        Self {
60            device: unsafe { port.device_handle() },
61            port,
62        }
63    }
64
65    /// Sets the power level of the magnet for a given duration.
66    ///
67    /// Power is expressed as a number from [-1.0, 1.0]. Larger power values will result
68    /// in a stronger force of attraction from the magnet.
69    ///
70    /// # Errors
71    ///
72    /// - A [`PortError::Disconnected`] error is returned if an electromagnet device was required but not connected.
73    /// - A [`PortError::IncorrectDevice`] error is returned if an electromagnet device was required but
74    ///   something else was connected.
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use vexide::prelude::*;
80    ///
81    /// #[vexide::main]
82    /// async fn main(peripherals: Peripherals) {
83    ///     let mut electromagnet = Electromagnet::new(peripherals.port_1);
84    ///     _ = electromagnet.set_power(1.0, Electromagnet::MAX_POWER_DURATION);
85    /// }
86    /// ```
87    pub fn set_power(&mut self, power: f64, duration: Duration) -> Result<(), PortError> {
88        self.validate_port()?;
89
90        let power = power.clamp(-1.0, 1.0);
91
92        unsafe {
93            vexDeviceMagnetPowerSet(self.device, (power * 100.0) as _, duration.as_millis() as _);
94        }
95
96        Ok(())
97    }
98
99    /// Returns the user-set power level as a number from [-1.0, 1.0].
100    ///
101    /// # Errors
102    ///
103    /// - A [`PortError::Disconnected`] error is returned if an electromagnet device was required but not connected.
104    /// - A [`PortError::IncorrectDevice`] error is returned if an electromagnet device was required but
105    ///   something else was connected.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use vexide::prelude::*;
111    ///
112    /// #[vexide::main]
113    /// async fn main(peripherals: Peripherals) {
114    ///     let mut electromagnet = Electromagnet::new(peripherals.port_1);
115    ///     _ = electromagnet.set_power(0.5, Electromagnet::MAX_POWER_DURATION);
116    ///
117    ///     if let Ok(power) = electromagnet.power() {
118    ///         println!("Power: {}%", power * 100.0);
119    ///     }
120    /// }
121    /// ```
122    pub fn power(&self) -> Result<f64, PortError> {
123        self.validate_port()?;
124
125        Ok(f64::from(unsafe { vexDeviceMagnetPowerGet(self.device) }) / 100.0)
126    }
127
128    /// Returns the magnet's electrical current in amps.
129    ///
130    /// # Errors
131    ///
132    /// - A [`PortError::Disconnected`] error is returned if an electromagnet device was required but not connected.
133    /// - A [`PortError::IncorrectDevice`] error is returned if an electromagnet device was required but
134    ///   something else was connected.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use vexide::prelude::*;
140    ///
141    /// #[vexide::main]
142    /// async fn main(peripherals: Peripherals) {
143    ///     let mut electromagnet = Electromagnet::new(peripherals.port_1);
144    ///     _ = electromagnet.set_power(1.0, Electromagnet::MAX_POWER_DURATION);
145    ///
146    ///     if let Ok(current) = electromagnet.current() {
147    ///         println!("Current: {}A", current);
148    ///     }
149    /// }
150    /// ```
151    pub fn current(&self) -> Result<f64, PortError> {
152        self.validate_port()?;
153
154        Ok(unsafe { vexDeviceMagnetCurrentGet(self.device) } / 1000.0)
155    }
156
157    /// Returns the internal temperature of the magnet in degrees celsius.
158    ///
159    /// # Errors
160    ///
161    /// - A [`PortError::Disconnected`] error is returned if an electromagnet device was required but not connected.
162    /// - A [`PortError::IncorrectDevice`] error is returned if an electromagnet device was required but
163    ///   something else was connected.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use vexide::prelude::*;
169    ///
170    /// #[vexide::main]
171    /// async fn main(peripherals: Peripherals) {
172    ///     let electromagnet = Electromagnet::new(peripherals.port_1);
173    ///
174    ///     if let Ok(temperature) = electromagnet.temperature() {
175    ///         println!("Temperature: {}°C", temperature);
176    ///     }
177    /// }
178    /// ```
179    pub fn temperature(&self) -> Result<f64, PortError> {
180        self.validate_port()?;
181
182        Ok(unsafe { vexDeviceMagnetTemperatureGet(self.device) })
183    }
184
185    /// Returns the status code of the magnet.
186    ///
187    /// # Errors
188    ///
189    /// - A [`PortError::Disconnected`] error is returned if an electromagnet device was required but not connected.
190    /// - A [`PortError::IncorrectDevice`] error is returned if an electromagnet device was required but
191    ///   something else was connected.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use vexide::prelude::*;
197    ///
198    /// #[vexide::main]
199    /// async fn main(peripherals: Peripherals) {
200    ///     let electromagnet = Electromagnet::new(peripherals.port_1);
201    ///
202    ///     if let Ok(status) = electromagnet.status() {
203    ///         println!("Status: {:b}", status);
204    ///     }
205    /// }
206    /// ```
207    pub fn status(&self) -> Result<u32, PortError> {
208        self.validate_port()?;
209
210        Ok(unsafe { vexDeviceMagnetStatusGet(self.device) })
211    }
212}
213
214impl SmartDevice for Electromagnet {
215    fn port_number(&self) -> u8 {
216        self.port.number()
217    }
218
219    fn device_type(&self) -> SmartDeviceType {
220        SmartDeviceType::Electromagnet
221    }
222}
223impl From<Electromagnet> for SmartPort {
224    fn from(device: Electromagnet) -> Self {
225        device.port
226    }
227}