From c706b049081938d939ce8ba06a8da3b5b70441ff Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 18 Jan 2024 19:57:06 -0600 Subject: [PATCH 01/33] refactor: Motor API --- packages/pros/src/devices/smart/motor.rs | 187 ++++++++++++----------- 1 file changed, 94 insertions(+), 93 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 2ff00579..bc7fb902 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -39,55 +39,44 @@ pub struct Motor { //TODO: Implement good set_velocity and get_velocity functions. //TODO: Measure the number of counts per rotation. Fow now we assume it is 4096 impl Motor { - pub fn new(port: SmartPort, brake_mode: BrakeMode) -> Result { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_set_encoder_units(port.index(), pros_sys::E_MOTOR_ENCODER_DEGREES) - ); - bail_on!( - PROS_ERR, - pros_sys::motor_set_brake_mode(port.index(), brake_mode.into()) - ); - } + pub fn new(port: SmartPort, gearset: Gearset, reversed: bool) -> Result { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_set_encoder_units(port.index(), pros_sys::E_MOTOR_ENCODER_DEGREES) + }); + + let mut motor = Self { port }; - Ok(Self { port }) + motor.set_gearset(gearset)?; + motor.set_reversed(reversed)?; + + Ok(motor) } pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_set_gearing(self.port.index(), gearset as i32) - ); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_set_gearing(self.port.index(), gearset as i32) + }); Ok(()) } pub fn gearset(&self) -> Result { - Ok(unsafe { bail_on!(PROS_ERR, pros_sys::motor_get_gearing(self.port.index())) }.into()) + unsafe { pros_sys::motor_get_gearing(self.port.index()).try_into() } } /// Takes in a f32 from -1 to 1 that is scaled to -12 to 12 volts. /// Useful for driving motors with controllers. pub fn set_output(&mut self, output: f32) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move(self.port.index(), (output * 127.0) as i32) - ); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move(self.port.index(), (output * 127.0) as i32) + }); Ok(()) } /// Takes in and i8 between -127 and 127 which is scaled to -12 to 12 Volts. pub fn set_raw_output(&mut self, raw_output: i8) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move(self.port.index(), raw_output as i32) - ); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move(self.port.index(), raw_output as i32) + }); Ok(()) } @@ -96,12 +85,9 @@ impl Motor { if !(-12.0..=12.0).contains(&voltage) || voltage.is_nan() { return Err(MotorError::VoltageOutOfRange); } - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_voltage(self.port.index(), (voltage * 1000.0) as i32) - ); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_voltage(self.port.index(), (voltage * 1000.0) as i32) + }); Ok(()) } @@ -113,12 +99,9 @@ impl Motor { position: Position, velocity: i32, ) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_absolute(self.port.index(), position.into_degrees(), velocity) - ); - }; + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_absolute(self.port.index(), position.into_degrees(), velocity) + }); Ok(()) } @@ -129,51 +112,41 @@ impl Motor { position: Position, velocity: i32, ) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_relative(self.port.index(), position.into_degrees(), velocity) - ); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_relative(self.port.index(), position.into_degrees(), velocity) + }); + Ok(()) } /// Returns the power drawn by the motor in Watts. pub fn power(&self) -> Result { - unsafe { - Ok(bail_on!( - PROS_ERR_F, - pros_sys::motor_get_power(self.port.index()) - )) - } + Ok(bail_on!(PROS_ERR_F, unsafe { + pros_sys::motor_get_power(self.port.index()) + })) } /// Returns the torque output of the motor in Nm. pub fn torque(&self) -> Result { - unsafe { - Ok(bail_on!( - PROS_ERR_F, - pros_sys::motor_get_torque(self.port.index()) - )) - } + Ok(bail_on!(PROS_ERR_F, unsafe { + pros_sys::motor_get_torque(self.port.index()) + })) } /// Returns the voltage the motor is drawing in volts. pub fn voltage(&self) -> Result { // docs say this function returns PROS_ERR_F but it actually returns PROS_ERR - let millivolts = - unsafe { bail_on!(PROS_ERR, pros_sys::motor_get_voltage(self.port.index())) }; + let millivolts = bail_on!(PROS_ERR, unsafe { + pros_sys::motor_get_voltage(self.port.index()) + }); Ok(millivolts as f64 / 1000.0) } /// Returns the current position of the motor. pub fn position(&self) -> Result { - unsafe { - Ok(Position::from_degrees(bail_on!( - PROS_ERR_F, - pros_sys::motor_get_position(self.port.index()) - ))) - } + Ok(Position::from_degrees(bail_on!(PROS_ERR_F, unsafe { + pros_sys::motor_get_position(self.port.index()) + }))) } /// Returns the current draw of the motor. @@ -186,9 +159,9 @@ impl Motor { /// Sets the current encoder position to zero without moving the motor. /// Analogous to taring or resetting the encoder to the current position. pub fn zero(&mut self) -> Result<(), MotorError> { - unsafe { - bail_on!(PROS_ERR, pros_sys::motor_tare_position(self.port.index())); - } + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_tare_position(self.port.index()) + }); Ok(()) } @@ -217,9 +190,13 @@ impl Motor { Ok(()) } - //TODO: Test this, as im not entirely sure of the actual implementation + pub fn brake_mode(&self) -> Result { + unsafe { pros_sys::motor_get_brake_mode(self.port.index()).try_into() } + } + + // TODO: Test this, as im not entirely sure of the actual implementation /// Get the current state of the motor. - pub fn get_state(&self) -> Result { + pub fn state(&self) -> Result { let bit_flags = bail_on!(PROS_ERR as _, unsafe { pros_sys::motor_get_flags(self.port.index()) }); @@ -235,8 +212,10 @@ impl Motor { } /// Check if this motor has been reversed. - pub fn reversed(&self) -> bool { - unsafe { pros_sys::motor_is_reversed(self.port.index()) == 1 } + pub fn reversed(&self) -> Result { + Ok(bail_on!(PROS_ERR, unsafe { + pros_sys::motor_is_reversed(self.port.index()) + }) == 1) } /// Returns a future that completes when the motor reports that it has stopped. @@ -257,22 +236,34 @@ impl SmartDevice for Motor { /// Determines how a motor should act when braking. #[derive(Debug, Clone, Copy)] +#[repr(i32)] pub enum BrakeMode { /// Motor never brakes. - None, + None = pros_sys::E_MOTOR_BRAKE_COAST, /// Motor uses regenerative braking to slow down faster. - Brake, + Brake = pros_sys::E_MOTOR_BRAKE_BRAKE, /// Motor exerts force to hold the same position. - Hold, + Hold = pros_sys::E_MOTOR_BRAKE_HOLD, +} + +impl TryFrom for BrakeMode { + type Error = MotorError; + + fn try_from(value: pros_sys::motor_brake_mode_e_t) -> Result { + bail_on!(PROS_ERR, value); + + Ok(match value { + pros_sys::E_MOTOR_BRAKE_COAST => Self::None, + pros_sys::E_MOTOR_BRAKE_BRAKE => Self::Brake, + pros_sys::E_MOTOR_BRAKE_HOLD => Self::Hold, + _ => unreachable!(), + }) + } } impl From for pros_sys::motor_brake_mode_e_t { - fn from(other: BrakeMode) -> pros_sys::motor_brake_mode_e_t { - match other { - BrakeMode::Brake => pros_sys::E_MOTOR_BRAKE_BRAKE, - BrakeMode::Hold => pros_sys::E_MOTOR_BRAKE_HOLD, - BrakeMode::None => pros_sys::E_MOTOR_BRAKE_COAST, - } + fn from(value: BrakeMode) -> pros_sys::motor_brake_mode_e_t { + value as _ } } @@ -321,14 +312,24 @@ impl Gearset { pub const RPM_600: Gearset = Gearset::Blue; } -impl From for Gearset { - fn from(value: i32) -> Self { - match value { - pros_sys::E_MOTOR_GEAR_RED => Gearset::Red, - pros_sys::E_MOTOR_GEAR_GREEN => Gearset::Green, - pros_sys::E_MOTOR_GEAR_BLUE => Gearset::Blue, +impl From for pros_sys::motor_gearset_e_t { + fn from(value: Gearset) -> Self { + value as _ + } +} + +impl TryFrom for Gearset { + type Error = MotorError; + + fn try_from(value: pros_sys::motor_gearset_e_t) -> Result { + bail_on!(PROS_ERR, value); + + Ok(match value { + pros_sys::E_MOTOR_GEAR_RED => Self::Red, + pros_sys::E_MOTOR_GEAR_GREEN => Self::Green, + pros_sys::E_MOTOR_GEAR_BLUE => Self::Blue, _ => unreachable!(), - } + }) } } @@ -342,7 +343,7 @@ impl<'a> core::future::Future for MotorStoppedFuture<'a> { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - match self.motor.get_state()?.stopped { + match self.motor.state()?.stopped { true => core::task::Poll::Ready(Ok(())), false => { cx.waker().wake_by_ref(); From 87bfbeabf14f0dcfd86b53eb06ee38db85827c4a Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:11:11 -0600 Subject: [PATCH 02/33] docs: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f263e691..3fa65298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ Before releasing: - Macros for printing to stdout (`println`, `print`, `eprintln`, etc...) (#30) - All ADI device bindings (#55) - `LocalKey` now has `Cell`/`RefCell`-specific methods for setting and taking values. (#42) +- Implements TryFrom for Gearset. +- Adds support for getting brake modes from motors. (#66) ### Fixed @@ -48,6 +50,9 @@ Before releasing: - Overhauled the `competition` module with more straightforward getters for competition state. (#38) (**Breaking Change**) - LLEMU-related macros have been prefixed with `llemu_` (e.g. `llemu_println`). (**Breaking Change**) (#30) - Added `Debug`, `Copy`, and `Clone` derives for common structs (#37) +- Adjusts constructor arguments for `Motor` to allow passing `Gearset` and `reversed` instead of `brake_mode` at construction. (**Breaking Change**) (#66) +- Renamed `Motor::get_state` to `Motor::state`. (**Breaking Change**) (#66) +- Changed `Motor::reversed` to return `Result`` rather than just `false` if `PROS_ERR` is returned. (**Breaking Change**) (#66) ### Removed From 2d605282895f4d6190f8cd5fbf118fd7d6ad8c41 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:27:08 -0600 Subject: [PATCH 03/33] docs: fix examples --- packages/pros/examples/accessories.rs | 2 +- packages/pros/examples/dynamic_peripherals.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/pros/examples/accessories.rs b/packages/pros/examples/accessories.rs index 7056f4c0..16a5ed7f 100644 --- a/packages/pros/examples/accessories.rs +++ b/packages/pros/examples/accessories.rs @@ -24,7 +24,7 @@ impl ExampleRobot { pub fn new(peripherals: Peripherals) -> Self { Self { motor: Arc::new(Mutex::new( - Motor::new(peripherals.port_2, BrakeMode::Brake).unwrap(), + Motor::new(peripherals.port_2, Gearset::Green, false).unwrap(), )), vision: VisionSensor::new(peripherals.port_9, VisionZeroPoint::Center).unwrap(), } diff --git a/packages/pros/examples/dynamic_peripherals.rs b/packages/pros/examples/dynamic_peripherals.rs index 205ecea1..570091cc 100644 --- a/packages/pros/examples/dynamic_peripherals.rs +++ b/packages/pros/examples/dynamic_peripherals.rs @@ -17,7 +17,8 @@ impl AsyncRobot for Robot { async fn opcontrol(&mut self) -> pros::Result { let motor = Motor::new( self.peripherals.take_smart_port(10).unwrap(), - BrakeMode::Brake, + Gearset::Green, + false, )?; motor.wait_until_stopped().await?; Ok(()) From 5f2c100f9ca388738a241457284242622023a03b Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:56:47 -0600 Subject: [PATCH 04/33] feat: (hopefully) full coverage of the PROS motor api --- packages/pros/Cargo.toml | 3 + packages/pros/src/devices/smart/motor.rs | 436 ++++++++++++++++++----- 2 files changed, 345 insertions(+), 94 deletions(-) diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index 702142b2..7f99896e 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -32,3 +32,6 @@ waker-fn = "1.1.1" [target.'cfg(target_arch = "wasm32")'.dependencies] dlmalloc = { version = "0.2.4", features = ["global"] } + +[features] +dangerous-motor-tuning = [] diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index adb5d658..12cced98 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -21,7 +21,9 @@ //! } //! ``` -use pros_sys::{PROS_ERR, PROS_ERR_F}; +use core::time::Duration; + +use pros_sys::{motor_fault_e_t, motor_flag_e_t, PROS_ERR, PROS_ERR_F}; use snafu::Snafu; use super::{SmartDevice, SmartDeviceType, SmartPort}; @@ -30,21 +32,54 @@ use crate::{ error::{bail_on, map_errno, PortError}, }; +/// The maximum voltage value that can be sent to a [`Motor`]. +pub const MOTOR_MAX_VOLTAGE: f64 = 12.0; + +/// The rate at which data can be read from a [`Motor`]. +pub const MOTOR_READ_DATA_RATE: Duration = Duration::from_millis(10); + +/// The rate at which data can be written to a [`Motor]. +pub const MOTOR_WRITE_DATA_RATE: Duration = Duration::from_millis(5); + /// The basic motor struct. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Motor { port: SmartPort, + target: MotorTarget, +} + +/// Represents a possible target for a [`Motor`]. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MotorTarget { + /// Motor is braking using a [`BrakeMode`]. + Brake(BrakeMode), + + /// Motor is attempting to hold a velocity using internal PID control. + Velocity(i32), + + /// Motor is outputting a raw voltage. + Voltage(f64), + + /// Motor is attempting to reach a relative position. + RelativePosition(Position, i32), + + /// Motor is attempting to reach an absolute position. + AbsolutePosition(Position, i32), } -//TODO: Implement good set_velocity and get_velocity functions. -//TODO: Measure the number of counts per rotation. Fow now we assume it is 4096 +// TODO: Measure the number of counts per rotation. Fow now we assume it is 4096 +// TODO: Wrap motor_modify_profiled_velocity impl Motor { + /// Create a new motor from a smart port index. pub fn new(port: SmartPort, gearset: Gearset, reversed: bool) -> Result { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_encoder_units(port.index(), pros_sys::E_MOTOR_ENCODER_DEGREES) }); - let mut motor = Self { port }; + let mut motor = Self { + port, + target: MotorTarget::Voltage(0.0), + }; motor.set_gearset(gearset)?; motor.set_reversed(reversed)?; @@ -52,75 +87,117 @@ impl Motor { Ok(motor) } - /// Sets the gearset of the motor. - pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorError> { - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_gearing(self.port.index(), gearset as i32) - }); - Ok(()) - } - - /// Gets the gearset of the motor. - pub fn gearset(&self) -> Result { - unsafe { pros_sys::motor_get_gearing(self.port.index()).try_into() } - } + /// Sets the target that the motor should attempt to reach. + /// + /// This could be a voltage, velocity, position, or even brake mode. + pub fn set_target(&mut self, target: MotorTarget) -> Result<(), MotorError> { + match target { + MotorTarget::Brake(mode) => unsafe { + bail_on!( + PROS_ERR, + pros_sys::motor_set_brake_mode(self.port.index(), mode.into()) + ); + bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index())); + }, + MotorTarget::Velocity(rpm) => { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_velocity(self.port.index(), rpm) + }); + }, + MotorTarget::Voltage(volts) => { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_voltage(self.port.index(), (volts * 1000.0) as i32) + }); + }, + MotorTarget::AbsolutePosition(position, velocity) => { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_absolute( + self.port.index(), + position.into_degrees(), + velocity, + ) + }); + }, + MotorTarget::RelativePosition(position, velocity) => { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_move_relative( + self.port.index(), + position.into_degrees(), + velocity, + ) + }); + }, + } - /// Takes in a f32 from -1 to 1 that is scaled to -12 to 12 volts. - /// Useful for driving motors with controllers. - pub fn set_output(&mut self, output: f32) -> Result<(), MotorError> { - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move(self.port.index(), (output * 127.0) as i32) - }); + self.target = target; Ok(()) } - /// Takes in and i8 between -127 and 127 which is scaled to -12 to 12 Volts. - pub fn set_raw_output(&mut self, raw_output: i8) -> Result<(), MotorError> { - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move(self.port.index(), raw_output as i32) - }); - Ok(()) + /// Sets the motors target to a given [`BrakeMode`]. + pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorError> { + self.set_target(MotorTarget::Brake(mode)) } - /// Takes in a voltage that must be between -12 and 12 Volts. - pub fn set_voltage(&mut self, voltage: f32) -> Result<(), MotorError> { - if !(-12.0..=12.0).contains(&voltage) || voltage.is_nan() { - return Err(MotorError::VoltageOutOfRange); - } - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_voltage(self.port.index(), (voltage * 1000.0) as i32) - }); + /// Spins the motor at a target velocity. + /// + /// This velocity corresponds to different actual speeds in RPM depending on the gearset used for the motor. + /// Velocity is held with an internal PID controller to ensure consistent speed, as opposed to setting the + /// motor's voltage. + pub fn set_velocity(&mut self, rpm: i32) -> Result<(), MotorError> { + self.set_target(MotorTarget::Velocity(rpm)) + } - Ok(()) + /// Sets the motor's ouput voltage. + /// + /// This voltage value spans from -12 (fully spinning reverse) to +12 (fully spinning forwards) volts, and + /// controls the raw output of the motor. + pub fn set_voltage(&mut self, volts: f64) -> Result<(), MotorError> { + self.set_target(MotorTarget::Voltage(volts)) } - /// Moves the motor to an absolute position, based off of when the motor was zeroed - /// units for the velocity is RPM. - pub fn set_position_absolute( + /// Sets an absolute position target for the motor to attempt to reach. + pub fn set_absolute_target( &mut self, position: Position, velocity: i32, ) -> Result<(), MotorError> { - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_absolute(self.port.index(), position.into_degrees(), velocity) - }); - Ok(()) + self.set_target(MotorTarget::AbsolutePosition(position, velocity)) } - /// Moves the motor to a position relative to the current position. - /// units for velocity is RPM. - pub fn set_position_relative( + /// Sets an position target relative to the current position for the motor to attempt to reach. + pub fn set_relative_target( &mut self, position: Position, velocity: i32, ) -> Result<(), MotorError> { + self.set_target(MotorTarget::RelativePosition(position, velocity)) + } + + /// Get the current [`MotorTarget`] value that the motor is attempting to reach. + pub fn target(&self) -> MotorTarget { + self.target + } + + /// Sets the gearset of the motor. + pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_relative(self.port.index(), position.into_degrees(), velocity) + pros_sys::motor_set_gearing(self.port.index(), gearset as i32) }); - Ok(()) } + /// Gets the gearset of the motor. + pub fn gearset(&self) -> Result { + unsafe { pros_sys::motor_get_gearing(self.port.index()).try_into() } + } + + /// Gets the estimated angular velocity (RPM) of the motor. + pub fn velocity(&self) -> Result { + Ok(bail_on!(PROS_ERR_F, unsafe { + pros_sys::motor_get_actual_velocity(self.port.index()) + })) + } + /// Returns the power drawn by the motor in Watts. pub fn power(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { @@ -151,10 +228,28 @@ impl Motor { }))) } - /// Returns the current draw of the motor. - pub fn current_draw(&self) -> Result { + /// Returns the raw position data recorded by the motor at a given timestamp. + pub fn raw_position(&self, timestamp: Duration) -> Result { + Ok(bail_on!(PROS_ERR, unsafe { + pros_sys::motor_get_raw_position(self.port.index(), timestamp.as_millis() as *const u32) + })) + } + + /// Returns the electrical current draw of the motor in amps. + pub fn current(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_current_draw(self.port.index()) + }) as f64 / 1000.0) + } + + /// Gets the efficiency of the motor in percent. + /// + /// An efficiency of 100% means that the motor is moving electrically while + /// drawing no electrical power, and an efficiency of 0% means that the motor + /// is drawing power but not moving. + pub fn efficiency(&self) -> Result { + Ok(bail_on!(PROS_ERR_F, unsafe { + pros_sys::motor_get_efficiency(self.port.index()) })) } @@ -167,45 +262,87 @@ impl Motor { Ok(()) } - /// Stops the motor based on the current [`BrakeMode`] - pub fn brake(&mut self) -> Result<(), MotorError> { + /// Sets the current encoder position to the given position without moving the motor. + /// Analogous to taring or resetting the encoder so that the new position is equal to the given position. + pub fn set_position(&mut self, position: Position) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_brake(self.port.index()) + pros_sys::motor_set_zero_position(self.port.index(), position.into_degrees()) }); Ok(()) } - /// Sets the current encoder position to the given position without moving the motor. - /// Analogous to taring or resetting the encoder so that the new position is equal to the given position. - pub fn set_zero_position(&mut self, position: Position) -> Result<(), MotorError> { + pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_zero_position(self.port.index(), position.into_degrees()) + pros_sys::motor_set_current_limit(self.port.index(), (limit * 1000.0) as i32) }); Ok(()) } - /// Sets how the motor should act when stopping. - pub fn set_brake_mode(&mut self, brake_mode: BrakeMode) -> Result<(), MotorError> { + pub fn set_voltage_limit(&mut self, limit: i32) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_brake_mode(self.port.index(), brake_mode.into()) + pros_sys::motor_set_voltage_limit(self.port.index(), limit) }); Ok(()) } - pub fn brake_mode(&self) -> Result { - unsafe { pros_sys::motor_get_brake_mode(self.port.index()).try_into() } + fn current_limit(&self) -> Result { + Ok(bail_on!(PROS_ERR, unsafe { + pros_sys::motor_get_current_limit(self.port.index()) + }) as f64 / 1000.0) } - // TODO: Test this, as im not entirely sure of the actual implementation - /// Get the current state of the motor. - pub fn state(&self) -> Result { - let bit_flags = bail_on!(PROS_ERR as _, unsafe { - pros_sys::motor_get_flags(self.port.index()) + fn voltage_limit(&self) -> Result, MotorError> { + let raw_limit = bail_on!(PROS_ERR, unsafe { + pros_sys::motor_get_voltage_limit(self.port.index()) }); - Ok(bit_flags.into()) + + Ok(match raw_limit { + 0 => None, + limited => Some(limited) + }) } - /// Reverse this motor by multiplying all input by -1. + /// Get the status flagss of a motor. + pub fn status(&self) -> Result { + unsafe { pros_sys::motor_get_flags(self.port.index()).try_into() } + } + + /// Check if the motor's stopped flag is set. + pub fn is_stopped(&self) -> Result { + Ok(self.status()?.is_stopped()) + } + + /// Check if the motor's zeroed flag is set. + pub fn is_zeroed(&self) -> Result { + Ok(self.status()?.is_zeroed()) + } + + /// Get the faults flags of the motor. + pub fn faults(&self) -> Result { + unsafe { pros_sys::motor_get_faults(self.port.index()).try_into() } + } + + /// Check if the motor's over temperature flag is set. + pub fn is_over_temperature(&self) -> Result { + Ok(self.faults()?.is_over_temperature()) + } + + /// Check if the motor's overcurrent flag is set. + pub fn is_over_current(&self) -> Result { + Ok(self.faults()?.is_over_current()) + } + + /// Check if a H-bridge (motor driver) fault has occurred. + pub fn is_driver_fault(&self) -> Result { + Ok(self.faults()?.is_driver_fault()) + } + + /// Check if the motor's H-bridge has an overucrrent fault. + pub fn is_driver_over_current(&self) -> Result { + Ok(self.faults()?.is_driver_over_current()) + } + + /// Reverse this motor's output values. pub fn set_reversed(&mut self, reversed: bool) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_reversed(self.port.index(), reversed) @@ -214,7 +351,7 @@ impl Motor { } /// Check if this motor has been reversed. - pub fn reversed(&self) -> Result { + pub fn is_reversed(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_is_reversed(self.port.index()) }) == 1) @@ -224,6 +361,30 @@ impl Motor { pub const fn wait_until_stopped(&self) -> MotorStoppedFuture<'_> { MotorStoppedFuture { motor: self } } + + #[cfg(feature = "dangerous_motor_tuning")] + pub fn set_velocity_tuning_constants( + &mut self, + constants: MotorTuningConstants, + ) -> Result<(), MotorError> { + bail_on!(PROS_ERR, unsafe { + #[allow(deprecated)] + pros_sys::motor_set_pos_pid_full(self.port.index(), constants.into()) + }); + Ok(()) + } + + #[cfg(feature = "dangerous_motor_tuning")] + pub fn set_position_tuning_constants( + &mut self, + constants: MotorTuningConstants, + ) -> Result<(), MotorError> { + bail_on!(PROS_ERR, unsafe { + #[allow(deprecated)] + pros_sys::motor_set_vel_pid_full(self.port.index(), constants.into()) + }); + Ok(()) + } } impl SmartDevice for Motor { @@ -237,7 +398,7 @@ impl SmartDevice for Motor { } /// Determines how a motor should act when braking. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(i32)] pub enum BrakeMode { /// Motor never brakes. @@ -269,24 +430,66 @@ impl From for pros_sys::motor_brake_mode_e_t { } } -/// Represents what the physical motor is currently doing. -#[derive(Debug, Clone, Default)] -pub struct MotorState { - /// The motor is currently moving. - pub busy: bool, - /// the motor is not moving. - pub stopped: bool, - /// the motor is at zero encoder units of rotation. - pub zeroed: bool, +/// The fault flags returned by a [`Motor`]. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] +pub struct MotorFaults(pub u32); + +impl MotorFaults { + /// Checks if the motor's temperature is above its limit. + pub fn is_over_temperature(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FAULT_MOTOR_OVER_TEMP != 0 + } + + /// Check if the motor's H-bridge has encountered a fault. + pub fn is_driver_fault(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FAULT_DRIVER_FAULT != 0 + } + + /// Check if the motor is over current. + pub fn is_over_current(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FAULT_OVER_CURRENT != 0 + } + + /// Check if the motor's H-bridge is over current. + pub fn is_driver_over_current(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FAULT_DRV_OVER_CURRENT != 0 + } } -//TODO: Test this, like mentioned above -impl From for MotorState { - fn from(value: u32) -> Self { - Self { - busy: (value & pros_sys::E_MOTOR_FLAGS_BUSY) == 0b001, - stopped: (value & pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY) == 0b010, - zeroed: (value & pros_sys::E_MOTOR_FLAGS_ZERO_POSITION) == 0b100, +impl TryFrom for MotorFaults { + type Error = MotorError; + + fn try_from(value: motor_fault_e_t) -> Result { + Ok(Self(bail_on!(PROS_ERR as _, value))) + } +} + +/// The status flags returned by a [`Motor`]. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] +pub struct MotorStatus(pub u32); + +impl MotorStatus { + /// Check if the motor is currently stopped. + pub fn is_stopped(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY != 0 + } + + /// Check if the motor is at its zero position. + pub fn is_zeroed(&self) -> bool { + self.0 & pros_sys::E_MOTOR_FLAGS_ZERO_POSITION != 0 + } +} + +impl TryFrom for MotorStatus { + type Error = MotorError; + + fn try_from(value: motor_flag_e_t) -> Result { + let flags = bail_on!(PROS_ERR as _, value); + + if flags & pros_sys::E_MOTOR_FLAGS_BUSY == 0 { + Ok(Self(flags)) + } else { + Err(MotorError::Busy) } } } @@ -340,9 +543,54 @@ impl TryFrom for Gearset { } } -#[derive(Debug)] +/// Holds the information about a Motor's position or velocity PID controls. +/// +/// These values are in 4.4 format, meaning that a value of 0x20 represents 2.0, +/// 0x21 represents 2.0625, 0x22 represents 2.125, etc. +#[cfg(feature = "dangerous_motor_tuning")] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MotorTuningConstants { + /// The feedforward constant. + pub kf: f64, + + /// The proportional constant. + pub kp: f64, + + /// The integral constant. + pub ki: f64, + + /// The derivative constant. + pub kd: f64, + + /// A constant used for filtering the profile acceleration. + pub filter: f64, + + /// The integral limit. + pub limit: f64, + + /// The threshold for determining if a position movement has + /// reached its goal. This has no effect for velocity PID calculations. + pub threshold: f64, + + /// The rate at whsich the PID computation is run in ms. + pub loopspeed: Duration, +} + +#[cfg(feature = "dangerous_motor_tuning")] +impl From for pros_sys::motor_pid_full_s_t { + fn from(value: MotorTuningConstants) -> Self { + unsafe { + // Docs incorrectly claim that this function can set errno. + // It can't. . + #[allow(deprecated)] + pros_sys::motor_convert_pid_full(value.kf, value.kp, value.ki, value.kd, value.filter, value.limit, value.threshold, value.loopspeed.as_millis() as f64) + } + } +} + /// A future that completes when the motor reports that it has stopped. /// Created by [`Motor::wait_until_stopped`] +#[derive(Debug)] pub struct MotorStoppedFuture<'a> { motor: &'a Motor, } @@ -353,7 +601,7 @@ impl<'a> core::future::Future for MotorStoppedFuture<'a> { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - match self.motor.state()?.stopped { + match self.motor.status()?.is_stopped() { true => core::task::Poll::Ready(Ok(())), false => { cx.waker().wake_by_ref(); @@ -366,10 +614,10 @@ impl<'a> core::future::Future for MotorStoppedFuture<'a> { #[derive(Debug, Snafu)] /// Errors that can occur when using a motor. pub enum MotorError { - /// The voltage supplied was outside of the allowed range of [-12, 12]. - VoltageOutOfRange, - #[snafu(display("{source}"), context(false))] + /// Failed to communicate with the motor while attempting to read flags. + Busy, /// Generic port related error. + #[snafu(display("{source}"), context(false))] Port { /// The source of the error. source: PortError, From 97f1f12ca721d97de31b2ad4e659deb480cf57e8 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:49:25 -0600 Subject: [PATCH 05/33] chore: fmt --- packages/pros/src/devices/smart/motor.rs | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 12cced98..9c8a3741 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -103,12 +103,12 @@ impl Motor { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_velocity(self.port.index(), rpm) }); - }, + } MotorTarget::Voltage(volts) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_voltage(self.port.index(), (volts * 1000.0) as i32) }); - }, + } MotorTarget::AbsolutePosition(position, velocity) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_absolute( @@ -117,7 +117,7 @@ impl Motor { velocity, ) }); - }, + } MotorTarget::RelativePosition(position, velocity) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_relative( @@ -126,7 +126,7 @@ impl Motor { velocity, ) }); - }, + } } self.target = target; @@ -239,7 +239,8 @@ impl Motor { pub fn current(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_current_draw(self.port.index()) - }) as f64 / 1000.0) + }) as f64 + / 1000.0) } /// Gets the efficiency of the motor in percent. @@ -288,7 +289,8 @@ impl Motor { fn current_limit(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_current_limit(self.port.index()) - }) as f64 / 1000.0) + }) as f64 + / 1000.0) } fn voltage_limit(&self) -> Result, MotorError> { @@ -298,7 +300,7 @@ impl Motor { Ok(match raw_limit { 0 => None, - limited => Some(limited) + limited => Some(limited), }) } @@ -362,6 +364,17 @@ impl Motor { MotorStoppedFuture { motor: self } } + /// Adjusts the internal tuning constants of the motor when using velocity control. + /// + /// # Hardware Safety + /// + /// Retuning internal motor control is **dangerous**, and can result in permanent hardware damage + /// to smart motors if done incorrectly. Use these functions entirely at your own risk. + /// + /// VEX has chosen not to disclose the default constants used by smart motors, and currently + /// has no plans to do so. As such, the units and finer details of [`MotorTuningConstants`] are not + /// well-known or understood, as we have no reference for what these constants should look + /// like. #[cfg(feature = "dangerous_motor_tuning")] pub fn set_velocity_tuning_constants( &mut self, @@ -374,6 +387,17 @@ impl Motor { Ok(()) } + /// Adjusts the internal tuning constants of the motor when using position control. + /// + /// # Hardware Safety + /// + /// Retuning internal motor control is **dangerous**, and can result in permanent hardware damage + /// to smart motors if done incorrectly. Use these functions entirely at your own risk. + /// + /// VEX has chosen not to disclose the default constants used by smart motors, and currently + /// has no plans to do so. As such, the units and finer details of [`MotorTuningConstants`] are not + /// well-known or understood, as we have no reference for what these constants should look + /// like. #[cfg(feature = "dangerous_motor_tuning")] pub fn set_position_tuning_constants( &mut self, @@ -583,7 +607,16 @@ impl From for pros_sys::motor_pid_full_s_t { // Docs incorrectly claim that this function can set errno. // It can't. . #[allow(deprecated)] - pros_sys::motor_convert_pid_full(value.kf, value.kp, value.ki, value.kd, value.filter, value.limit, value.threshold, value.loopspeed.as_millis() as f64) + pros_sys::motor_convert_pid_full( + value.kf, + value.kp, + value.ki, + value.kd, + value.filter, + value.limit, + value.threshold, + value.loopspeed.as_millis() as f64, + ) } } } From c45a0471e886216aea2b90372fec22b3bc190e6b Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:49:58 -0600 Subject: [PATCH 06/33] docs: reword warning --- packages/pros/src/devices/smart/motor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 9c8a3741..9cf00879 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -368,7 +368,7 @@ impl Motor { /// /// # Hardware Safety /// - /// Retuning internal motor control is **dangerous**, and can result in permanent hardware damage + /// Modifying internal motor control is **dangerous**, and can result in permanent hardware damage /// to smart motors if done incorrectly. Use these functions entirely at your own risk. /// /// VEX has chosen not to disclose the default constants used by smart motors, and currently @@ -391,7 +391,7 @@ impl Motor { /// /// # Hardware Safety /// - /// Retuning internal motor control is **dangerous**, and can result in permanent hardware damage + /// Modifying internal motor control is **dangerous**, and can result in permanent hardware damage /// to smart motors if done incorrectly. Use these functions entirely at your own risk. /// /// VEX has chosen not to disclose the default constants used by smart motors, and currently From 12ec99d72cabfa02d2c6da94db379b1cfd1eb4a5 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:57:49 -0600 Subject: [PATCH 07/33] docs: add comments for missing functions --- packages/pros/src/devices/smart/motor.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 9cf00879..4a102997 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -272,6 +272,7 @@ impl Motor { Ok(()) } + /// Sets the current limit for the motor in amps. pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_current_limit(self.port.index(), (limit * 1000.0) as i32) @@ -279,6 +280,7 @@ impl Motor { Ok(()) } + /// Sets the voltage limit for the motor in volts. pub fn set_voltage_limit(&mut self, limit: i32) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_voltage_limit(self.port.index(), limit) @@ -286,20 +288,24 @@ impl Motor { Ok(()) } - fn current_limit(&self) -> Result { + /// Gets the current limit for the motor in mA. + /// + /// The default value is 2.5A. + pub fn current_limit(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_current_limit(self.port.index()) }) as f64 / 1000.0) } - fn voltage_limit(&self) -> Result, MotorError> { + /// Gets the voltage limit for the motor if one has been explicitly set. + pub fn voltage_limit(&self) -> Result, MotorError> { let raw_limit = bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_voltage_limit(self.port.index()) }); Ok(match raw_limit { - 0 => None, + 0 => None, // SDK uses a voltage limit of zero to indicate that there is no limit present limited => Some(limited), }) } From 42caf6132e98bf15bc510eeea141facb5853b3c1 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 22:05:27 -0600 Subject: [PATCH 08/33] feat: set_profiled_velocity --- packages/pros/src/devices/smart/motor.rs | 39 ++++++++++++++++++------ 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 4a102997..33fff0f6 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -32,15 +32,6 @@ use crate::{ error::{bail_on, map_errno, PortError}, }; -/// The maximum voltage value that can be sent to a [`Motor`]. -pub const MOTOR_MAX_VOLTAGE: f64 = 12.0; - -/// The rate at which data can be read from a [`Motor`]. -pub const MOTOR_READ_DATA_RATE: Duration = Duration::from_millis(10); - -/// The rate at which data can be written to a [`Motor]. -pub const MOTOR_WRITE_DATA_RATE: Duration = Duration::from_millis(5); - /// The basic motor struct. #[derive(Debug, PartialEq)] pub struct Motor { @@ -68,8 +59,16 @@ pub enum MotorTarget { } // TODO: Measure the number of counts per rotation. Fow now we assume it is 4096 -// TODO: Wrap motor_modify_profiled_velocity impl Motor { + /// The maximum voltage value that can be sent to a [`Motor`]. + pub const MAX_VOLTAGE: f64 = 12.0; + + /// The rate at which data can be read from a [`Motor`]. + pub const DATA_READ_RATE: Duration = Duration::from_millis(10); + + /// The rate at which data can be written to a [`Motor`]. + pub const DATA_WRITE_RATE: Duration = Duration::from_millis(5); + /// Create a new motor from a smart port index. pub fn new(port: SmartPort, gearset: Gearset, reversed: bool) -> Result { bail_on!(PROS_ERR, unsafe { @@ -173,6 +172,26 @@ impl Motor { self.set_target(MotorTarget::RelativePosition(position, velocity)) } + /// Changes the output velocity for a profiled movement (motor_move_absolute or motor_move_relative). + /// This will have no effect if the motor is not following a profiled movement. + pub fn set_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_modify_profiled_velocity(self.port.index(), velocity) + }); + + match self.target { + MotorTarget::AbsolutePosition(position, _) => { + self.target = MotorTarget::AbsolutePosition(position, velocity) + }, + MotorTarget::RelativePosition(position, _) => { + self.target = MotorTarget::RelativePosition(position, velocity) + } + _ => {} + } + + Ok(()) + } + /// Get the current [`MotorTarget`] value that the motor is attempting to reach. pub fn target(&self) -> MotorTarget { self.target From 1625a5894bdc25f95054556468779c5583959baf Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 29 Feb 2024 22:10:12 -0600 Subject: [PATCH 09/33] refactor/docs: clean up MotorTuningConstants --- packages/pros/src/devices/smart/motor.rs | 30 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 33fff0f6..6234f6db 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -594,8 +594,15 @@ impl TryFrom for Gearset { /// Holds the information about a Motor's position or velocity PID controls. /// -/// These values are in 4.4 format, meaning that a value of 0x20 represents 2.0, -/// 0x21 represents 2.0625, 0x22 represents 2.125, etc. +/// # Hardware Safety +/// +/// Modifying internal motor control is **dangerous**, and can result in permanent hardware damage +/// to smart motors if done incorrectly. Use these functions entirely at your own risk. +/// +/// VEX has chosen not to disclose the default constants used by smart motors, and currently +/// has no plans to do so. As such, the units and finer details of [`MotorTuningConstants`] are not +/// well-known or understood, as we have no reference for what these constants should look +/// like. #[cfg(feature = "dangerous_motor_tuning")] #[derive(Debug, Clone, Copy, PartialEq)] pub struct MotorTuningConstants { @@ -615,14 +622,17 @@ pub struct MotorTuningConstants { pub filter: f64, /// The integral limit. - pub limit: f64, + /// + /// Presumably used for anti-windup protection. + pub integral_limit: f64, - /// The threshold for determining if a position movement has - /// reached its goal. This has no effect for velocity PID calculations. - pub threshold: f64, + /// The threshold for determining if a position movement has reached its goal. + /// + /// This has no effect for velocity PID calculations. + pub tolerance: f64, - /// The rate at whsich the PID computation is run in ms. - pub loopspeed: Duration, + /// The rate at which the PID computation is run in ms. + pub sample_rate: Duration, } #[cfg(feature = "dangerous_motor_tuning")] @@ -639,8 +649,8 @@ impl From for pros_sys::motor_pid_full_s_t { value.kd, value.filter, value.limit, - value.threshold, - value.loopspeed.as_millis() as f64, + value.tolerance, + value.sample_rate.as_millis() as f64, ) } } From 5f4f1739d7a8e0d9849ca927629e90b2a35338cb Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:56:23 -0600 Subject: [PATCH 10/33] refactor: various subjective API tweaks --- packages/pros/src/devices/smart/motor.rs | 85 ++++++------------------ 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 6234f6db..85677e51 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -1,25 +1,4 @@ -//! Motors and gearsets. -//! -//! The motor API is similar to that of [`sensors`](crate::sensors). -//! Multiple motors can be created on the same port and they are thread safe. -//! -//! Motors can be created with the [`Motor::new`] function. -//! Once created they can be controlled with one three functions: -//! [`Motor::set_output`], [`Motor::set_raw_output`], and [`Motor::set_voltage`]. -//! [`Motor::set_output`] takes in a f32 from -1 to 1 for ease of use with [`Controller`](crate::controller::Controller)s. -//! [`Motor::set_raw_output`] takes in an i8 from -127 to 127. -//! [`Motor::set_voltage`] takes in an i16 from -12000 to 12000. -//! -//! Example of driving a single motor with a controller: -//! ```rust -//! # use pros::prelude::*; -//! let motor = Motor::new(1, BrakeMode::Brake).unwrap(); -//! let controller = Controller::Master; -//! loop { -//! let output = controller.state().joysticks.left.y; -//! motor.set_output(output).ok(); -//! } -//! ``` +//! V5 Smart Motors use core::time::Duration; @@ -51,14 +30,10 @@ pub enum MotorTarget { /// Motor is outputting a raw voltage. Voltage(f64), - /// Motor is attempting to reach a relative position. - RelativePosition(Position, i32), - - /// Motor is attempting to reach an absolute position. - AbsolutePosition(Position, i32), + /// Motor is attempting to reach a position. + Position(Position, i32), } -// TODO: Measure the number of counts per rotation. Fow now we assume it is 4096 impl Motor { /// The maximum voltage value that can be sent to a [`Motor`]. pub const MAX_VOLTAGE: f64 = 12.0; @@ -108,7 +83,7 @@ impl Motor { pros_sys::motor_move_voltage(self.port.index(), (volts * 1000.0) as i32) }); } - MotorTarget::AbsolutePosition(position, velocity) => { + MotorTarget::Position(position, velocity) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_absolute( self.port.index(), @@ -117,15 +92,6 @@ impl Motor { ) }); } - MotorTarget::RelativePosition(position, velocity) => { - bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_relative( - self.port.index(), - position.into_degrees(), - velocity, - ) - }); - } } self.target = target; @@ -155,36 +121,25 @@ impl Motor { } /// Sets an absolute position target for the motor to attempt to reach. - pub fn set_absolute_target( + pub fn rotate_to_position( &mut self, position: Position, velocity: i32, ) -> Result<(), MotorError> { - self.set_target(MotorTarget::AbsolutePosition(position, velocity)) - } - - /// Sets an position target relative to the current position for the motor to attempt to reach. - pub fn set_relative_target( - &mut self, - position: Position, - velocity: i32, - ) -> Result<(), MotorError> { - self.set_target(MotorTarget::RelativePosition(position, velocity)) + self.set_target(MotorTarget::Position(position, velocity)) } /// Changes the output velocity for a profiled movement (motor_move_absolute or motor_move_relative). + /// /// This will have no effect if the motor is not following a profiled movement. - pub fn set_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> { + pub fn update_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { pros_sys::motor_modify_profiled_velocity(self.port.index(), velocity) }); match self.target { - MotorTarget::AbsolutePosition(position, _) => { - self.target = MotorTarget::AbsolutePosition(position, velocity) - }, - MotorTarget::RelativePosition(position, _) => { - self.target = MotorTarget::RelativePosition(position, velocity) + MotorTarget::Position(position, _) => { + self.target = MotorTarget::Position(position, velocity) } _ => {} } @@ -247,7 +202,7 @@ impl Motor { }))) } - /// Returns the raw position data recorded by the motor at a given timestamp. + /// Returns the raw position tick data recorded by the motor at a given timestamp. pub fn raw_position(&self, timestamp: Duration) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_raw_position(self.port.index(), timestamp.as_millis() as *const u32) @@ -300,16 +255,18 @@ impl Motor { } /// Sets the voltage limit for the motor in volts. - pub fn set_voltage_limit(&mut self, limit: i32) -> Result<(), MotorError> { + pub fn set_voltage_limit(&mut self, limit: f64) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_voltage_limit(self.port.index(), limit) + // TODO: Docs claim that this function takes volts, but I + // seriously don't buy it. We unfortunately can't tell if + // this is true or not just from source code, since this + // function just wraps vexDeviceMotorVoltageLimitSet. + pros_sys::motor_set_voltage_limit(self.port.index(), (limit * 1000.0) as i32) }); Ok(()) } - /// Gets the current limit for the motor in mA. - /// - /// The default value is 2.5A. + /// Gets the current limit for the motor in amps. pub fn current_limit(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_current_limit(self.port.index()) @@ -318,14 +275,14 @@ impl Motor { } /// Gets the voltage limit for the motor if one has been explicitly set. - pub fn voltage_limit(&self) -> Result, MotorError> { + pub fn voltage_limit(&self) -> Result, MotorError> { let raw_limit = bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_voltage_limit(self.port.index()) }); Ok(match raw_limit { 0 => None, // SDK uses a voltage limit of zero to indicate that there is no limit present - limited => Some(limited), + limit => Some(limit as f64 / 1000.0), }) } @@ -369,7 +326,7 @@ impl Motor { Ok(self.faults()?.is_driver_over_current()) } - /// Reverse this motor's output values. + /// Set whether or not this motor's ouput should be reversed. pub fn set_reversed(&mut self, reversed: bool) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_reversed(self.port.index(), reversed) From d522bca0d7a49bd2f3f3b6bb523f21a913ac2c47 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 1 Mar 2024 22:05:53 -0600 Subject: [PATCH 11/33] docs: document `voltage_limit` unit uncertainty --- packages/pros/src/devices/smart/motor.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/pros/src/devices/smart/motor.rs b/packages/pros/src/devices/smart/motor.rs index 85677e51..8c58d7a7 100644 --- a/packages/pros/src/devices/smart/motor.rs +++ b/packages/pros/src/devices/smart/motor.rs @@ -281,7 +281,13 @@ impl Motor { }); Ok(match raw_limit { - 0 => None, // SDK uses a voltage limit of zero to indicate that there is no limit present + // SDK uses a voltage limit of zero to indicate that there is no limit present + 0 => None, + + // TODO: Docs claim that this function returns volts, but I + // seriously don't buy it. We unfortunately can't tell if + // this is true or not just from source code, since this + // function just wraps vexDeviceMotorVoltageLimitGet. limit => Some(limit as f64 / 1000.0), }) } From 68a740403007d44baec9f1c7c4d57d562cc39da2 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Sat, 2 Mar 2024 10:02:44 -0600 Subject: [PATCH 12/33] fix: `dangerous_motor_tuning` feature flag --- packages/pros-devices/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/pros-devices/Cargo.toml b/packages/pros-devices/Cargo.toml index 17a62012..477567c5 100644 --- a/packages/pros-devices/Cargo.toml +++ b/packages/pros-devices/Cargo.toml @@ -16,3 +16,6 @@ no_std_io = { version = "0.6.0", features = ["alloc"] } [lints] workspace = true + +[features] +dangerous_motor_tuning = [] From 42d16145d4c80ff82b6253e1c0cac1fe4c9a5edf Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 6 Mar 2024 00:01:08 -0600 Subject: [PATCH 13/33] refactor: rename `MotorTarget` to `MotorControl` --- packages/pros-devices/src/smart/motor.rs | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 6837f59f..825ad4ce 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -13,12 +13,12 @@ use crate::Position; #[derive(Debug, PartialEq)] pub struct Motor { port: SmartPort, - target: MotorTarget, + target: MotorControl, } /// Represents a possible target for a [`Motor`]. #[derive(Clone, Copy, Debug, PartialEq)] -pub enum MotorTarget { +pub enum MotorControl { /// Motor is braking using a [`BrakeMode`]. Brake(BrakeMode), @@ -50,7 +50,7 @@ impl Motor { let mut motor = Self { port, - target: MotorTarget::Voltage(0.0), + target: MotorControl::Voltage(0.0), }; motor.set_gearset(gearset)?; @@ -62,26 +62,26 @@ impl Motor { /// Sets the target that the motor should attempt to reach. /// /// This could be a voltage, velocity, position, or even brake mode. - pub fn set_target(&mut self, target: MotorTarget) -> Result<(), MotorError> { + pub fn set_target(&mut self, target: MotorControl) -> Result<(), MotorError> { match target { - MotorTarget::Brake(mode) => unsafe { + MotorControl::Brake(mode) => unsafe { bail_on!( PROS_ERR, pros_sys::motor_set_brake_mode(self.port.index(), mode.into()) ); bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index())); }, - MotorTarget::Velocity(rpm) => { + MotorControl::Velocity(rpm) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_velocity(self.port.index(), rpm) }); } - MotorTarget::Voltage(volts) => { + MotorControl::Voltage(volts) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_voltage(self.port.index(), (volts * 1000.0) as i32) }); } - MotorTarget::Position(position, velocity) => { + MotorControl::Position(position, velocity) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_absolute( self.port.index(), @@ -98,7 +98,7 @@ impl Motor { /// Sets the motors target to a given [`BrakeMode`]. pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorError> { - self.set_target(MotorTarget::Brake(mode)) + self.set_target(MotorControl::Brake(mode)) } /// Spins the motor at a target velocity. @@ -107,7 +107,7 @@ impl Motor { /// Velocity is held with an internal PID controller to ensure consistent speed, as opposed to setting the /// motor's voltage. pub fn set_velocity(&mut self, rpm: i32) -> Result<(), MotorError> { - self.set_target(MotorTarget::Velocity(rpm)) + self.set_target(MotorControl::Velocity(rpm)) } /// Sets the motor's ouput voltage. @@ -115,7 +115,7 @@ impl Motor { /// This voltage value spans from -12 (fully spinning reverse) to +12 (fully spinning forwards) volts, and /// controls the raw output of the motor. pub fn set_voltage(&mut self, volts: f64) -> Result<(), MotorError> { - self.set_target(MotorTarget::Voltage(volts)) + self.set_target(MotorControl::Voltage(volts)) } /// Sets an absolute position target for the motor to attempt to reach. @@ -124,7 +124,7 @@ impl Motor { position: Position, velocity: i32, ) -> Result<(), MotorError> { - self.set_target(MotorTarget::Position(position, velocity)) + self.set_target(MotorControl::Position(position, velocity)) } /// Changes the output velocity for a profiled movement (motor_move_absolute or motor_move_relative). @@ -136,8 +136,8 @@ impl Motor { }); match self.target { - MotorTarget::Position(position, _) => { - self.target = MotorTarget::Position(position, velocity) + MotorControl::Position(position, _) => { + self.target = MotorControl::Position(position, velocity) } _ => {} } @@ -145,8 +145,8 @@ impl Motor { Ok(()) } - /// Get the current [`MotorTarget`] value that the motor is attempting to reach. - pub fn target(&self) -> MotorTarget { + /// Get the current [`MotorControl`] value that the motor is attempting to reach. + pub fn target(&self) -> MotorControl { self.target } From c2f96c17872c4e8b8f07c8825363b3327efc8457 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:01:37 -0600 Subject: [PATCH 14/33] refactor: make `voltage_limit` non-optional --- packages/pros-devices/src/smart/motor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 825ad4ce..429e8f80 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -273,20 +273,20 @@ impl Motor { } /// Gets the voltage limit for the motor if one has been explicitly set. - pub fn voltage_limit(&self) -> Result, MotorError> { + pub fn voltage_limit(&self) -> Result { let raw_limit = bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_voltage_limit(self.port.index()) }); Ok(match raw_limit { // SDK uses a voltage limit of zero to indicate that there is no limit present - 0 => None, + 0 => Self::MAX_VOLTAGE, // TODO: Docs claim that this function returns volts, but I // seriously don't buy it. We unfortunately can't tell if // this is true or not just from source code, since this // function just wraps vexDeviceMotorVoltageLimitGet. - limit => Some(limit as f64 / 1000.0), + limit => limit as f64 / 1000.0, }) } From b6924b20654007649b09d7fc41918324eea20e15 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:57:01 -0600 Subject: [PATCH 15/33] refactor: use `bitflags` crate for status structs, deprecate MotorStatus things --- packages/pros-devices/Cargo.toml | 1 + packages/pros-devices/src/smart/motor.rs | 163 +++++++++-------------- packages/pros-sys/src/error.rs | 1 + 3 files changed, 64 insertions(+), 101 deletions(-) diff --git a/packages/pros-devices/Cargo.toml b/packages/pros-devices/Cargo.toml index e9e8f216..52bbf80d 100644 --- a/packages/pros-devices/Cargo.toml +++ b/packages/pros-devices/Cargo.toml @@ -27,6 +27,7 @@ snafu = { version = "0.8.0", default-features = false, features = [ "unstable-core-error", ] } no_std_io = { version = "0.6.0", features = ["alloc"] } +bitflags = "2.4.2" [lints] workspace = true diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 429e8f80..d95ca969 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -2,8 +2,9 @@ use core::time::Duration; +use bitflags::bitflags; use pros_core::{bail_on, error::PortError, map_errno}; -use pros_sys::{motor_fault_e_t, motor_flag_e_t, PROS_ERR, PROS_ERR_F}; +use pros_sys::{PROS_ERR, PROS_ERR_F}; use snafu::Snafu; use super::{SmartDevice, SmartDeviceType, SmartPort}; @@ -290,44 +291,48 @@ impl Motor { }) } - /// Get the status flagss of a motor. + /// Get the status flags of a motor. pub fn status(&self) -> Result { - unsafe { pros_sys::motor_get_flags(self.port.index()).try_into() } - } + let bits = bail_on!(PROS_ERR as u32, unsafe { + pros_sys::motor_get_flags(self.port.index()) + }); - /// Check if the motor's stopped flag is set. - pub fn is_stopped(&self) -> Result { - Ok(self.status()?.is_stopped()) - } + // For some reason, PROS doesn't set errno if this flag is returned, + // even though it is by-definition an error (failing to retrieve flags). + if (bits & pros_sys::E_MOTOR_FLAGS_BUSY) != 0 { + return Err(MotorError::Busy); + } - /// Check if the motor's zeroed flag is set. - pub fn is_zeroed(&self) -> Result { - Ok(self.status()?.is_zeroed()) + Ok(MotorStatus::from_bits_retain(bits)) } - /// Get the faults flags of the motor. + /// Get the fault flags of the motor. pub fn faults(&self) -> Result { - unsafe { pros_sys::motor_get_faults(self.port.index()).try_into() } + let bits = bail_on!(PROS_ERR as u32, unsafe { + pros_sys::motor_get_faults(self.port.index()) + }); + + Ok(MotorFaults::from_bits_retain(bits)) } /// Check if the motor's over temperature flag is set. pub fn is_over_temperature(&self) -> Result { - Ok(self.faults()?.is_over_temperature()) + Ok(self.faults()?.contains(MotorFaults::OVER_TEMPERATURE)) } /// Check if the motor's overcurrent flag is set. pub fn is_over_current(&self) -> Result { - Ok(self.faults()?.is_over_current()) + Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT)) } /// Check if a H-bridge (motor driver) fault has occurred. pub fn is_driver_fault(&self) -> Result { - Ok(self.faults()?.is_driver_fault()) + Ok(self.faults()?.contains(MotorFaults::DRIVER_FAULT)) } /// Check if the motor's H-bridge has an overucrrent fault. pub fn is_driver_over_current(&self) -> Result { - Ok(self.faults()?.is_driver_over_current()) + Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT)) } /// Set whether or not this motor's ouput should be reversed. @@ -345,11 +350,6 @@ impl Motor { }) == 1) } - /// Returns a future that completes when the motor reports that it has stopped. - pub const fn wait_until_stopped(&self) -> MotorStoppedFuture<'_> { - MotorStoppedFuture { motor: self } - } - /// Adjusts the internal tuning constants of the motor when using velocity control. /// /// # Hardware Safety @@ -440,67 +440,44 @@ impl From for pros_sys::motor_brake_mode_e_t { } } -/// The fault flags returned by a [`Motor`]. -#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] -pub struct MotorFaults(pub u32); +bitflags! { + /// The fault flags returned by a [`Motor`]. + #[derive(Debug, Clone, Copy, Eq, PartialEq)] + pub struct MotorFaults: u32 { + /// The motor's temperature is above its limit. + const OVER_TEMPERATURE = pros_sys::E_MOTOR_FAULT_MOTOR_OVER_TEMP; -impl MotorFaults { - /// Checks if the motor's temperature is above its limit. - pub fn is_over_temperature(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FAULT_MOTOR_OVER_TEMP != 0 - } + /// The motor is over current. + const OVER_CURRENT = pros_sys::E_MOTOR_FAULT_OVER_CURRENT; - /// Check if the motor's H-bridge has encountered a fault. - pub fn is_driver_fault(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FAULT_DRIVER_FAULT != 0 - } + /// The motor's H-bridge has encountered a fault. + const DRIVER_FAULT = pros_sys::E_MOTOR_FAULT_DRIVER_FAULT; - /// Check if the motor is over current. - pub fn is_over_current(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FAULT_OVER_CURRENT != 0 - } - - /// Check if the motor's H-bridge is over current. - pub fn is_driver_over_current(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FAULT_DRV_OVER_CURRENT != 0 + /// The motor's H-bridge is over current. + const DRIVER_OVER_CURRENT = pros_sys::E_MOTOR_FAULT_DRV_OVER_CURRENT; } } -impl TryFrom for MotorFaults { - type Error = MotorError; - - fn try_from(value: motor_fault_e_t) -> Result { - Ok(Self(bail_on!(PROS_ERR as _, value))) - } -} - -/// The status flags returned by a [`Motor`]. -#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] -pub struct MotorStatus(pub u32); - -impl MotorStatus { - /// Check if the motor is currently stopped. - pub fn is_stopped(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY != 0 - } - - /// Check if the motor is at its zero position. - pub fn is_zeroed(&self) -> bool { - self.0 & pros_sys::E_MOTOR_FLAGS_ZERO_POSITION != 0 - } -} - -impl TryFrom for MotorStatus { - type Error = MotorError; - - fn try_from(value: motor_flag_e_t) -> Result { - let flags = bail_on!(PROS_ERR as _, value); - - if flags & pros_sys::E_MOTOR_FLAGS_BUSY == 0 { - Ok(Self(flags)) - } else { - Err(MotorError::Busy) - } +bitflags! { + /// The status bits returned by a [`Motor`]. + #[derive(Debug, Clone, Copy, Eq, PartialEq)] + pub struct MotorStatus: u32 { + /// The motor is currently near zero velocity. + #[deprecated( + since = "0.9.0", + note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future." + )] + const STOPPED = pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY; + + /// The motor is at its zero position. + #[deprecated( + since = "0.9.0", + note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future." + )] + const ZEROED = pros_sys::E_MOTOR_FLAGS_ZERO_POSITION; + + /// Cannot currently communicate to the motor + const BUSY = pros_sys::E_MOTOR_FLAGS_BUSY; } } @@ -617,34 +594,16 @@ impl From for pros_sys::motor_pid_full_s_t { } } -/// A future that completes when the motor reports that it has stopped. -/// Created by [`Motor::wait_until_stopped`] -#[derive(Debug)] -pub struct MotorStoppedFuture<'a> { - motor: &'a Motor, -} - -impl<'a> core::future::Future for MotorStoppedFuture<'a> { - type Output = pros_core::error::Result; - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - match self.motor.status()?.is_stopped() { - true => core::task::Poll::Ready(Ok(())), - false => { - cx.waker().wake_by_ref(); - core::task::Poll::Pending - } - } - } -} - #[derive(Debug, Snafu)] /// Errors that can occur when using a motor. pub enum MotorError { /// Failed to communicate with the motor while attempting to read flags. Busy, + + /// This functionality is not currently implemented in hardware, even + /// though the SDK may support it. + NotImplemented, + /// Generic port related error. #[snafu(display("{source}"), context(false))] Port { @@ -654,6 +613,8 @@ pub enum MotorError { } map_errno! { - MotorError {} + MotorError { + ENOSYS => Self::NotImplemented, + } inherit PortError; } diff --git a/packages/pros-sys/src/error.rs b/packages/pros-sys/src/error.rs index bfe5a48b..6c33cefc 100644 --- a/packages/pros-sys/src/error.rs +++ b/packages/pros-sys/src/error.rs @@ -43,3 +43,4 @@ pub const ERANGE: c_int = 34; pub const EHOSTDOWN: c_int = 112; pub const EBADMSG: c_int = 74; pub const EADDRINUSE: c_int = 98; +pub const ENOSYS: c_int = 38; From a88c9c489667679616d72e2ff8c0063bba7222d1 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:57:20 -0600 Subject: [PATCH 16/33] refactor: `InertialStatus` bitflags --- packages/pros-devices/src/smart/imu.rs | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/pros-devices/src/smart/imu.rs b/packages/pros-devices/src/smart/imu.rs index b60c5ba6..caa59dc0 100644 --- a/packages/pros-devices/src/smart/imu.rs +++ b/packages/pros-devices/src/smart/imu.rs @@ -6,6 +6,7 @@ use core::{ time::Duration, }; +use bitflags::bitflags; use pros_core::{ bail_on, error::{take_errno, FromErrno, PortError}, @@ -56,8 +57,8 @@ impl InertialSensor { } /// Check if the Intertial Sensor is currently calibrating. - pub fn calibrating(&mut self) -> Result { - Ok(self.status()?.calibrating()) + pub fn is_calibrating(&mut self) -> Result { + Ok(self.status()?.contains(InertialStatus::CALIBRATING)) } /// Get the total number of degrees the Inertial Sensor has spun about the z-axis. @@ -103,7 +104,11 @@ impl InertialSensor { /// Read the inertial sensor's status code. pub fn status(&self) -> Result { - unsafe { pros_sys::imu_get_status(self.port.index()).try_into() } + let bits = bail_on!(pros_sys::E_IMU_STATUS_ERROR, unsafe { + pros_sys::motor_get_flags(self.port.index()) + }); + + Ok(InertialStatus::from_bits_retain(bits)) } /// Get a quaternion representing the Inertial Sensor’s orientation. @@ -376,22 +381,12 @@ impl TryFrom for InertialRaw { } } -/// Represents a status code returned by the Inertial Sensor. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct InertialStatus(pub u32); - -impl InertialStatus { - /// Determine if the sensor is currently calibrating. - pub const fn calibrating(&self) -> bool { - self.0 & pros_sys::E_IMU_STATUS_CALIBRATING != 0 - } -} - -impl TryFrom for InertialStatus { - type Error = InertialError; - - fn try_from(value: pros_sys::imu_status_e_t) -> Result { - Ok(Self(bail_on!(pros_sys::E_IMU_STATUS_ERROR, value))) +bitflags! { + /// The status bits returned by an [`InertialSensor`]. + #[derive(Debug, Clone, Copy, Eq, PartialEq)] + pub struct InertialStatus: u32 { + /// The sensor is currently calibrating. + const CALIBRATING = pros_sys::E_IMU_STATUS_CALIBRATING; } } From a70fa5879e22ed3e46cefa035b0bb2198a074255 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:30:25 -0600 Subject: [PATCH 17/33] refactor: `Direction` enum --- packages/pros-devices/src/smart/motor.rs | 62 ++++++++++++++++++------ 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index d95ca969..4c78b749 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -23,16 +23,45 @@ pub enum MotorControl { /// Motor is braking using a [`BrakeMode`]. Brake(BrakeMode), - /// Motor is attempting to hold a velocity using internal PID control. - Velocity(i32), - /// Motor is outputting a raw voltage. Voltage(f64), - /// Motor is attempting to reach a position. + /// Motor is attempting to hold a velocity using internal PID control. + Velocity(i32), + + /// Motor is attempting to reach a position using internal PID control. Position(Position, i32), } +/// Represents a possible direction that a motor can be configured as. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] +pub enum Direction { + /// Motor rotates in the forward direction. + #[default] + Forward, + + /// Motor rotates in the reverse direction. + Reverse +} + +impl Direction { + /// Returns `true` if the level is [`Forward`]. + pub const fn is_forward(&self) -> bool { + match self { + Self::Forward => true, + Self::Reverse => false, + } + } + + /// Returns `true` if the level is [`Reverse`]. + pub const fn is_reverse(&self) -> bool { + match self { + Self::Forward => false, + Self::Reverse => true, + } + } +} + impl Motor { /// The maximum voltage value that can be sent to a [`Motor`]. pub const MAX_VOLTAGE: f64 = 12.0; @@ -44,7 +73,7 @@ impl Motor { pub const DATA_WRITE_RATE: Duration = Duration::from_millis(5); /// Create a new motor from a smart port index. - pub fn new(port: SmartPort, gearset: Gearset, reversed: bool) -> Result { + pub fn new(port: SmartPort, gearset: Gearset, direction: Direction) -> Result { bail_on!(PROS_ERR, unsafe { pros_sys::motor_set_encoder_units(port.index(), pros_sys::E_MOTOR_ENCODER_DEGREES) }); @@ -55,7 +84,7 @@ impl Motor { }; motor.set_gearset(gearset)?; - motor.set_reversed(reversed)?; + motor.set_direction(direction)?; Ok(motor) } @@ -146,7 +175,7 @@ impl Motor { Ok(()) } - /// Get the current [`MotorControl`] value that the motor is attempting to reach. + /// Get the current [`MotorControl`] value that the motor is attempting to use. pub fn target(&self) -> MotorControl { self.target } @@ -335,19 +364,24 @@ impl Motor { Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT)) } - /// Set whether or not this motor's ouput should be reversed. - pub fn set_reversed(&mut self, reversed: bool) -> Result<(), MotorError> { + /// Set the [`Direction`] of this motor. + pub fn set_direction(&mut self, direction: Direction) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_reversed(self.port.index(), reversed) + pros_sys::motor_set_reversed(self.port.index(), direction.is_reverse()) }); Ok(()) } - /// Check if this motor has been reversed. - pub fn is_reversed(&self) -> Result { - Ok(bail_on!(PROS_ERR, unsafe { + /// Get the [`Direction`] of this motor. + pub fn direction(&self) -> Result { + let reversed = bail_on!(PROS_ERR, unsafe { pros_sys::motor_is_reversed(self.port.index()) - }) == 1) + }) == 1; + + Ok(match reversed { + false => Direction::Forward, + true => Direction::Reverse, + }) } /// Adjusts the internal tuning constants of the motor when using velocity control. From cf9899a9aeb5e635f4570c07970c5f173ef63061 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:31:14 -0600 Subject: [PATCH 18/33] refactor: switch to i8 port indexes in `pros_sys` --- packages/pros-devices/src/smart/imu.rs | 2 +- packages/pros-devices/src/smart/motor.rs | 72 ++++++++++--------- packages/pros-sys/src/motor.rs | 90 ++++++++++++------------ 3 files changed, 85 insertions(+), 79 deletions(-) diff --git a/packages/pros-devices/src/smart/imu.rs b/packages/pros-devices/src/smart/imu.rs index caa59dc0..b76ef393 100644 --- a/packages/pros-devices/src/smart/imu.rs +++ b/packages/pros-devices/src/smart/imu.rs @@ -105,7 +105,7 @@ impl InertialSensor { /// Read the inertial sensor's status code. pub fn status(&self) -> Result { let bits = bail_on!(pros_sys::E_IMU_STATUS_ERROR, unsafe { - pros_sys::motor_get_flags(self.port.index()) + pros_sys::imu_get_status(self.port.index()) }); Ok(InertialStatus::from_bits_retain(bits)) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 4c78b749..426c8e2d 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -34,14 +34,13 @@ pub enum MotorControl { } /// Represents a possible direction that a motor can be configured as. -#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Direction { /// Motor rotates in the forward direction. - #[default] Forward, /// Motor rotates in the reverse direction. - Reverse + Reverse, } impl Direction { @@ -73,9 +72,13 @@ impl Motor { pub const DATA_WRITE_RATE: Duration = Duration::from_millis(5); /// Create a new motor from a smart port index. - pub fn new(port: SmartPort, gearset: Gearset, direction: Direction) -> Result { + pub fn new( + port: SmartPort, + gearset: Gearset, + direction: Direction, + ) -> Result { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_encoder_units(port.index(), pros_sys::E_MOTOR_ENCODER_DEGREES) + pros_sys::motor_set_encoder_units(port.index() as i8, pros_sys::E_MOTOR_ENCODER_DEGREES) }); let mut motor = Self { @@ -97,24 +100,24 @@ impl Motor { MotorControl::Brake(mode) => unsafe { bail_on!( PROS_ERR, - pros_sys::motor_set_brake_mode(self.port.index(), mode.into()) + pros_sys::motor_set_brake_mode(self.port.index() as i8, mode.into()) ); - bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index())); + bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index() as i8)); }, MotorControl::Velocity(rpm) => { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_velocity(self.port.index(), rpm) + pros_sys::motor_move_velocity(self.port.index() as i8, rpm) }); } MotorControl::Voltage(volts) => { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_move_voltage(self.port.index(), (volts * 1000.0) as i32) + pros_sys::motor_move_voltage(self.port.index() as i8, (volts * 1000.0) as i32) }); } MotorControl::Position(position, velocity) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_absolute( - self.port.index(), + self.port.index() as i8, position.into_degrees(), velocity, ) @@ -162,7 +165,7 @@ impl Motor { /// This will have no effect if the motor is not following a profiled movement. pub fn update_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_modify_profiled_velocity(self.port.index(), velocity) + pros_sys::motor_modify_profiled_velocity(self.port.index() as i8, velocity) }); match self.target { @@ -183,34 +186,34 @@ impl Motor { /// Sets the gearset of the motor. pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_gearing(self.port.index(), gearset as i32) + pros_sys::motor_set_gearing(self.port.index() as i8, gearset as i32) }); Ok(()) } /// Gets the gearset of the motor. pub fn gearset(&self) -> Result { - unsafe { pros_sys::motor_get_gearing(self.port.index()).try_into() } + unsafe { pros_sys::motor_get_gearing(self.port.index() as i8).try_into() } } /// Gets the estimated angular velocity (RPM) of the motor. pub fn velocity(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { - pros_sys::motor_get_actual_velocity(self.port.index()) + pros_sys::motor_get_actual_velocity(self.port.index() as i8) })) } /// Returns the power drawn by the motor in Watts. pub fn power(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { - pros_sys::motor_get_power(self.port.index()) + pros_sys::motor_get_power(self.port.index() as i8) })) } /// Returns the torque output of the motor in Nm. pub fn torque(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { - pros_sys::motor_get_torque(self.port.index()) + pros_sys::motor_get_torque(self.port.index() as i8) })) } @@ -218,7 +221,7 @@ impl Motor { pub fn voltage(&self) -> Result { // docs say this function returns PROS_ERR_F but it actually returns PROS_ERR let millivolts = bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_voltage(self.port.index()) + pros_sys::motor_get_voltage(self.port.index() as i8) }); Ok(millivolts as f64 / 1000.0) } @@ -226,21 +229,24 @@ impl Motor { /// Returns the current position of the motor. pub fn position(&self) -> Result { Ok(Position::from_degrees(bail_on!(PROS_ERR_F, unsafe { - pros_sys::motor_get_position(self.port.index()) + pros_sys::motor_get_position(self.port.index() as i8) }))) } /// Returns the raw position tick data recorded by the motor at a given timestamp. pub fn raw_position(&self, timestamp: Duration) -> Result { Ok(bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_raw_position(self.port.index(), timestamp.as_millis() as *const u32) + pros_sys::motor_get_raw_position( + self.port.index() as i8, + timestamp.as_millis() as *const u32, + ) })) } /// Returns the electrical current draw of the motor in amps. pub fn current(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_current_draw(self.port.index()) + pros_sys::motor_get_current_draw(self.port.index() as i8) }) as f64 / 1000.0) } @@ -252,7 +258,7 @@ impl Motor { /// is drawing power but not moving. pub fn efficiency(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { - pros_sys::motor_get_efficiency(self.port.index()) + pros_sys::motor_get_efficiency(self.port.index() as i8) })) } @@ -260,7 +266,7 @@ impl Motor { /// Analogous to taring or resetting the encoder to the current position. pub fn zero(&mut self) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_tare_position(self.port.index()) + pros_sys::motor_tare_position(self.port.index() as i8) }); Ok(()) } @@ -269,7 +275,7 @@ impl Motor { /// Analogous to taring or resetting the encoder so that the new position is equal to the given position. pub fn set_position(&mut self, position: Position) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_zero_position(self.port.index(), position.into_degrees()) + pros_sys::motor_set_zero_position(self.port.index() as i8, position.into_degrees()) }); Ok(()) } @@ -277,7 +283,7 @@ impl Motor { /// Sets the current limit for the motor in amps. pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_current_limit(self.port.index(), (limit * 1000.0) as i32) + pros_sys::motor_set_current_limit(self.port.index() as i8, (limit * 1000.0) as i32) }); Ok(()) } @@ -289,7 +295,7 @@ impl Motor { // seriously don't buy it. We unfortunately can't tell if // this is true or not just from source code, since this // function just wraps vexDeviceMotorVoltageLimitSet. - pros_sys::motor_set_voltage_limit(self.port.index(), (limit * 1000.0) as i32) + pros_sys::motor_set_voltage_limit(self.port.index() as i8, (limit * 1000.0) as i32) }); Ok(()) } @@ -297,7 +303,7 @@ impl Motor { /// Gets the current limit for the motor in amps. pub fn current_limit(&self) -> Result { Ok(bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_current_limit(self.port.index()) + pros_sys::motor_get_current_limit(self.port.index() as i8) }) as f64 / 1000.0) } @@ -305,7 +311,7 @@ impl Motor { /// Gets the voltage limit for the motor if one has been explicitly set. pub fn voltage_limit(&self) -> Result { let raw_limit = bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_voltage_limit(self.port.index()) + pros_sys::motor_get_voltage_limit(self.port.index() as i8) }); Ok(match raw_limit { @@ -323,7 +329,7 @@ impl Motor { /// Get the status flags of a motor. pub fn status(&self) -> Result { let bits = bail_on!(PROS_ERR as u32, unsafe { - pros_sys::motor_get_flags(self.port.index()) + pros_sys::motor_get_flags(self.port.index() as i8) }); // For some reason, PROS doesn't set errno if this flag is returned, @@ -338,7 +344,7 @@ impl Motor { /// Get the fault flags of the motor. pub fn faults(&self) -> Result { let bits = bail_on!(PROS_ERR as u32, unsafe { - pros_sys::motor_get_faults(self.port.index()) + pros_sys::motor_get_faults(self.port.index() as i8) }); Ok(MotorFaults::from_bits_retain(bits)) @@ -367,7 +373,7 @@ impl Motor { /// Set the [`Direction`] of this motor. pub fn set_direction(&mut self, direction: Direction) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - pros_sys::motor_set_reversed(self.port.index(), direction.is_reverse()) + pros_sys::motor_set_reversed(self.port.index() as i8, direction.is_reverse()) }); Ok(()) } @@ -375,7 +381,7 @@ impl Motor { /// Get the [`Direction`] of this motor. pub fn direction(&self) -> Result { let reversed = bail_on!(PROS_ERR, unsafe { - pros_sys::motor_is_reversed(self.port.index()) + pros_sys::motor_is_reversed(self.port.index() as i8) }) == 1; Ok(match reversed { @@ -402,7 +408,7 @@ impl Motor { ) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { #[allow(deprecated)] - pros_sys::motor_set_pos_pid_full(self.port.index(), constants.into()) + pros_sys::motor_set_pos_pid_full(self.port.index() as i8, constants.into()) }); Ok(()) } @@ -425,7 +431,7 @@ impl Motor { ) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { #[allow(deprecated)] - pros_sys::motor_set_vel_pid_full(self.port.index(), constants.into()) + pros_sys::motor_set_vel_pid_full(self.port.index() as i8, constants.into()) }); Ok(()) } diff --git a/packages/pros-sys/src/motor.rs b/packages/pros-sys/src/motor.rs index b893904b..dbad67e5 100644 --- a/packages/pros-sys/src/motor.rs +++ b/packages/pros-sys/src/motor.rs @@ -118,7 +118,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_move(port: u8, voltage: i32) -> i32; + pub fn motor_move(port: i8, voltage: i32) -> i32; /** Stops the motor using the currently configured brake mode. @@ -138,7 +138,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_brake(port: u8) -> i32; + pub fn motor_brake(port: i8) -> i32; /** Sets the target absolute position for the motor to move to. @@ -163,7 +163,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_move_absolute(port: u8, position: c_double, velocity: i32) -> i32; + pub fn motor_move_absolute(port: i8, position: c_double, velocity: i32) -> i32; /** Sets the relative target position for the motor to move to. @@ -190,7 +190,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_move_relative(port: u8, position: c_double, velocity: i32) -> i32; + pub fn motor_move_relative(port: i8, position: c_double, velocity: i32) -> i32; /** Sets the velocity for the motor. @@ -214,7 +214,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_move_velocity(port: u8, velocity: i32) -> i32; + pub fn motor_move_velocity(port: i8, velocity: i32) -> i32; /** Sets the output voltage for the motor from -12000 to 12000 in millivolts @@ -231,7 +231,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_move_voltage(port: u8, voltage: i32) -> i32; + pub fn motor_move_voltage(port: i8, voltage: i32) -> i32; /** Changes the output velocity for a profiled movement (motor_move_absolute or motor_move_relative). This will have no effect if the motor is not following @@ -251,7 +251,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_modify_profiled_velocity(port: u8, velocity: i32) -> i32; + pub fn motor_modify_profiled_velocity(port: i8, velocity: i32) -> i32; /** Gets the target position set for the motor by the user. @@ -266,7 +266,7 @@ extern "C" { \return The target position in its encoder units or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_target_position(port: u8) -> c_double; + pub fn motor_get_target_position(port: i8) -> c_double; /** Gets the velocity commanded to the motor by the user. @@ -281,7 +281,7 @@ extern "C" { \return The commanded motor velocity from +-100, +-200, or +-600, or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_get_target_velocity(port: u8) -> i32; + pub fn motor_get_target_velocity(port: i8) -> i32; /** Gets the actual velocity of the motor. @@ -296,7 +296,7 @@ extern "C" { \return The motor's actual velocity in RPM or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_actual_velocity(port: u8) -> c_double; + pub fn motor_get_actual_velocity(port: i8) -> c_double; /** Gets the current drawn by the motor in mA. @@ -311,7 +311,7 @@ extern "C" { \return The motor's current in mA or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_get_current_draw(port: u8) -> i32; + pub fn motor_get_current_draw(port: i8) -> i32; /** Gets the direction of movement for the motor. @@ -326,7 +326,7 @@ extern "C" { \return 1 for moving in the positive direction, -1 for moving in the negative direction, or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_get_direction(port: u8) -> i32; + pub fn motor_get_direction(port: i8) -> i32; /** Gets the efficiency of the motor in percent. @@ -345,7 +345,7 @@ extern "C" { \return The motor's efficiency in percent or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_efficiency(port: u8) -> c_double; + pub fn motor_get_efficiency(port: i8) -> c_double; /** Checks if the motor is drawing over its current limit. @@ -360,7 +360,7 @@ extern "C" { \return 1 if the motor's current limit is being exceeded and 0 if the current limit is not exceeded, or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_is_over_current(port: u8) -> i32; + pub fn motor_is_over_current(port: i8) -> i32; /** Checks if the motor's temperature is above its limit. @@ -375,7 +375,7 @@ extern "C" { \return 1 if the temperature limit is exceeded and 0 if the the temperature is below the limit, or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_is_over_temp(port: u8) -> i32; + pub fn motor_is_over_temp(port: i8) -> i32; /** Checks if the motor is stopped. @@ -389,7 +389,7 @@ extern "C" { \return 1 if the motor is not moving, 0 if the motor is moving, or PROS_ERR if the operation failed, setting errno */ - pub fn motor_is_stopped(port: u8) -> i32; + pub fn motor_is_stopped(port: i8) -> i32; /** Checks if the motor is at its zero position. @@ -404,7 +404,7 @@ extern "C" { moved from its absolute zero, or PROS_ERR if the operation failed, setting errno */ - pub fn motor_get_zero_position_flag(port: u8) -> i32; + pub fn motor_get_zero_position_flag(port: i8) -> i32; /** Gets the faults experienced by the motor. @@ -421,7 +421,7 @@ extern "C" { \return A bitfield containing the motor's faults. */ - pub fn motor_get_faults(port: u8) -> motor_fault_e_t; + pub fn motor_get_faults(port: i8) -> motor_fault_e_t; /** Gets the flags set by the motor's operation. @@ -437,7 +437,7 @@ extern "C" { \return A bitfield containing the motor's flags. */ - pub fn motor_get_flags(port: u8) -> motor_flag_e_t; + pub fn motor_get_flags(port: i8) -> motor_flag_e_t; /** Gets the raw encoder count of the motor at a given timestamp. @@ -457,7 +457,7 @@ extern "C" { \return The raw encoder count at the given timestamp or PROS_ERR if the operation failed. */ - pub fn motor_get_raw_position(port: u8, timestamp: *const u32) -> i32; + pub fn motor_get_raw_position(port: i8, timestamp: *const u32) -> i32; /** Gets the absolute position of the motor in its encoder units. @@ -472,7 +472,7 @@ extern "C" { \return The motor's absolute position in its encoder units or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_position(port: u8) -> c_double; + pub fn motor_get_position(port: i8) -> c_double; /** Gets the power drawn by the motor in Watts. @@ -487,7 +487,7 @@ extern "C" { \return The motor's power draw in Watts or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_power(port: u8) -> c_double; + pub fn motor_get_power(port: i8) -> c_double; /** Gets the temperature of the motor in degrees Celsius. @@ -502,7 +502,7 @@ extern "C" { \return The motor's temperature in degrees Celsius or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_temperature(port: u8) -> c_double; + pub fn motor_get_temperature(port: i8) -> c_double; /** Gets the torque generated by the motor in Newton Meters (Nm). @@ -517,7 +517,7 @@ extern "C" { \return The motor's torque in Nm or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_torque(port: u8) -> c_double; + pub fn motor_get_torque(port: i8) -> c_double; /** Gets the voltage delivered to the motor in millivolts. @@ -532,7 +532,7 @@ extern "C" { \return The motor's voltage in mV or PROS_ERR_F if the operation failed, setting errno. */ - pub fn motor_get_voltage(port: u8) -> i32; + pub fn motor_get_voltage(port: i8) -> i32; /** Sets the position for the motor in its encoder units. @@ -552,7 +552,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_zero_position(port: u8, position: c_double) -> i32; + pub fn motor_set_zero_position(port: i8, position: c_double) -> i32; /** Sets the "absolute" zero position of the motor to its current position. @@ -567,7 +567,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_tare_position(port: u8) -> i32; + pub fn motor_tare_position(port: i8) -> i32; /** Sets one of motor_brake_mode_e_t to the motor. @@ -584,7 +584,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_brake_mode(port: u8, mode: motor_brake_mode_e_t) -> i32; + pub fn motor_set_brake_mode(port: i8, mode: motor_brake_mode_e_t) -> i32; /** Sets the current limit for the motor in mA. @@ -601,7 +601,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_current_limit(port: u8, limit: i32) -> i32; + pub fn motor_set_current_limit(port: i8, limit: i32) -> i32; /** Sets one of motor_encoder_units_e_t for the motor encoder. @@ -618,7 +618,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_encoder_units(port: u8, units: motor_encoder_units_e_t) -> i32; + pub fn motor_set_encoder_units(port: i8, units: motor_encoder_units_e_t) -> i32; /** Sets one of motor_gearset_e_t for the motor. @@ -635,7 +635,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_gearing(port: u8, gearset: motor_gearset_e_t) -> i32; + pub fn motor_set_gearing(port: i8, gearset: motor_gearset_e_t) -> i32; /** Takes in floating point values and returns a properly formatted pid struct. @@ -739,7 +739,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_set_pos_pid(port: u8, pid: motor_pid_s_t) -> i32; + pub fn motor_set_pos_pid(port: i8, pid: motor_pid_s_t) -> i32; /** Sets one of motor_pid_full_s_t for the motor. @@ -764,7 +764,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_set_pos_pid_full(port: u8, pid: motor_pid_full_s_t) -> i32; + pub fn motor_set_pos_pid_full(port: i8, pid: motor_pid_full_s_t) -> i32; /** Sets one of motor_pid_s_t for the motor. This intended to just modify the main PID constants. @@ -790,7 +790,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_set_vel_pid(port: u8, pid: motor_pid_s_t) -> i32; + pub fn motor_set_vel_pid(port: i8, pid: motor_pid_s_t) -> i32; /** Sets one of motor_pid_full_s_t for the motor. @@ -815,7 +815,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_set_vel_pid_full(port: u8, pid: motor_pid_full_s_t) -> i32; + pub fn motor_set_vel_pid_full(port: i8, pid: motor_pid_full_s_t) -> i32; /** Gets the position PID that was set for the motor. This function will return zero for all of the parameters if the motor_set_pos_pid() or @@ -838,7 +838,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_get_pos_pid(port: u8) -> motor_pid_full_s_t; + pub fn motor_get_pos_pid(port: i8) -> motor_pid_full_s_t; /** Gets the velocity PID that was set for the motor. This function will return zero for all of the parameters if the motor_set_vel_pid() or @@ -861,7 +861,7 @@ extern "C" { #[deprecated( note = "Changing these values is not supported by VEX and may lead to permanent motor damage." )] - pub fn motor_get_vel_pid(port: u8) -> motor_pid_full_s_t; + pub fn motor_get_vel_pid(port: i8) -> motor_pid_full_s_t; /** Sets the reverse flag for the motor. @@ -880,7 +880,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_reversed(port: u8, reversed: bool) -> i32; + pub fn motor_set_reversed(port: i8, reversed: bool) -> i32; /** Sets the voltage limit for the motor in Volts. @@ -897,7 +897,7 @@ extern "C" { \return 1 if the operation was successful or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_set_voltage_limit(port: u8, limit: i32) -> i32; + pub fn motor_set_voltage_limit(port: i8, limit: i32) -> i32; /** Gets the brake mode that was set for the motor. @@ -912,7 +912,7 @@ extern "C" { \return One of motor_brake_mode_e_t, according to what was set for the motor, or E_MOTOR_BRAKE_INVALID if the operation failed, setting errno. */ - pub fn motor_get_brake_mode(port: u8) -> motor_brake_mode_e_t; + pub fn motor_get_brake_mode(port: i8) -> motor_brake_mode_e_t; /** Gets the current limit for the motor in mA. @@ -929,7 +929,7 @@ extern "C" { \return The motor's current limit in mA or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_get_current_limit(port: u8) -> i32; + pub fn motor_get_current_limit(port: i8) -> i32; /** Gets the encoder units that were set for the motor. @@ -944,7 +944,7 @@ extern "C" { \return One of motor_encoder_units_e_t according to what is set for the motor or E_MOTOR_ENCODER_INVALID if the operation failed. */ - pub fn motor_get_encoder_units(port: u8) -> motor_encoder_units_e_t; + pub fn motor_get_encoder_units(port: i8) -> motor_encoder_units_e_t; /** Gets the gearset that was set for the motor. @@ -959,7 +959,7 @@ extern "C" { \return One of motor_gearset_e_t according to what is set for the motor, or E_GEARSET_INVALID if the operation failed. */ - pub fn motor_get_gearing(port: u8) -> motor_gearset_e_t; + pub fn motor_get_gearing(port: i8) -> motor_gearset_e_t; /** Gets the operation direction of the motor as set by the user. @@ -974,7 +974,7 @@ extern "C" { \return 1 if the motor has been reversed and 0 if the motor was not reversed, or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_is_reversed(port: u8) -> i32; + pub fn motor_is_reversed(port: i8) -> i32; /** Gets the voltage limit set by the user. @@ -992,5 +992,5 @@ extern "C" { \return The motor's voltage limit in V or PROS_ERR if the operation failed, setting errno. */ - pub fn motor_get_voltage_limit(port: u8) -> i32; + pub fn motor_get_voltage_limit(port: i8) -> i32; } From cb7c6d0377be2b42e9ef38edf81c45266472a19d Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:35:52 -0600 Subject: [PATCH 19/33] refactor: use coast braking on velocity/position control --- packages/pros-devices/src/smart/motor.rs | 36 +++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 426c8e2d..e09c0362 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -104,25 +104,41 @@ impl Motor { ); bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index() as i8)); }, - MotorControl::Velocity(rpm) => { - bail_on!(PROS_ERR, unsafe { + MotorControl::Velocity(rpm) => unsafe { + bail_on!( + PROS_ERR, pros_sys::motor_move_velocity(self.port.index() as i8, rpm) - }); - } + ); + bail_on!( + PROS_ERR, + pros_sys::motor_set_brake_mode( + self.port.index() as i8, + pros_sys::E_MOTOR_BRAKE_COAST + ) + ); + }, MotorControl::Voltage(volts) => { bail_on!(PROS_ERR, unsafe { pros_sys::motor_move_voltage(self.port.index() as i8, (volts * 1000.0) as i32) }); } - MotorControl::Position(position, velocity) => { - bail_on!(PROS_ERR, unsafe { + MotorControl::Position(position, velocity) => unsafe { + bail_on!( + PROS_ERR, + pros_sys::motor_set_brake_mode( + self.port.index() as i8, + pros_sys::E_MOTOR_BRAKE_COAST + ) + ); + bail_on!( + PROS_ERR, pros_sys::motor_move_absolute( self.port.index() as i8, position.into_degrees(), velocity, ) - }); - } + ); + }, } self.target = target; @@ -507,14 +523,14 @@ bitflags! { since = "0.9.0", note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future." )] - const STOPPED = pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY; + const ZERO_VELOCITY = pros_sys::E_MOTOR_FLAGS_ZERO_VELOCITY; /// The motor is at its zero position. #[deprecated( since = "0.9.0", note = "This flag will never be set by the hardware, even though it exists in the SDK. This may change in the future." )] - const ZEROED = pros_sys::E_MOTOR_FLAGS_ZERO_POSITION; + const ZERO_POSITION = pros_sys::E_MOTOR_FLAGS_ZERO_POSITION; /// Cannot currently communicate to the motor const BUSY = pros_sys::E_MOTOR_FLAGS_BUSY; From b25cd5f292f8cbd6454350566499d713934438c9 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:45:14 -0600 Subject: [PATCH 20/33] docs: update description --- packages/pros-devices/src/smart/motor.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index e09c0362..4782da2f 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -307,10 +307,8 @@ impl Motor { /// Sets the voltage limit for the motor in volts. pub fn set_voltage_limit(&mut self, limit: f64) -> Result<(), MotorError> { bail_on!(PROS_ERR, unsafe { - // TODO: Docs claim that this function takes volts, but I - // seriously don't buy it. We unfortunately can't tell if - // this is true or not just from source code, since this - // function just wraps vexDeviceMotorVoltageLimitSet. + // Docs claim that this function takes volts, but this is incorrect. It takes millivolts, + // just like all other SDK voltage-related functions. pros_sys::motor_set_voltage_limit(self.port.index() as i8, (limit * 1000.0) as i32) }); Ok(()) From eda3dd24f41a7844048072f0736851afb3b52a0d Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 00:15:15 -0600 Subject: [PATCH 21/33] fix: remove `voltage_limit` until kernel fix --- packages/pros-devices/src/smart/motor.rs | 30 ++++++++++-------------- packages/pros/src/lib.rs | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 4782da2f..44163487 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -311,6 +311,7 @@ impl Motor { // just like all other SDK voltage-related functions. pros_sys::motor_set_voltage_limit(self.port.index() as i8, (limit * 1000.0) as i32) }); + Ok(()) } @@ -322,23 +323,18 @@ impl Motor { / 1000.0) } - /// Gets the voltage limit for the motor if one has been explicitly set. - pub fn voltage_limit(&self) -> Result { - let raw_limit = bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_voltage_limit(self.port.index() as i8) - }); - - Ok(match raw_limit { - // SDK uses a voltage limit of zero to indicate that there is no limit present - 0 => Self::MAX_VOLTAGE, - - // TODO: Docs claim that this function returns volts, but I - // seriously don't buy it. We unfortunately can't tell if - // this is true or not just from source code, since this - // function just wraps vexDeviceMotorVoltageLimitGet. - limit => limit as f64 / 1000.0, - }) - } + // /// Gets the voltage limit for the motor if one has been explicitly set. + // /// NOTE: Broken until next kernel version due to mutex release bug. + // pub fn voltage_limit(&self) -> Result { + // // NOTE: PROS docs claim that this function will return zero if voltage is uncapped. + // // + // // From testing this does not appear to be true, so we don't need to perform any + // // special checks for a zero return value. + // Ok(bail_on!(PROS_ERR, unsafe { + // pros_sys::motor_get_voltage_limit(self.port.index() as i8) + // }) as f64 + // / 1000.0) + // } /// Get the status flags of a motor. pub fn status(&self) -> Result { diff --git a/packages/pros/src/lib.rs b/packages/pros/src/lib.rs index 7e24f127..e14accd7 100644 --- a/packages/pros/src/lib.rs +++ b/packages/pros/src/lib.rs @@ -106,7 +106,7 @@ pub mod prelude { gps::GpsSensor, imu::InertialSensor, link::{Link, RxLink, TxLink}, - motor::{BrakeMode, Gearset, Motor}, + motor::{BrakeMode, Direction, Gearset, Motor, MotorControl}, optical::OpticalSensor, rotation::RotationSensor, vision::VisionSensor, From 5ad9b43c81cd7d9014193a4e7d14a539cc0c96d4 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:55:07 -0600 Subject: [PATCH 22/33] refactor: rename `rotate_to_position` to `set_position_target` --- packages/pros-devices/src/smart/motor.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 44163487..42017167 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -105,10 +105,6 @@ impl Motor { bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index() as i8)); }, MotorControl::Velocity(rpm) => unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_velocity(self.port.index() as i8, rpm) - ); bail_on!( PROS_ERR, pros_sys::motor_set_brake_mode( @@ -116,6 +112,10 @@ impl Motor { pros_sys::E_MOTOR_BRAKE_COAST ) ); + bail_on!( + PROS_ERR, + pros_sys::motor_move_velocity(self.port.index() as i8, rpm) + ); }, MotorControl::Voltage(volts) => { bail_on!(PROS_ERR, unsafe { @@ -168,7 +168,7 @@ impl Motor { } /// Sets an absolute position target for the motor to attempt to reach. - pub fn rotate_to_position( + pub fn set_position_target( &mut self, position: Position, velocity: i32, From 86125ae4ea40a51d89c7e0239fbcba650fdcb5ba Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:00:10 -0600 Subject: [PATCH 23/33] feat: add max RPM getter for `Gearset` --- packages/pros-devices/src/smart/motor.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 42017167..574feca1 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -557,6 +557,22 @@ impl Gearset { pub const RPM_200: Gearset = Gearset::Green; /// 600 rpm pub const RPM_600: Gearset = Gearset::Blue; + + /// Rated max speed for a smart motor with a red gearset cartridge. + pub const MAX_RED_RPM: f64 = 100.0; + /// Rated speed for a smart motor with a green cartridge. + pub const MAX_GREEN_RPM: f64 = 200.0; + /// Rated speed for a smart motor with a blue cartridge. + pub const MAX_BLUE_RPM: f64 = 600.0; + + /// Get the rated maximum speed for this motor gearset. + pub const fn max_rpm(&self) -> f64 { + match self { + Self::Red => Self::MAX_RED_RPM, + Self::Green => Self::MAX_GREEN_RPM, + Self::Blue => Self::MAX_BLUE_RPM, + } + } } impl From for pros_sys::motor_gearset_e_t { From 0555f6f4813c2943301b812446eaa9e334fdaa25 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:11:14 -0600 Subject: [PATCH 24/33] docs: fix example --- packages/pros/examples/accessories.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/pros/examples/accessories.rs b/packages/pros/examples/accessories.rs index 811849f5..e9329a54 100644 --- a/packages/pros/examples/accessories.rs +++ b/packages/pros/examples/accessories.rs @@ -23,7 +23,7 @@ impl ExampleRobot { pub fn new(peripherals: Peripherals) -> Self { Self { motor: Arc::new(Mutex::new( - Motor::new(peripherals.port_2, Gearset::Green, false).unwrap(), + Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward).unwrap(), )), vision: VisionSensor::new(peripherals.port_9, VisionZeroPoint::Center).unwrap(), } @@ -40,9 +40,6 @@ impl AsyncRobot for ExampleRobot { }); handle.await; - // Create a new motor plugged into port 2. The motor will brake when not moving. - // We'll wrap it in an Arc> to allow safe access to the device from multiple tasks. - self.motor.lock().wait_until_stopped().await?; // Create a controller, specifically controller 1. let controller = Controller::Master; @@ -56,14 +53,14 @@ impl AsyncRobot for ExampleRobot { move || loop { println!( "Motor stopped? {}", - motor.lock().get_state().unwrap_or_default().stopped + motor.lock().velocity() < 2 ); // Sleep the task as to not steal processing time from the OS. // This should always be done in any loop, including loops in the main task. // Because this is a real FreeRTOS task this is not the sleep function used elsewhere in this example. // This sleep function will block the entire task, including the async executor! (There isn't one running here, but there is in the main task.) - delay(Duration::from_millis(20)); + delay(Duration::from_millis(Motor::DATA_READ_RATE)); } }); @@ -72,7 +69,7 @@ impl AsyncRobot for ExampleRobot { // Set output takes a float from -1 to 1 that is scaled to -12 to 12 volts. self.motor .lock() - .set_output(controller.state()?.joysticks.right.y)?; + .set_voltage(Motor::MAX_VOLTAGE * controller.state()?.joysticks.right.y)?; // println!("pid out {}", pid.update(10.0, motor.position().into_degrees() as f32)); println!( From f71cfea9ea330e1cbc32ec478a1e9ff0683fc8f3 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:25:50 -0600 Subject: [PATCH 25/33] docs: update changelog --- CHANGELOG.md | 28 ++++++++++++++++++++++++--- packages/pros/examples/accessories.rs | 5 +---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 626575a9..bf8afeea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,17 +22,39 @@ Before releasing: ## [Unreleased] ### Added +- New Motor API (**Breaking Change**) (#66) + - Added `MotorControl` enum for controlling motor targets (#66). + - Added support for onboard velocity and position control in motors (#66). + - Added missing motor telemetry functions `velocity`, `efficiency`, `gearset`, `current_limit`. + - Added support for current and voltage limiting in motors. + - Added `Motor::raw_position` for getting raw IME ticks at a given timestamp. + - Added support for getting motor fault flags (e.g. over-temperature, over-current, H-bridge faults). + - Added support for internal motor PID tuning. Feature gated behind `dangerous_motor_tuning`, as this can cause hardware damage and is not recommended. + - Added various constants for convenience around `Motor` and `Gearset`. ### Fixed +- `pros_sys` bindings to the Motors C API now takes the correct port type (`i8`) as of PROS 4 (**Breaking Change**) (#66). + ### Changed -- Adjusts constructor arguments for `Motor` to allow passing `Gearset` and `reversed` instead of `brake_mode` at construction. (**Breaking Change**) (#66) -- Renamed `Motor::get_state` to `Motor::state`. (**Breaking Change**) (#66) -- Changed `Motor::reversed` to return `Result`` rather than just `false` if `PROS_ERR` is returned. (**Breaking Change**) (#66) +- Refactored the Motor API (**Breaking Change**) (#66) + - Adjusts constructor arguments for `Motor` to allow passing `Gearset` and a direction instead of `brake_mode` at construction. (**Breaking Change**) (#66) + - Renamed `Motor::get_state` to `Motor::state`. (**Breaking Change**) (#66) + - Changed `Motor::reversed` to return `Result`` rather than just `false` if `PROS_ERR` is returned. (**Breaking Change**) (#66) + - Adjusted motor targeting to work around the `MotorControl` enum. + - Adjusted motor reverse flag to use `Direction` enum rather than a boolean. + - Motor braking is now stateless and requires an explicit method to be called to use `BrakeMode`s other than `BrakeMode::Coast`. + - Renamed `Motor::current_draw` to `Motor::current`. + - Renamed `Motor::get_state` to `Motor::status`. +- Status structs containing device bits now use the `bitflags!` crate. (**Breaking Change**) (#66) +- Renamed `InertialSensor::calibrating` to `InertialSensor::calibrating` (**Breaking CHange**) (#66) ### Removed +- Removed `MotorStoppedFuture`, as it was broken due to hardware not returning the `stopped` flag (**Breaking Change**) (#66). +- Removed `Motor::set_output` and `Motor::set_raw_output` in favor of `set_voltage`. + ## [0.8.0] ### Added diff --git a/packages/pros/examples/accessories.rs b/packages/pros/examples/accessories.rs index e9329a54..6a032819 100644 --- a/packages/pros/examples/accessories.rs +++ b/packages/pros/examples/accessories.rs @@ -51,10 +51,7 @@ impl AsyncRobot for ExampleRobot { let motor = Arc::clone(&self.motor); // Obtain a shared reference to our motor to safely share between tasks. move || loop { - println!( - "Motor stopped? {}", - motor.lock().velocity() < 2 - ); + println!("Motor stopped? {}", motor.lock().velocity() < 2); // Sleep the task as to not steal processing time from the OS. // This should always be done in any loop, including loops in the main task. From a7f7dd3d67019764b40fc23cad62b1a62015cfc6 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:27:28 -0600 Subject: [PATCH 26/33] refactor: make `efficiency` return 0.0-1.0 --- packages/pros-devices/src/smart/motor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 574feca1..ec29d029 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -267,15 +267,15 @@ impl Motor { / 1000.0) } - /// Gets the efficiency of the motor in percent. + /// Gets the efficiency of the motor from a range of [0.0, 1.0]. /// - /// An efficiency of 100% means that the motor is moving electrically while - /// drawing no electrical power, and an efficiency of 0% means that the motor + /// An efficiency of 1.0 means that the motor is moving electrically while + /// drawing no electrical power, and an efficiency of 0.0 means that the motor /// is drawing power but not moving. pub fn efficiency(&self) -> Result { Ok(bail_on!(PROS_ERR_F, unsafe { pros_sys::motor_get_efficiency(self.port.index() as i8) - })) + }) / 100.0) } /// Sets the current encoder position to zero without moving the motor. From 127f7e2378ed9fb1db5206341855eabade243c25 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 13:24:45 -0600 Subject: [PATCH 27/33] feat: add TPR getter --- packages/pros-devices/src/smart/motor.rs | 50 ++++++++++++++++-------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index ec29d029..839934e0 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -544,27 +544,34 @@ pub enum Gearset { } impl Gearset { - /// 36:1 gear ratio - pub const RATIO_36: Gearset = Gearset::Red; - /// 18:1 gear ratio - pub const RATIO_18: Gearset = Gearset::Green; - /// 6:1 gear ratio - pub const RATIO_6: Gearset = Gearset::Blue; - - /// 100 rpm - pub const RPM_100: Gearset = Gearset::Red; - /// 200 rpm - pub const RPM_200: Gearset = Gearset::Green; - /// 600 rpm - pub const RPM_600: Gearset = Gearset::Blue; - - /// Rated max speed for a smart motor with a red gearset cartridge. + /// 36:1 gear ratio (alias to `Self::Red`) + pub const RATIO_36: Gearset = Self::Red; + /// 18:1 gear ratio (alias to `Self::Green`) + pub const RATIO_18: Gearset = Self::Green; + /// 6:1 gear ratio (alias to `Self::Blue`) + pub const RATIO_6: Gearset = Self::Blue; + + /// 100 rpm gearset (alias to `Self::Red`) + pub const RPM_100: Gearset = Self::Red; + /// 200 rpm (alias to `Self::Green`) + pub const RPM_200: Gearset = Self::Green; + /// 600 rpm (alias to `Self::Blue`) + pub const RPM_600: Gearset = Self::Blue; + + /// Rated max speed for a smart motor with a [`Red`] gearset. pub const MAX_RED_RPM: f64 = 100.0; - /// Rated speed for a smart motor with a green cartridge. + /// Rated speed for a smart motor with a [`Green`] gearset. pub const MAX_GREEN_RPM: f64 = 200.0; - /// Rated speed for a smart motor with a blue cartridge. + /// Rated speed for a smart motor with a [`Blue`] gearset. pub const MAX_BLUE_RPM: f64 = 600.0; + /// Number of encoder ticks per revolution for the [`Red`] gearset. + pub const RED_TICKS_PER_REVOLUTION: u32 = 1800; + /// Number of encoder ticks per revolution for the [`Green`] gearset. + pub const GREEN_TICKS_PER_REVOLUTION: u32 = 900; + /// Number of encoder ticks per revolution for the [`Blue`] gearset. + pub const BLUE_TICKS_PER_REVOLUTION: u32 = 300; + /// Get the rated maximum speed for this motor gearset. pub const fn max_rpm(&self) -> f64 { match self { @@ -573,6 +580,15 @@ impl Gearset { Self::Blue => Self::MAX_BLUE_RPM, } } + + /// Get the number of encoder ticks per revolution for this motor gearset. + pub const fn ticks_per_revolution(&self) -> u32 { + match self { + Self::Red => Self::RED_TICKS_PER_REVOLUTION, + Self::Green => Self::GREEN_TICKS_PER_REVOLUTION, + Self::Blue => Self::BLUE_TICKS_PER_REVOLUTION, + } + } } impl From for pros_sys::motor_gearset_e_t { From 460c982303efc03334b394aaa2c76b9fea635d5d Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:07:45 -0600 Subject: [PATCH 28/33] refactor: raw_position timestamp outvalue --- packages/pros-devices/src/smart/motor.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 839934e0..780379ae 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -3,7 +3,7 @@ use core::time::Duration; use bitflags::bitflags; -use pros_core::{bail_on, error::PortError, map_errno}; +use pros_core::{bail_on, error::PortError, map_errno, time::Instant}; use pros_sys::{PROS_ERR, PROS_ERR_F}; use snafu::Snafu; @@ -249,14 +249,24 @@ impl Motor { }))) } - /// Returns the raw position tick data recorded by the motor at a given timestamp. - pub fn raw_position(&self, timestamp: Duration) -> Result { - Ok(bail_on!(PROS_ERR, unsafe { + /// Returns the most recently recorded raw encoder tick data from the motor's IME + /// along with a timestamp of the internal clock of the motor indicating when the + /// data was recorded. + pub fn raw_position(&self) -> Result<(i32, Duration), MotorError> { + let timestamp = 0 as *mut u32; + + // PROS docs claim that this function gets the position *at* a recorded timestamp, + // but in reality the "timestamp" paramater is a mutable outvalue. The function + // outputs the most recent recorded posision AND the timestamp it was measured at, + // rather than a position at a requested timestamp. + let ticks = bail_on!(PROS_ERR, unsafe { pros_sys::motor_get_raw_position( self.port.index() as i8, - timestamp.as_millis() as *const u32, + timestamp, ) - })) + }); + + Ok((ticks, Duration::from_millis(unsafe { *timestamp } as u64))) } /// Returns the electrical current draw of the motor in amps. From d34a4f331328df26dd60939013446c6097401e07 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:08:53 -0600 Subject: [PATCH 29/33] chore: fmt :] --- packages/pros-devices/src/smart/motor.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 780379ae..9a912faa 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -260,10 +260,7 @@ impl Motor { // outputs the most recent recorded posision AND the timestamp it was measured at, // rather than a position at a requested timestamp. let ticks = bail_on!(PROS_ERR, unsafe { - pros_sys::motor_get_raw_position( - self.port.index() as i8, - timestamp, - ) + pros_sys::motor_get_raw_position(self.port.index() as i8, timestamp) }); Ok((ticks, Duration::from_millis(unsafe { *timestamp } as u64))) From 1d7cdf71e6d718089818363325eaded8b4495094 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:36:18 -0600 Subject: [PATCH 30/33] refactor: `SmartDeviceTimestamp` --- packages/pros-devices/src/smart/mod.rs | 22 ++++++++++++++++++++++ packages/pros-devices/src/smart/motor.rs | 8 ++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/pros-devices/src/smart/mod.rs b/packages/pros-devices/src/smart/mod.rs index e45ae7cf..ba0e2caf 100644 --- a/packages/pros-devices/src/smart/mod.rs +++ b/packages/pros-devices/src/smart/mod.rs @@ -41,6 +41,8 @@ use pros_core::{bail_on, error::PortError}; pub use rotation::RotationSensor; pub use vision::VisionSensor; +use core::fmt; + /// Defines common functionality shared by all smart port devices. pub trait SmartDevice { /// Get the index of the [`SmartPort`] this device is registered on. @@ -238,3 +240,23 @@ impl From for pros_sys::apix::v5_device_e_t { value as _ } } + +/// Represents a timestamp on a smart device's internal clock. This type offers +/// no guarantees that the device's clock is in sync with the internal clock of +/// the brain, and thus cannot be safely compared with [`pros_core::time::Instant`]s. +/// +/// There is additionally no guarantee that this is in sync with other smart devices, +/// or even the same device if a disconnect occurred causing the clock to reset. As such, +/// this is effectively a newtype wrapper of `u32`. +/// +/// # Precision +/// +/// This type has a precision of 1 millisecond. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SmartDeviceTimestamp(pub u32); + +impl fmt::Debug for SmartDeviceTimestamp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index 9a912faa..ad50d521 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -3,11 +3,11 @@ use core::time::Duration; use bitflags::bitflags; -use pros_core::{bail_on, error::PortError, map_errno, time::Instant}; +use pros_core::{bail_on, error::PortError, map_errno}; use pros_sys::{PROS_ERR, PROS_ERR_F}; use snafu::Snafu; -use super::{SmartDevice, SmartDeviceType, SmartPort}; +use super::{SmartDevice, SmartDeviceType, SmartDeviceTimestamp, SmartPort}; use crate::Position; /// The basic motor struct. @@ -252,7 +252,7 @@ impl Motor { /// Returns the most recently recorded raw encoder tick data from the motor's IME /// along with a timestamp of the internal clock of the motor indicating when the /// data was recorded. - pub fn raw_position(&self) -> Result<(i32, Duration), MotorError> { + pub fn raw_position(&self) -> Result<(i32, SmartDeviceTimestamp), MotorError> { let timestamp = 0 as *mut u32; // PROS docs claim that this function gets the position *at* a recorded timestamp, @@ -263,7 +263,7 @@ impl Motor { pros_sys::motor_get_raw_position(self.port.index() as i8, timestamp) }); - Ok((ticks, Duration::from_millis(unsafe { *timestamp } as u64))) + Ok((ticks, SmartDeviceTimestamp(unsafe { *timestamp }))) } /// Returns the electrical current draw of the motor in amps. From 10d8eaf7757b327be9e8ec7e62843fc3d1720dbc Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:39:00 -0600 Subject: [PATCH 31/33] =?UTF-8?q?chore:=20fmt=20=F0=9F=AA=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/pros-devices/src/smart/mod.rs | 4 ++-- packages/pros-devices/src/smart/motor.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/pros-devices/src/smart/mod.rs b/packages/pros-devices/src/smart/mod.rs index ba0e2caf..f528aa90 100644 --- a/packages/pros-devices/src/smart/mod.rs +++ b/packages/pros-devices/src/smart/mod.rs @@ -30,6 +30,8 @@ pub mod optical; pub mod rotation; pub mod vision; +use core::fmt; + pub use distance::DistanceSensor; pub use expander::AdiExpander; pub use gps::GpsSensor; @@ -41,8 +43,6 @@ use pros_core::{bail_on, error::PortError}; pub use rotation::RotationSensor; pub use vision::VisionSensor; -use core::fmt; - /// Defines common functionality shared by all smart port devices. pub trait SmartDevice { /// Get the index of the [`SmartPort`] this device is registered on. diff --git a/packages/pros-devices/src/smart/motor.rs b/packages/pros-devices/src/smart/motor.rs index ad50d521..ffcb1e2f 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -7,7 +7,7 @@ use pros_core::{bail_on, error::PortError, map_errno}; use pros_sys::{PROS_ERR, PROS_ERR_F}; use snafu::Snafu; -use super::{SmartDevice, SmartDeviceType, SmartDeviceTimestamp, SmartPort}; +use super::{SmartDevice, SmartDeviceTimestamp, SmartDeviceType, SmartPort}; use crate::Position; /// The basic motor struct. From ad92aa96051cc88bbe517c7cf3551acfddb2a685 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:00:35 -0500 Subject: [PATCH 32/33] chore: remove extraneous dlmalloc dep --- packages/pros/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index c268d471..97e368fc 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -26,9 +26,6 @@ pros-core = { version = "0.1.0", path = "../pros-core", optional = true } pros-math = { version = "0.1.0", path = "../pros-math", optional = true } pros-sys = { version = "0.7.0", path = "../pros-sys" } -[target.'cfg(target_arch = "wasm32")'.dependencies] -dlmalloc = { version = "0.2.4", features = ["global"] } - [features] default = ["async", "devices", "panic", "display_panics", "core", "math"] From 8fa3eafd965a9f82b75f84a88e2e64bc4cb18375 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:52:16 -0500 Subject: [PATCH 33/33] fix: typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8afeea..a8942371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,7 @@ Before releasing: - Added `AdiSolenoid`, a wrapper over `AdiDigitalOut` for actuating SMC pneumatic solenoids. (#61) - Added `AdiSwitch`, another `AdiDigitalOut` wrapper that abstracts bumper switches and limit switches. (#61) - Added `AdiLineTracker` for abstracting the EDR line tracker sensor. -- Implements TryFrom for Gearset. +- Implemented TryFrom for Gearset. - Adds support for getting brake modes from motors. (#66) ### Fixed