1use core::time::Duration;
4
5use bitflags::bitflags;
6use pros_core::{bail_on, error::PortError, map_errno};
7use pros_sys::{PROS_ERR, PROS_ERR_F};
8use snafu::Snafu;
9
10use super::{SmartDevice, SmartDeviceTimestamp, SmartDeviceType, SmartPort};
11use crate::Position;
12
13#[derive(Debug, PartialEq)]
15pub struct Motor {
16 port: SmartPort,
17 target: MotorControl,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq)]
22pub enum MotorControl {
23 Brake(BrakeMode),
25
26 Voltage(f64),
28
29 Velocity(i32),
31
32 Position(Position, i32),
34}
35
36#[derive(Debug, Clone, Copy, Eq, PartialEq)]
38pub enum Direction {
39 Forward,
41
42 Reverse,
44}
45
46impl Direction {
47 pub const fn is_forward(&self) -> bool {
49 match self {
50 Self::Forward => true,
51 Self::Reverse => false,
52 }
53 }
54
55 pub const fn is_reverse(&self) -> bool {
57 match self {
58 Self::Forward => false,
59 Self::Reverse => true,
60 }
61 }
62}
63
64impl Motor {
65 pub const MAX_VOLTAGE: f64 = 12.0;
67
68 pub const DATA_READ_RATE: Duration = Duration::from_millis(10);
70
71 pub const DATA_WRITE_RATE: Duration = Duration::from_millis(5);
73
74 pub fn new(
76 port: SmartPort,
77 gearset: Gearset,
78 direction: Direction,
79 ) -> Result<Self, MotorError> {
80 bail_on!(PROS_ERR, unsafe {
81 pros_sys::motor_set_encoder_units(port.index() as i8, pros_sys::E_MOTOR_ENCODER_DEGREES)
82 });
83
84 let mut motor = Self {
85 port,
86 target: MotorControl::Voltage(0.0),
87 };
88
89 motor.set_gearset(gearset)?;
90 motor.set_direction(direction)?;
91
92 Ok(motor)
93 }
94
95 pub fn set_target(&mut self, target: MotorControl) -> Result<(), MotorError> {
99 match target {
100 MotorControl::Brake(mode) => unsafe {
101 bail_on!(
102 PROS_ERR,
103 pros_sys::motor_set_brake_mode(self.port.index() as i8, mode.into())
104 );
105 bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index() as i8));
106 },
107 MotorControl::Velocity(rpm) => unsafe {
108 bail_on!(
109 PROS_ERR,
110 pros_sys::motor_set_brake_mode(
111 self.port.index() as i8,
112 pros_sys::E_MOTOR_BRAKE_COAST
113 )
114 );
115 bail_on!(
116 PROS_ERR,
117 pros_sys::motor_move_velocity(self.port.index() as i8, rpm)
118 );
119 },
120 MotorControl::Voltage(volts) => {
121 bail_on!(PROS_ERR, unsafe {
122 pros_sys::motor_move_voltage(self.port.index() as i8, (volts * 1000.0) as i32)
123 });
124 }
125 MotorControl::Position(position, velocity) => unsafe {
126 bail_on!(
127 PROS_ERR,
128 pros_sys::motor_set_brake_mode(
129 self.port.index() as i8,
130 pros_sys::E_MOTOR_BRAKE_COAST
131 )
132 );
133 bail_on!(
134 PROS_ERR,
135 pros_sys::motor_move_absolute(
136 self.port.index() as i8,
137 position.into_degrees(),
138 velocity,
139 )
140 );
141 },
142 }
143
144 self.target = target;
145 Ok(())
146 }
147
148 pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorError> {
150 self.set_target(MotorControl::Brake(mode))
151 }
152
153 pub fn set_velocity(&mut self, rpm: i32) -> Result<(), MotorError> {
159 self.set_target(MotorControl::Velocity(rpm))
160 }
161
162 pub fn set_voltage(&mut self, volts: f64) -> Result<(), MotorError> {
167 self.set_target(MotorControl::Voltage(volts))
168 }
169
170 pub fn set_position_target(
172 &mut self,
173 position: Position,
174 velocity: i32,
175 ) -> Result<(), MotorError> {
176 self.set_target(MotorControl::Position(position, velocity))
177 }
178
179 pub fn update_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> {
183 bail_on!(PROS_ERR, unsafe {
184 pros_sys::motor_modify_profiled_velocity(self.port.index() as i8, velocity)
185 });
186
187 match self.target {
188 MotorControl::Position(position, _) => {
189 self.target = MotorControl::Position(position, velocity)
190 }
191 _ => {}
192 }
193
194 Ok(())
195 }
196
197 pub fn target(&self) -> MotorControl {
199 self.target
200 }
201
202 pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorError> {
204 bail_on!(PROS_ERR, unsafe {
205 pros_sys::motor_set_gearing(self.port.index() as i8, gearset as i32)
206 });
207 Ok(())
208 }
209
210 pub fn gearset(&self) -> Result<Gearset, MotorError> {
212 unsafe { pros_sys::motor_get_gearing(self.port.index() as i8).try_into() }
213 }
214
215 pub fn velocity(&self) -> Result<f64, MotorError> {
217 Ok(bail_on!(PROS_ERR_F, unsafe {
218 pros_sys::motor_get_actual_velocity(self.port.index() as i8)
219 }))
220 }
221
222 pub fn power(&self) -> Result<f64, MotorError> {
224 Ok(bail_on!(PROS_ERR_F, unsafe {
225 pros_sys::motor_get_power(self.port.index() as i8)
226 }))
227 }
228
229 pub fn torque(&self) -> Result<f64, MotorError> {
231 Ok(bail_on!(PROS_ERR_F, unsafe {
232 pros_sys::motor_get_torque(self.port.index() as i8)
233 }))
234 }
235
236 pub fn voltage(&self) -> Result<f64, MotorError> {
238 let millivolts = bail_on!(PROS_ERR, unsafe {
240 pros_sys::motor_get_voltage(self.port.index() as i8)
241 });
242 Ok(millivolts as f64 / 1000.0)
243 }
244
245 pub fn position(&self) -> Result<Position, MotorError> {
247 Ok(Position::from_degrees(bail_on!(PROS_ERR_F, unsafe {
248 pros_sys::motor_get_position(self.port.index() as i8)
249 })))
250 }
251
252 pub fn raw_position(&self) -> Result<(i32, SmartDeviceTimestamp), MotorError> {
256 let timestamp = 0 as *mut u32;
257
258 let ticks = bail_on!(PROS_ERR, unsafe {
263 pros_sys::motor_get_raw_position(self.port.index() as i8, timestamp)
264 });
265
266 Ok((ticks, SmartDeviceTimestamp(unsafe { *timestamp })))
267 }
268
269 pub fn current(&self) -> Result<f64, MotorError> {
271 Ok(bail_on!(PROS_ERR, unsafe {
272 pros_sys::motor_get_current_draw(self.port.index() as i8)
273 }) as f64
274 / 1000.0)
275 }
276
277 pub fn efficiency(&self) -> Result<f64, MotorError> {
283 Ok(bail_on!(PROS_ERR_F, unsafe {
284 pros_sys::motor_get_efficiency(self.port.index() as i8)
285 }) / 100.0)
286 }
287
288 pub fn zero(&mut self) -> Result<(), MotorError> {
291 bail_on!(PROS_ERR, unsafe {
292 pros_sys::motor_tare_position(self.port.index() as i8)
293 });
294 Ok(())
295 }
296
297 pub fn set_position(&mut self, position: Position) -> Result<(), MotorError> {
300 bail_on!(PROS_ERR, unsafe {
301 pros_sys::motor_set_zero_position(self.port.index() as i8, position.into_degrees())
302 });
303 Ok(())
304 }
305
306 pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorError> {
308 bail_on!(PROS_ERR, unsafe {
309 pros_sys::motor_set_current_limit(self.port.index() as i8, (limit * 1000.0) as i32)
310 });
311 Ok(())
312 }
313
314 pub fn set_voltage_limit(&mut self, limit: f64) -> Result<(), MotorError> {
316 bail_on!(PROS_ERR, unsafe {
317 pros_sys::motor_set_voltage_limit(self.port.index() as i8, (limit * 1000.0) as i32)
320 });
321
322 Ok(())
323 }
324
325 pub fn current_limit(&self) -> Result<f64, MotorError> {
327 Ok(bail_on!(PROS_ERR, unsafe {
328 pros_sys::motor_get_current_limit(self.port.index() as i8)
329 }) as f64
330 / 1000.0)
331 }
332
333 pub fn status(&self) -> Result<MotorStatus, MotorError> {
348 let bits = bail_on!(PROS_ERR as u32, unsafe {
349 pros_sys::motor_get_flags(self.port.index() as i8)
350 });
351
352 if (bits & pros_sys::E_MOTOR_FLAGS_BUSY) != 0 {
355 return Err(MotorError::Busy);
356 }
357
358 Ok(MotorStatus::from_bits_retain(bits))
359 }
360
361 pub fn faults(&self) -> Result<MotorFaults, MotorError> {
363 let bits = bail_on!(PROS_ERR as u32, unsafe {
364 pros_sys::motor_get_faults(self.port.index() as i8)
365 });
366
367 Ok(MotorFaults::from_bits_retain(bits))
368 }
369
370 pub fn is_over_temperature(&self) -> Result<bool, MotorError> {
372 Ok(self.faults()?.contains(MotorFaults::OVER_TEMPERATURE))
373 }
374
375 pub fn is_over_current(&self) -> Result<bool, MotorError> {
377 Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT))
378 }
379
380 pub fn is_driver_fault(&self) -> Result<bool, MotorError> {
382 Ok(self.faults()?.contains(MotorFaults::DRIVER_FAULT))
383 }
384
385 pub fn is_driver_over_current(&self) -> Result<bool, MotorError> {
387 Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT))
388 }
389
390 pub fn set_direction(&mut self, direction: Direction) -> Result<(), MotorError> {
392 bail_on!(PROS_ERR, unsafe {
393 pros_sys::motor_set_reversed(self.port.index() as i8, direction.is_reverse())
394 });
395 Ok(())
396 }
397
398 pub fn direction(&self) -> Result<Direction, MotorError> {
400 let reversed = bail_on!(PROS_ERR, unsafe {
401 pros_sys::motor_is_reversed(self.port.index() as i8)
402 }) == 1;
403
404 Ok(match reversed {
405 false => Direction::Forward,
406 true => Direction::Reverse,
407 })
408 }
409
410 #[cfg(feature = "dangerous_motor_tuning")]
422 pub fn set_velocity_tuning_constants(
423 &mut self,
424 constants: MotorTuningConstants,
425 ) -> Result<(), MotorError> {
426 bail_on!(PROS_ERR, unsafe {
427 #[allow(deprecated)]
428 pros_sys::motor_set_pos_pid_full(self.port.index() as i8, constants.into())
429 });
430 Ok(())
431 }
432
433 #[cfg(feature = "dangerous_motor_tuning")]
445 pub fn set_position_tuning_constants(
446 &mut self,
447 constants: MotorTuningConstants,
448 ) -> Result<(), MotorError> {
449 bail_on!(PROS_ERR, unsafe {
450 #[allow(deprecated)]
451 pros_sys::motor_set_vel_pid_full(self.port.index() as i8, constants.into())
452 });
453 Ok(())
454 }
455}
456
457impl SmartDevice for Motor {
458 fn port_index(&self) -> u8 {
459 self.port.index()
460 }
461
462 fn device_type(&self) -> SmartDeviceType {
463 SmartDeviceType::Motor
464 }
465}
466
467#[derive(Debug, Clone, Copy, Eq, PartialEq)]
469#[repr(i32)]
470pub enum BrakeMode {
471 None = pros_sys::E_MOTOR_BRAKE_COAST,
473 Brake = pros_sys::E_MOTOR_BRAKE_BRAKE,
475 Hold = pros_sys::E_MOTOR_BRAKE_HOLD,
477}
478
479impl TryFrom<pros_sys::motor_brake_mode_e_t> for BrakeMode {
480 type Error = MotorError;
481
482 fn try_from(value: pros_sys::motor_brake_mode_e_t) -> Result<Self, MotorError> {
483 bail_on!(PROS_ERR, value);
484
485 Ok(match value {
486 pros_sys::E_MOTOR_BRAKE_COAST => Self::None,
487 pros_sys::E_MOTOR_BRAKE_BRAKE => Self::Brake,
488 pros_sys::E_MOTOR_BRAKE_HOLD => Self::Hold,
489 _ => unreachable!(),
490 })
491 }
492}
493
494impl From<BrakeMode> for pros_sys::motor_brake_mode_e_t {
495 fn from(value: BrakeMode) -> pros_sys::motor_brake_mode_e_t {
496 value as _
497 }
498}
499
500bitflags! {
501 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
503 pub struct MotorFaults: u32 {
504 const OVER_TEMPERATURE = pros_sys::E_MOTOR_FAULT_MOTOR_OVER_TEMP;
506
507 const OVER_CURRENT = pros_sys::E_MOTOR_FAULT_OVER_CURRENT;
509
510 const DRIVER_FAULT = pros_sys::E_MOTOR_FAULT_DRIVER_FAULT;
512
513 const DRIVER_OVER_CURRENT = pros_sys::E_MOTOR_FAULT_DRV_OVER_CURRENT;
515 }
516}
517
518bitflags! {
519 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
521 pub struct MotorStatus: u32 {
522 #[deprecated(
524 since = "0.9.0",
525 note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future."
526 )]
527 const ZERO_VELOCITY = pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY;
528
529 #[deprecated(
531 since = "0.9.0",
532 note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future."
533 )]
534 const ZERO_POSITION = pros_sys::E_MOTOR_FLAGS_ZERO_POSITION;
535
536 const BUSY = pros_sys::E_MOTOR_FLAGS_BUSY;
538 }
539}
540
541#[derive(Debug, Clone, Copy, PartialEq, Eq)]
543#[repr(i32)]
544pub enum Gearset {
545 Red = pros_sys::E_MOTOR_GEAR_RED,
547 Green = pros_sys::E_MOTOR_GEAR_GREEN,
549 Blue = pros_sys::E_MOTOR_GEAR_BLUE,
551}
552
553impl Gearset {
554 pub const RATIO_36: Gearset = Self::Red;
556 pub const RATIO_18: Gearset = Self::Green;
558 pub const RATIO_6: Gearset = Self::Blue;
560
561 pub const RPM_100: Gearset = Self::Red;
563 pub const RPM_200: Gearset = Self::Green;
565 pub const RPM_600: Gearset = Self::Blue;
567
568 pub const MAX_RED_RPM: f64 = 100.0;
570 pub const MAX_GREEN_RPM: f64 = 200.0;
572 pub const MAX_BLUE_RPM: f64 = 600.0;
574
575 pub const RED_TICKS_PER_REVOLUTION: u32 = 1800;
577 pub const GREEN_TICKS_PER_REVOLUTION: u32 = 900;
579 pub const BLUE_TICKS_PER_REVOLUTION: u32 = 300;
581
582 pub const fn max_rpm(&self) -> f64 {
584 match self {
585 Self::Red => Self::MAX_RED_RPM,
586 Self::Green => Self::MAX_GREEN_RPM,
587 Self::Blue => Self::MAX_BLUE_RPM,
588 }
589 }
590
591 pub const fn ticks_per_revolution(&self) -> u32 {
593 match self {
594 Self::Red => Self::RED_TICKS_PER_REVOLUTION,
595 Self::Green => Self::GREEN_TICKS_PER_REVOLUTION,
596 Self::Blue => Self::BLUE_TICKS_PER_REVOLUTION,
597 }
598 }
599}
600
601impl From<Gearset> for pros_sys::motor_gearset_e_t {
602 fn from(value: Gearset) -> Self {
603 value as _
604 }
605}
606
607impl TryFrom<pros_sys::motor_gearset_e_t> for Gearset {
608 type Error = MotorError;
609
610 fn try_from(value: pros_sys::motor_gearset_e_t) -> Result<Self, MotorError> {
611 bail_on!(PROS_ERR, value);
612
613 Ok(match value {
614 pros_sys::E_MOTOR_GEAR_RED => Self::Red,
615 pros_sys::E_MOTOR_GEAR_GREEN => Self::Green,
616 pros_sys::E_MOTOR_GEAR_BLUE => Self::Blue,
617 _ => unreachable!(),
618 })
619 }
620}
621
622#[cfg(feature = "dangerous_motor_tuning")]
634#[derive(Debug, Clone, Copy, PartialEq)]
635pub struct MotorTuningConstants {
636 pub kf: f64,
638
639 pub kp: f64,
641
642 pub ki: f64,
644
645 pub kd: f64,
647
648 pub filter: f64,
650
651 pub integral_limit: f64,
655
656 pub tolerance: f64,
660
661 pub sample_rate: Duration,
663}
664
665#[cfg(feature = "dangerous_motor_tuning")]
666impl From<MotorTuningConstants> for pros_sys::motor_pid_full_s_t {
667 fn from(value: MotorTuningConstants) -> Self {
668 unsafe {
669 #[allow(deprecated)]
672 pros_sys::motor_convert_pid_full(
673 value.kf,
674 value.kp,
675 value.ki,
676 value.kd,
677 value.filter,
678 value.limit,
679 value.tolerance,
680 value.sample_rate.as_millis() as f64,
681 )
682 }
683 }
684}
685
686#[derive(Debug, Snafu)]
687pub enum MotorError {
689 Busy,
691
692 NotImplemented,
695
696 #[snafu(display("{source}"), context(false))]
698 Port {
699 source: PortError,
701 },
702}
703
704map_errno! {
705 MotorError {
706 ENOSYS => Self::NotImplemented,
707 }
708 inherit PortError;
709}