diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e6b0afd..a8942371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,13 +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 +- 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 @@ -40,6 +66,8 @@ 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. +- Implemented TryFrom for Gearset. +- Adds support for getting brake modes from motors. (#66) ### Fixed diff --git a/packages/pros-devices/Cargo.toml b/packages/pros-devices/Cargo.toml index 6d49ac81..52bbf80d 100644 --- a/packages/pros-devices/Cargo.toml +++ b/packages/pros-devices/Cargo.toml @@ -27,6 +27,10 @@ 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 + +[features] +dangerous_motor_tuning = [] diff --git a/packages/pros-devices/src/smart/imu.rs b/packages/pros-devices/src/smart/imu.rs index b60c5ba6..b76ef393 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::imu_get_status(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; } } diff --git a/packages/pros-devices/src/smart/mod.rs b/packages/pros-devices/src/smart/mod.rs index e45ae7cf..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; @@ -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 b6310001..ffcb1e2f 100644 --- a/packages/pros-devices/src/smart/motor.rs +++ b/packages/pros-devices/src/smart/motor.rs @@ -1,244 +1,456 @@ -//! Motors and gearsets. -//! -//! Once created motors 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; + +use bitflags::bitflags; 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, SmartDeviceTimestamp, SmartDeviceType, SmartPort}; use crate::Position; /// The basic motor struct. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Motor { port: SmartPort, + target: MotorControl, } -//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 { - /// Create a new motor on the given port with the given brake mode. - 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()) - ); - } +/// Represents a possible target for a [`Motor`]. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MotorControl { + /// Motor is braking using a [`BrakeMode`]. + Brake(BrakeMode), + + /// Motor is outputting a raw voltage. + Voltage(f64), + + /// 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(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Direction { + /// Motor rotates in the forward direction. + Forward, + + /// Motor rotates in the reverse direction. + Reverse, +} - Ok(Self { port }) +impl Direction { + /// Returns `true` if the level is [`Forward`]. + pub const fn is_forward(&self) -> bool { + match self { + Self::Forward => true, + Self::Reverse => false, + } } - /// Sets the gearset of the 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) - ); + /// Returns `true` if the level is [`Reverse`]. + pub const fn is_reverse(&self) -> bool { + match self { + Self::Forward => false, + Self::Reverse => true, } - Ok(()) } +} - /// Gets the gearset of the motor. - pub fn gearset(&self) -> Result { - Ok(unsafe { bail_on!(PROS_ERR, pros_sys::motor_get_gearing(self.port.index())) }.into()) +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, + direction: Direction, + ) -> Result { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_set_encoder_units(port.index() as i8, pros_sys::E_MOTOR_ENCODER_DEGREES) + }); + + let mut motor = Self { + port, + target: MotorControl::Voltage(0.0), + }; + + motor.set_gearset(gearset)?; + motor.set_direction(direction)?; + + Ok(motor) } - /// 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) - ); + /// 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: MotorControl) -> Result<(), MotorError> { + match target { + MotorControl::Brake(mode) => unsafe { + bail_on!( + PROS_ERR, + pros_sys::motor_set_brake_mode(self.port.index() as i8, mode.into()) + ); + bail_on!(PROS_ERR, pros_sys::motor_brake(self.port.index() as i8)); + }, + MotorControl::Velocity(rpm) => 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_velocity(self.port.index() as i8, rpm) + ); + }, + 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) => 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; 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) - ); - } - Ok(()) + /// Sets the motors target to a given [`BrakeMode`]. + pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorError> { + self.set_target(MotorControl::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); - } - unsafe { - bail_on!( - PROS_ERR, - 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(MotorControl::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(MotorControl::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_position_target( &mut self, position: Position, velocity: i32, ) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_absolute(self.port.index(), position.into_degrees(), velocity) - ); - }; - Ok(()) + self.set_target(MotorControl::Position(position, velocity)) } - /// Moves the motor to a position relative to the current position. - /// units for velocity is RPM. - pub fn set_position_relative( - &mut self, - position: Position, - velocity: i32, - ) -> Result<(), MotorError> { - unsafe { - bail_on!( - PROS_ERR, - pros_sys::motor_move_relative(self.port.index(), position.into_degrees(), 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 update_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorError> { + bail_on!(PROS_ERR, unsafe { + pros_sys::motor_modify_profiled_velocity(self.port.index() as i8, velocity) + }); + + match self.target { + MotorControl::Position(position, _) => { + self.target = MotorControl::Position(position, velocity) + } + _ => {} } + + Ok(()) + } + + /// Get the current [`MotorControl`] value that the motor is attempting to use. + pub fn target(&self) -> MotorControl { + 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_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() 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() as i8) + })) + } + /// 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() as i8) + })) } /// 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() as i8) + })) } /// 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() as i8) + }); 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() as i8) + }))) } - /// Returns the current draw of the motor. - pub fn current_draw(&self) -> Result { + /// 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, SmartDeviceTimestamp), 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) + }); + + Ok((ticks, SmartDeviceTimestamp(unsafe { *timestamp }))) + } + + /// 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) + } + + /// Gets the efficiency of the motor from a range of [0.0, 1.0]. + /// + /// 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. /// 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() as i8) + }); 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() as i8, 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> { + /// 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_zero_position(self.port.index(), position.into_degrees()) + pros_sys::motor_set_current_limit(self.port.index() as i8, (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> { + /// 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 { - pros_sys::motor_set_brake_mode(self.port.index(), brake_mode.into()) + // 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(()) } - //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 { - let bit_flags = bail_on!(PROS_ERR as _, unsafe { - pros_sys::motor_get_flags(self.port.index()) + /// 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() as i8) + }) 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 { + let bits = bail_on!(PROS_ERR as u32, unsafe { + pros_sys::motor_get_flags(self.port.index() as i8) + }); + + // 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); + } + + Ok(MotorStatus::from_bits_retain(bits)) + } + + /// 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() as i8) }); - Ok(bit_flags.into()) + + 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()?.contains(MotorFaults::OVER_TEMPERATURE)) + } + + /// Check if the motor's overcurrent flag is set. + pub fn is_over_current(&self) -> Result { + 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()?.contains(MotorFaults::DRIVER_FAULT)) } - /// Reverse this motor by multiplying all input by -1. - pub fn set_reversed(&mut self, reversed: bool) -> Result<(), MotorError> { + /// Check if the motor's H-bridge has an overucrrent fault. + pub fn is_driver_over_current(&self) -> Result { + Ok(self.faults()?.contains(MotorFaults::OVER_CURRENT)) + } + + /// 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() as i8, direction.is_reverse()) }); Ok(()) } - /// Check if this motor has been reversed. - pub fn reversed(&self) -> bool { - unsafe { pros_sys::motor_is_reversed(self.port.index()) == 1 } + /// 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() as i8) + }) == 1; + + Ok(match reversed { + false => Direction::Forward, + true => Direction::Reverse, + }) } - /// 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 + /// + /// 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")] + 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() as i8, constants.into()) + }); + Ok(()) + } + + /// Adjusts the internal tuning constants of the motor when using position control. + /// + /// # 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")] + 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() as i8, constants.into()) + }); + Ok(()) } } @@ -253,45 +465,76 @@ 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. - 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 _ } } -/// 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, +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; + + /// The motor is over current. + const OVER_CURRENT = pros_sys::E_MOTOR_FAULT_OVER_CURRENT; + + /// The motor's H-bridge has encountered a fault. + const DRIVER_FAULT = pros_sys::E_MOTOR_FAULT_DRIVER_FAULT; + + /// The motor's H-bridge is over current. + const DRIVER_OVER_CURRENT = pros_sys::E_MOTOR_FAULT_DRV_OVER_CURRENT; + } } -//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, - } +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 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 ZERO_POSITION = pros_sys::E_MOTOR_FLAGS_ZERO_POSITION; + + /// Cannot currently communicate to the motor + const BUSY = pros_sys::E_MOTOR_FLAGS_BUSY; } } @@ -308,51 +551,134 @@ 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; + /// 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`] gearset. + pub const MAX_GREEN_RPM: f64 = 200.0; + /// 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 { + Self::Red => Self::MAX_RED_RPM, + Self::Green => Self::MAX_GREEN_RPM, + 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 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!(), - } + }) } } -#[derive(Debug)] -/// A future that completes when the motor reports that it has stopped. -/// Created by [`Motor::wait_until_stopped`] -pub struct MotorStoppedFuture<'a> { - motor: &'a Motor, +/// Holds the information about a Motor's position or velocity PID controls. +/// +/// # 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 { + /// 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. + /// + /// 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 tolerance: f64, + + /// The rate at which the PID computation is run in ms. + pub sample_rate: Duration, } -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.get_state()?.stopped { - true => core::task::Poll::Ready(Ok(())), - false => { - cx.waker().wake_by_ref(); - core::task::Poll::Pending - } +#[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.tolerance, + value.sample_rate.as_millis() as f64, + ) } } } @@ -360,10 +686,15 @@ 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, + + /// 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 { /// The source of the error. source: PortError, @@ -371,6 +702,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; 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; } diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index 4740916b..97e368fc 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -40,3 +40,5 @@ math = ["dep:pros-math"] panic = ["dep:pros-panic"] display_panics = ["pros-panic/display_panics"] + +dangerous-motor-tuning = ["pros-devices/dangerous_motor_tuning"] diff --git a/packages/pros/examples/accessories.rs b/packages/pros/examples/accessories.rs index fb19faba..6a032819 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, BrakeMode::Brake).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; @@ -54,16 +51,13 @@ 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().get_state().unwrap_or_default().stopped - ); + 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. // 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 +66,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!( diff --git a/packages/pros/examples/dynamic_peripherals.rs b/packages/pros/examples/dynamic_peripherals.rs index 4b4c321a..c785931a 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) -> Result { let motor = Motor::new( self.peripherals.take_smart_port(10).unwrap(), - BrakeMode::Brake, + Gearset::Green, + false, )?; motor.wait_until_stopped().await?; Ok(()) 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,