vex_rt/
motor.rs

1//! # Motor API.
2
3use crate::{
4    bindings,
5    error::{get_errno, Error},
6    rtos::DataSource,
7};
8
9/// A struct which represents a V5 smart port configured as a motor.
10pub struct Motor {
11    port: u8,
12}
13
14impl Motor {
15    /// Constructs a new motor.
16    ///
17    /// # Safety
18    ///
19    /// This function is unsafe because it allows the user to create multiple
20    /// mutable references to the same motor. You likely want to implement
21    /// [`Robot::new()`](crate::robot::Robot::new()) instead.
22    pub unsafe fn new(
23        port: u8,
24        gearset: Gearset,
25        encoder_units: EncoderUnits,
26        reverse: bool,
27    ) -> Result<Self, MotorError> {
28        let mut motor = Self { port };
29        motor.set_reversed(reverse)?;
30        motor.set_gearing(gearset)?;
31        match unsafe { bindings::motor_set_encoder_units(port, encoder_units.into()) } {
32            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
33            _ => Ok(motor),
34        }
35    }
36
37    /// Sets the voltage for the motor from -127 to 127.
38    ///
39    /// This is designed to map easily to the input from the controller's analog
40    /// stick for simple opcontrol use. The actual behavior of the motor is
41    /// analogous to use of [`Motor::move_voltage()`].
42    pub fn move_i8(&mut self, voltage: i8) -> Result<(), MotorError> {
43        match unsafe { bindings::motor_move(self.port, voltage as i32) } {
44            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
45            _ => Ok(()),
46        }
47    }
48
49    /// Sets the target absolute position for the motor to move to.
50    ///
51    /// This movement is relative to the position of the motor when initialized
52    /// or the position when it was most recently reset with
53    /// [`Motor::set_zero_position()`].
54    ///
55    /// **Note:** This function simply sets the target for the motor, it does
56    /// not block program execution until the movement finishes.
57    pub fn move_absolute(&mut self, position: f64, velocity: i32) -> Result<(), MotorError> {
58        match unsafe { bindings::motor_move_absolute(self.port, position, velocity) } {
59            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
60            _ => Ok(()),
61        }
62    }
63
64    /// Sets the relative target position for the motor to move to.
65    ///
66    /// This movement is relative to the current position of the motor as given
67    /// in [`Motor::get_position()`]. Providing 10.0 as the position parameter
68    /// would result in the motor moving clockwise 10 ticks, no matter what
69    /// the current position is.
70    ///
71    /// **Note:** This function simply sets the target for the motor, it does
72    /// not block program execution until the movement finishes.
73    pub fn move_relative(&mut self, position: f64, velocity: i32) -> Result<(), MotorError> {
74        match unsafe { bindings::motor_move_relative(self.port, position, velocity) } {
75            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
76            _ => Ok(()),
77        }
78    }
79
80    /// Sets the velocity for the motor.
81    ///
82    /// This velocity corresponds to different actual speeds depending on the
83    /// gearset used for the motor. This results in a range of ±100 for
84    /// [`Gearset::ThirtySixToOne`] ±200 for [`Gearset::EighteenToOne`] and ±600
85    /// for [`Gearset::SixToOne`]. The velocity is held with PID to ensure
86    /// consistent speed.
87    pub fn move_velocity(&mut self, velocity: i32) -> Result<(), MotorError> {
88        match unsafe { bindings::motor_move_velocity(self.port, velocity) } {
89            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
90            _ => Ok(()),
91        }
92    }
93
94    /// Sets the output voltage for the motor from -12000 to 12000 in
95    /// millivolts.
96    pub fn move_voltage(&mut self, voltage: i32) -> Result<(), MotorError> {
97        match unsafe { bindings::motor_move_voltage(self.port, voltage) } {
98            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
99            _ => Ok(()),
100        }
101    }
102
103    /// Changes the output velocity for a profiled movement
104    /// ([`Motor::move_absolute()`] or [`Motor::move_relative()`]). This
105    /// will have no effect if the motor is not following a profiled movement.
106    pub fn modify_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> {
107        match unsafe { bindings::motor_modify_profiled_velocity(self.port, velocity) } {
108            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
109            _ => Ok(()),
110        }
111    }
112
113    /// Gets the target position set for the motor by the user.
114    pub fn get_target_position(&self) -> Result<f64, MotorError> {
115        match unsafe { bindings::motor_get_target_position(self.port) } {
116            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
117            x => Ok(x),
118        }
119    }
120
121    /// Gets the velocity commanded to the motor by the user.
122    pub fn get_target_velocity(&self) -> Result<i32, MotorError> {
123        match unsafe { bindings::motor_get_target_velocity(self.port) } {
124            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
125            x => Ok(x),
126        }
127    }
128
129    /// Gets the actual velocity of the motor.
130    pub fn get_actual_velocity(&self) -> Result<f64, MotorError> {
131        match unsafe { bindings::motor_get_actual_velocity(self.port) } {
132            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
133            x => Ok(x),
134        }
135    }
136
137    /// Gets the current drawn by the motor in milliamperes.
138    pub fn get_current_draw(&self) -> Result<i32, MotorError> {
139        match unsafe { bindings::motor_get_current_draw(self.port) } {
140            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
141            x => Ok(x),
142        }
143    }
144
145    /// Gets the direction of movement for the motor.
146    pub fn get_direction(&self) -> Result<Direction, MotorError> {
147        match unsafe { bindings::motor_get_direction(self.port) } {
148            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
149            1 => Ok(Direction::Positive),
150            -1 => Ok(Direction::Negative),
151            x => panic!(
152                "bindings::motor_get_direction returned unexpected value: {}",
153                x
154            ),
155        }
156    }
157
158    /// Gets the efficiency of the motor in percent.
159    pub fn get_efficiency(&self) -> Result<f64, MotorError> {
160        match unsafe { bindings::motor_get_efficiency(self.port) } {
161            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
162            x => Ok(x),
163        }
164    }
165
166    /// Gets the absolute position of the motor in encoder ticks.
167    pub fn get_position(&self) -> Result<f64, MotorError> {
168        match unsafe { bindings::motor_get_position(self.port) } {
169            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
170            x => Ok(x),
171        }
172    }
173
174    /// Gets the power drawn by the motor in Watts.
175    pub fn get_power(&self) -> Result<f64, MotorError> {
176        match unsafe { bindings::motor_get_power(self.port) } {
177            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
178            x => Ok(x),
179        }
180    }
181
182    /// Gets the temperature of the motor in degrees Celsius.
183    pub fn get_temperature(&self) -> Result<f64, MotorError> {
184        match unsafe { bindings::motor_get_temperature(self.port) } {
185            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
186            x => Ok(x),
187        }
188    }
189
190    /// Gets the torque of the motor in Newton-Meters.
191    pub fn get_torque(&self) -> Result<f64, MotorError> {
192        match unsafe { bindings::motor_get_torque(self.port) } {
193            x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
194            x => Ok(x),
195        }
196    }
197
198    /// Gets the voltage delivered to the motor in millivolts.
199    pub fn get_voltage(&self) -> Result<i32, MotorError> {
200        match unsafe { bindings::motor_get_voltage(self.port) } {
201            x if x == bindings::PROS_ERR_ => Err(MotorError::from_errno()),
202            x => Ok(x),
203        }
204    }
205
206    /// Checks if the motor is drawing over its current limit.
207    pub fn is_over_current(&self) -> Result<bool, MotorError> {
208        match unsafe { bindings::motor_is_over_current(self.port) } {
209            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
210            0 => Ok(false),
211            _ => Ok(true),
212        }
213    }
214
215    /// Checks if the motor's temperature is above its limit.
216    pub fn is_over_temp(&self) -> Result<bool, MotorError> {
217        match unsafe { bindings::motor_is_over_temp(self.port) } {
218            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
219            0 => Ok(false),
220            _ => Ok(true),
221        }
222    }
223
224    /// Gets the brake mode that was set for the motor.
225    pub fn get_brake_mode(&self) -> Result<BrakeMode, MotorError> {
226        match unsafe { bindings::motor_get_brake_mode(self.port) } {
227            bindings::motor_brake_mode_e_E_MOTOR_BRAKE_BRAKE => Ok(BrakeMode::Brake),
228            bindings::motor_brake_mode_e_E_MOTOR_BRAKE_COAST => Ok(BrakeMode::Coast),
229            bindings::motor_brake_mode_e_E_MOTOR_BRAKE_HOLD => Ok(BrakeMode::Hold),
230            bindings::motor_brake_mode_e_E_MOTOR_BRAKE_INVALID => Err(MotorError::from_errno()),
231            x => panic!(
232                "bindings::motor_get_brake_mode returned unexpected value: {}.",
233                x
234            ),
235        }
236    }
237
238    /// Gets the current limit for the motor in milliamperes.
239    ///
240    /// The default value is 2500 milliamperes, however the effective limit may
241    /// be lower if more then 8 motors are competing for power.
242    pub fn get_current_limit(&self) -> Result<i32, MotorError> {
243        match unsafe { bindings::motor_get_current_limit(self.port) } {
244            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
245            x => Ok(x),
246        }
247    }
248
249    /// Gets the gearset that was set for the motor.
250    pub fn get_gearing(&self) -> Result<Gearset, MotorError> {
251        match unsafe { bindings::motor_get_gearing(self.port) } {
252            bindings::motor_gearset_e_E_MOTOR_GEARSET_36 => Ok(Gearset::SixToOne),
253            bindings::motor_gearset_e_E_MOTOR_GEARSET_18 => Ok(Gearset::EighteenToOne),
254            bindings::motor_gearset_e_E_MOTOR_GEARSET_06 => Ok(Gearset::ThirtySixToOne),
255            bindings::motor_gearset_e_E_MOTOR_GEARSET_INVALID => Err(MotorError::from_errno()),
256            x => panic!(
257                "bindings::motor_get_gearing returned unexpected value: {}.",
258                x
259            ),
260        }
261    }
262
263    /// Gets the voltage limit set by the user in volts.
264    ///
265    /// Default value is 0V, which means that there is no software limitation
266    /// imposed on the voltage.
267    pub fn get_voltage_limit(&self) -> Result<i32, MotorError> {
268        match unsafe { bindings::motor_get_voltage_limit(self.port) } {
269            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
270            x => Ok(x),
271        }
272    }
273
274    /// Gets the operation direction of the motor as set by the user.
275    ///
276    /// Returns 1 if the motor has been reversed and 0 if the motor was not.
277    pub fn is_reversed(&self) -> Result<bool, MotorError> {
278        match unsafe { bindings::motor_is_reversed(self.port) } {
279            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
280            0 => Ok(false),
281            _ => Ok(true),
282        }
283    }
284
285    /// Gets the brake mode that was set for the motor.
286    pub fn set_brake_mode(&mut self, mode: BrakeMode) -> Result<(), MotorError> {
287        match unsafe { bindings::motor_set_brake_mode(self.port, mode.into()) } {
288            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
289            _ => Ok(()),
290        }
291    }
292
293    /// Sets the current limit for the motor in milliamperes.
294    pub fn set_current_limit(&mut self, limit: i32) -> Result<(), MotorError> {
295        match unsafe { bindings::motor_set_current_limit(self.port, limit) } {
296            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
297            _ => Ok(()),
298        }
299    }
300
301    /// Sets one of [`Gearset`] for the motor.
302    pub fn set_gearing(&mut self, gearset: Gearset) -> Result<(), MotorError> {
303        match unsafe { bindings::motor_set_gearing(self.port, gearset.into()) } {
304            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
305            _ => Ok(()),
306        }
307    }
308
309    /// Sets the reverse flag for the motor.
310    ///
311    /// This will invert its movements and the values returned for its position.
312    pub fn set_reversed(&mut self, reverse: bool) -> Result<(), MotorError> {
313        match unsafe { bindings::motor_set_reversed(self.port, reverse) } {
314            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
315            _ => Ok(()),
316        }
317    }
318
319    /// Sets the voltage limit for the motor in Volts.
320    pub fn set_voltage_limit(&mut self, limit: i32) -> Result<(), MotorError> {
321        match unsafe { bindings::motor_set_voltage_limit(self.port, limit) } {
322            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
323            _ => Ok(()),
324        }
325    }
326
327    /// Sets the "absolute" zero position of the motor.
328    pub fn set_zero_position(&mut self, position: f64) -> Result<(), MotorError> {
329        match unsafe { bindings::motor_set_zero_position(self.port, position) } {
330            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
331            _ => Ok(()),
332        }
333    }
334
335    /// Sets the "absolute" zero position of the motor to its current position.
336    pub fn tare_position(&mut self) -> Result<(), MotorError> {
337        match unsafe { bindings::motor_tare_position(self.port) } {
338            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
339            _ => Ok(()),
340        }
341    }
342
343    /// Sets the [`EncoderUnits`] for the motor.
344    pub fn set_encoder_units(&mut self, units: EncoderUnits) -> Result<(), MotorError> {
345        match unsafe { bindings::motor_set_encoder_units(self.port, units.into()) } {
346            bindings::PROS_ERR_ => Err(MotorError::from_errno()),
347            _ => Ok(()),
348        }
349    }
350
351    /// Gets the [`EncoderUnits`] set for the motor.
352    pub fn get_encoder_units(&self) -> Result<EncoderUnits, MotorError> {
353        match unsafe { bindings::motor_get_encoder_units(self.port) } {
354            bindings::motor_encoder_units_e_E_MOTOR_ENCODER_COUNTS => {
355                Ok(EncoderUnits::EncoderTicks)
356            }
357            bindings::motor_encoder_units_e_E_MOTOR_ENCODER_DEGREES => Ok(EncoderUnits::Degrees),
358            bindings::motor_encoder_units_e_E_MOTOR_ENCODER_ROTATIONS => {
359                Ok(EncoderUnits::Rotations)
360            }
361            bindings::motor_encoder_units_e_E_MOTOR_ENCODER_INVALID => {
362                Err(MotorError::from_errno())
363            }
364            x => panic!("bindings:get_encoder_units returned unexpected value {}", x),
365        }
366    }
367}
368
369impl DataSource for Motor {
370    type Data = MotorData;
371
372    type Error = MotorError;
373
374    fn read(&self) -> Result<Self::Data, Self::Error> {
375        Ok(MotorData {
376            target_position: self.get_target_position()?,
377            target_velocity: self.get_target_velocity()?,
378            actual_velocity: self.get_actual_velocity()?,
379            current_draw: self.get_current_draw()?,
380            direction: self.get_direction()?,
381            efficiency: self.get_efficiency()?,
382            position: self.get_position()?,
383            power: self.get_power()?,
384            temperature: self.get_temperature()?,
385            torque: self.get_torque()?,
386            voltage: self.get_voltage()?,
387            over_current: self.is_over_current()?,
388            over_temp: self.is_over_temp()?,
389            brake_mode: self.get_brake_mode()?,
390            current_limit: self.get_current_limit()?,
391            voltage_limit: self.get_voltage_limit()?,
392        })
393    }
394}
395
396/// Represents the data that can be read from a motor.
397#[derive(Clone, Copy, Debug, PartialEq)]
398pub struct MotorData {
399    /// The target position set for the motor by the user.
400    pub target_position: f64,
401    /// The velocity commanded to the motor by the user.
402    pub target_velocity: i32,
403    /// The actual velocity of the motor.
404    pub actual_velocity: f64,
405    /// The current drawn by the motor in milliamperes.
406    pub current_draw: i32,
407    /// The direction of movement for the motor.
408    pub direction: Direction,
409    /// The efficiency of the motor in percent.
410    pub efficiency: f64,
411    /// The absolute position of the motor in encoder ticks.
412    pub position: f64,
413    /// The power drawn by the motor in watts.
414    pub power: f64,
415    /// The temperature of the motor in degrees Celsius.
416    pub temperature: f64,
417    /// The torque of the motor in newton-metres.
418    pub torque: f64,
419    /// The voltage delivered to the motor in millivolts.
420    pub voltage: i32,
421    /// Whether the motor is drawing over its current limit.
422    pub over_current: bool,
423    /// Whether the motor's temperature is above its limit.
424    pub over_temp: bool,
425    /// The brake mode that was set for the motor.
426    pub brake_mode: BrakeMode,
427    /// The current limit for the motor in milliamperes.
428    pub current_limit: i32,
429    /// The voltage limit set by the user in volts.
430    pub voltage_limit: i32,
431}
432
433/// Represents possible errors for motor operations.
434#[derive(Debug)]
435pub enum MotorError {
436    /// Port is out of range (1-21).
437    PortOutOfRange,
438    /// Port cannot be configured as a motor.
439    PortNotMotor,
440    /// Unknown error.
441    Unknown(i32),
442}
443
444impl MotorError {
445    fn from_errno() -> Self {
446        match get_errno() {
447            libc::ENXIO => Self::PortOutOfRange,
448            libc::ENODEV => Self::PortNotMotor,
449            x => Self::Unknown(x),
450        }
451    }
452}
453
454impl From<MotorError> for Error {
455    fn from(err: MotorError) -> Self {
456        match err {
457            MotorError::PortOutOfRange => Error::Custom("port out of range".into()),
458            MotorError::PortNotMotor => Error::Custom("port not a motor".into()),
459            MotorError::Unknown(n) => Error::System(n),
460        }
461    }
462}
463
464/// Represents possible brake modes for a motor.
465#[derive(Clone, Copy, Debug, PartialEq, Eq)]
466pub enum BrakeMode {
467    /// Motor coasts when stopped.
468    Coast,
469    /// Motor brakes when stopped.
470    Brake,
471    /// Motor holds position when stopped.
472    Hold,
473}
474
475impl From<BrakeMode> for bindings::motor_brake_mode_e {
476    fn from(mode: BrakeMode) -> Self {
477        match mode {
478            BrakeMode::Coast => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_COAST,
479            BrakeMode::Brake => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_BRAKE,
480            BrakeMode::Hold => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_HOLD,
481        }
482    }
483}
484
485/// Represents possible gear cartridges for a motor.
486#[derive(Clone, Copy, Debug, PartialEq, Eq)]
487pub enum Gearset {
488    /// Blue 6:1 Gearset (600RPM).
489    SixToOne,
490    /// Green 18:1 Gearset (200RPM).
491    EighteenToOne,
492    /// Red 36:1 Gearset (100RPM).
493    ThirtySixToOne,
494}
495
496impl From<Gearset> for bindings::motor_gearset_e {
497    fn from(gearset: Gearset) -> Self {
498        match gearset {
499            Gearset::SixToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_06,
500            Gearset::EighteenToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_18,
501            Gearset::ThirtySixToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_36,
502        }
503    }
504}
505
506/// Represents two possible directions of movement for a robot.
507#[derive(Clone, Copy, Debug, PartialEq, Eq)]
508pub enum Direction {
509    /// The positive direction.
510    Positive,
511    /// The negative direction.
512    Negative,
513}
514
515/// Represents the possible encoder units.
516#[derive(Clone, Copy)]
517pub enum EncoderUnits {
518    /// The number of ticks of the internal motor encoder.
519    /// - 300 ticks/rev with [`Gearset::SixToOne`].
520    /// - 900 ticks/rev with [`Gearset::EighteenToOne`].
521    /// - 1800 ticks/rev with [`Gearset::ThirtySixToOne`].
522    EncoderTicks,
523    /// Degrees.
524    Degrees,
525    /// Rotations.
526    Rotations,
527}
528
529impl From<EncoderUnits> for bindings::motor_encoder_units_e {
530    fn from(units: EncoderUnits) -> Self {
531        match units {
532            EncoderUnits::EncoderTicks => bindings::motor_encoder_units_e_E_MOTOR_ENCODER_COUNTS,
533            EncoderUnits::Degrees => bindings::motor_encoder_units_e_E_MOTOR_ENCODER_DEGREES,
534            EncoderUnits::Rotations => bindings::motor_encoder_units_e_E_MOTOR_ENCODER_ROTATIONS,
535        }
536    }
537}