pololu-smc 0.2.1

A driver for the Pololu Simple Motor Controller G2
Documentation
//! This crate provides a user-friendly driver for the Pololu Simple Motor Controller G2, for use on embedded hardware.
//!
//! It uses traits from the [`embedded_hal`] crate in order to be independent from end user hardware.
//! Currently only the I<sup>2</sup>C protocol is implemented.
//!
//! This documentation is heavily based on the official [user's guide for the controller](https://www.pololu.com/docs/pdf/0J77/simple_motor_controller_g2.pdf), published by Pololu.
//! The aim is for this documentation to contain the basic information required to use the controller, but please consult the user's guide
//! in order to fully understand the capabilities and limitations of the hardware and the various protocols.
//!
//! Example
//!```
//! # use embedded_hal::blocking::i2c::{Write, WriteRead};
//! # #[derive(Debug)]
//! # struct Error {}
//! # struct Test {}
//! # impl Write for Test {
//! #     type Error = Error;
//! #     fn write(&mut self, _: u8, _: &[u8]) -> std::result::Result<(), <Self as Write>::Error> {Ok(())}
//! # }
//! # impl WriteRead for Test {
//! #     type Error = Error;
//! #     fn write_read(&mut self, _: u8, _: &[u8], _: &mut [u8]) -> std::result::Result<(), <Self as WriteRead>::Error> {Ok(())}
//! # }
//! use pololu_smc::{SimpleMotorController, Command};
//!
//! // ...
//!
//! # fn main() -> std::result::Result<(), Error> {
//! # let mut interface = Test{};
//! let mut controller = SimpleMotorController::new(interface, 0x12);
//!
//! let errors = controller.get_error_status()?;
//! # assert_eq!(errors.safe_start_violation, false);
//! # assert_eq!(errors.required_channel_invalid, false);
//! # assert_eq!(errors.serial_error, false);
//! # assert_eq!(errors.command_timeout, false);
//! # assert_eq!(errors.limit_kill_switch, false);
//! # assert_eq!(errors.low_vin, false);
//! # assert_eq!(errors.high_vin, false);
//! # assert_eq!(errors.over_temperature, false);
//! # assert_eq!(errors.motor_driver_error, false);
//! # assert_eq!(errors.err_line_high, false);
//!
//! controller.send_command(Command::ExitSafeStart)?;
//! controller.send_command(Command::MotorFwd{speed: 500})?;
//! # Ok(())
//! # }
//!```
#![warn(missing_docs)]
#![no_std]

pub mod command;
pub mod variable_types;

pub use command::{Command as Command, FirmwareVersion as FirmwareVersion, MotorLimitKind as MotorLimitKind, MotorLimitResponse as MotorLimitResponse};
pub use variable_types::{BrakeAmount as BrakeAmount, Errors as Errors, Limits as Limits, ResetSource as ResetSource};

/// Represents a single physical motor controller.
///
/// The type parameter `T` should be a type that implements the appropriate traits for the desired communications interface.
///   * For I<sup>2</sup>C:
///     - [`embedded_hal::blocking::i2c::Write`]
///     - [`embedded_hal::blocking::i2c::WriteRead`]
pub struct SimpleMotorController<T> {
    /// The [`embedded_hal`] interface to use for communication.
    interface: T,
    /// The device number of this controller, in order to separate different controllers using the same bus.
    pub device_number: u8,
}

/// Maps the variables that can be read from the controller to their identifiers in the protocol.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
enum Variable {
    /// Indicates the errors that are currently stopping the motor.
    /// Returns a value of [`VariableValue::Errors`]
    ErrorStatus = 0,
    /// Indicates which errors have occurred since last reading this variable.
    /// Returns a value of [`VariableValue::Errors`]
    ErrorsOccurred = 1,
    /// Indicates which serial errors have occurred since last reading this variable.
    /// Returns a value of [`VariableValue::SerialErrors`]
    SerialErrorsOccurred = 2,
    /// Indicates things that are currently limiting the motor controller.
    /// Returns a value of [`VariableValue::Limits`]
    LimitStatus = 3,

    /// Positive pulse width of signal on RC channel 1. Unit: 0.25 μs. 0xFFFF if no valid signal detected.
    RC1UnlimitedRaw = 4,
    /// Positive pulse width of signal on RC channel 1. Unit: 0.25 μs. 0xFFFF if no valid signal detected or if outside error max/error min channel calibration settings.
    RC1Raw = 5,
    /// Scaled version of the RC1 raw value (based on RC channel 1 calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    RC1Scaled = 6,
    /// Positive pulse width of signal on RC channel 2. Unit: 0.25 μs. 0xFFFF if no valid signal detected.
    RC2UnlimitedRaw = 8,
    /// Positive pulse width of signal on RC channel 2. Unit: 0.25 μs. 0xFFFF if no valid signal detected or if outside error max/error min channel calibration settings.
    RC2Raw = 9,
    /// Scaled version of the RC2 raw value (based on RC channel 2 calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    RC2Scaled = 10,

    /// The 12-bit ADC reading of analog channel 1. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected.
    AN1UnlimitedRaw = 12,
    /// The 12-bit ADC reading of analog channel 1. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected or outside of the error max/error min channel calibration settings.
    AN1Raw = 13,
    /// The scaled version of the AN1 raw value (based on analog channel 1 calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    AN1Scaled = 14,
    /// The 12-bit ADC reading of analog channel 2. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected.
    AN2UnlimitedRaw = 16,
    /// The 12-bit ADC reading of analog channel 2. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected or outside of the error max/error min channel calibration settings.
    AN2Raw = 17,
    /// The scaled version of the AN2 raw value (based on analog channel 2 calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    AN2Scaled = 18,

    /// Motor target speed (-3200 to 3200).
    TargetSpeed = 20,
    /// Current speed of the motor (-3200 to 3200).
    Speed = 21,
    /// When speed=0, this variable indicates whether the controller is braking or not.
    BrakeAmount = 22,
    /// Measured voltage on the VIN pin, in mV.
    InputVoltage = 23,
    /// Board temperature in 0.1 °C, measured at a separate location from [`Variable::TemperatureB`]. Temperatures below freezing are reported as 0. Errors measuring temperature are reported as 3000.
    TemperatureA = 24,
    /// Board temperature in 0.1 °C, measured at a separate location from [`Variable::TemperatureA`]. Temperatures below freezing are reported as 0. Errors measuring temperature are reported as 3000.
    TemperatureB = 25,
    /// If there is a valid signal on RC1, this contains the signal period in 0.1ms. Otherwise, this has value 0.
    RCPeriod = 26,
    /// Value of the controller's baud rate register, in seconds per 72 000 000 bits.
    BaudRate = 27,
    /// The two lower bytes of the number of milliseconds that have elapsed since the controller was last reset or powered up.
    UptimeLow = 28,
    /// The two upper bytes of the number of milliseconds that have elapsed since the controller was last reset or powered up.
    UptimeHigh = 29,

    /// Maximum allowed motor speed in the forward direction, 0 to 3200.
    MaxSpeedFwd = 30,
    /// Maximum allowed motor acceleration in the forward direction, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    MaxAccFwd = 31,
    /// Maximum allowed motor deceleration from the forward direction, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    MaxDecFwd = 32,
    /// Time spent braking (at speed 0) in ms when transitioning from forward to reverse.
    BrakeDurationFwd = 33,
    /// Minimum allowed motor speed in the forward direction, 0 to 3200.
    StartingSpeedFwd = 34,
    /// Maximum allowed motor speed in the reverse direction, 0 to 3200.
    MaxSpeedRev = 36,
    /// Maximum allowed motor acceleration in the reverse direction, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    MaxAccRev = 37,
    /// Maximum allowed motor deceleration from the reverse direction, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    MaxDecRev = 38,
    /// Time spent braking (at speed 0) in ms when transitioning from reverse to forward.
    BrakeDurationRev = 39,
    /// Minimum allowed motor speed in the reverse direction, 0 to 3200.
    StartingSpeedRev = 40,
    /// The hardware current limit currently being used. See section 5.2 in the official user's guide for instructions on how to interpret the units.
    CurrentLimit = 42,
    /// Raw motor current measurement. See section 5.2 in the official user's guide for instructions on how to interpret the units.
    RawCurrent = 43,
    /// Measurement of the motor current in mA.
    Current = 44,
    /// The number of consecutive 10ms time periods in which the hardware current limiting has activated.
    CurrentLimitingConsecutiveCount = 45,
    /// The number of 10 ms time periods in which the hardware current limit has activated since the last time this
    /// variable was read.
    CurrentLimitingOccurenceCount = 46,

    /// Flags indicating the source of the last board reset.
    /// Returns a value of [`VariableValue::ResetSource`]
    ResetFlags = 127,
}

/// A channel of measurement, either RC or analog
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Channel {
    Ch1,
    Ch2,
}

/// A direction of movement
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Direction {
    Forward,
    Reverse,
}

impl<T> SimpleMotorController<T> {
    /// Constructs a new controller using a specific interface and device number.
    pub fn new(interface: T, device_number: u8) -> SimpleMotorController<T> {
        SimpleMotorController {
            interface,
            device_number,
        }
    }
}

impl<T> SimpleMotorController<T>
where
    T: embedded_hal::blocking::i2c::Write,
{
    /// Sends a given command to the controller. No response is provided.
    pub fn send_command(&mut self, cmd: Command) -> Result<(), T::Error> {
        match cmd {
            Command::ExitSafeStart | Command::StopMotor => {
                self.interface.write(self.device_number, &[cmd.get_id()])?
            }
            Command::MotorFwd { speed } | Command::MotorRev { speed } => self.interface.write(
                self.device_number,
                &[cmd.get_id(), (speed % 32) as u8, (speed / 32) as u8],
            )?,
            Command::MotorFwd7bit { speed } | Command::MotorRev7bit { speed } => self
                .interface
                .write(self.device_number, &[cmd.get_id(), speed])?,
            Command::MotorBrake { brake_amount } => self
                .interface
                .write(self.device_number, &[cmd.get_id(), brake_amount])?,
            Command::SetCurrentLimit { value } => self.interface.write(
                self.device_number,
                &[cmd.get_id(), (value % 128) as u8, (value / 128) as u8],
            )?,
        };
        Ok(())
    }
}

impl<T> SimpleMotorController<T>
where
    T: embedded_hal::blocking::i2c::WriteRead,
{
    /// Performs a request for a raw u16 value of a variable.
    fn get_variable_raw(&mut self, variable: Variable) -> Result<u16, T::Error> {
        let mut buf: [u8; 2] = [0, 0];
        self.interface
            .write_read(self.device_number, &[0xa1, variable as u8], &mut buf)?;

        Ok(u16::from(buf[0]) | (u16::from(buf[1])) << 8)
    }

    /// Returns the errors that are currently stopping the motor
    pub fn get_error_status(&mut self) -> Result<Errors, T::Error> {
        Ok(Errors::from(self.get_variable_raw(Variable::ErrorStatus)?))
    }

    /// Like [`SimpleMotorController::get_error_status`], but only returns the new errors that occurred after the
    /// last time this variable was read.
    pub fn get_new_errors(&mut self) -> Result<Errors, T::Error> {
        Ok(Errors::from(
            self.get_variable_raw(Variable::ErrorsOccurred)?,
        ))
    }

    /// Returns the new serial errors that occurred after the
    /// last time this variable was read.
    pub fn get_new_serial_errors(&mut self) -> Result<Errors, T::Error> {
        Ok(Errors::from(
            self.get_variable_raw(Variable::SerialErrorsOccurred)?,
        ))
    }

    /// Returns the things that are currently limiting the operation of the motor controller in some way.
    pub fn get_limit_status(&mut self) -> Result<Limits, T::Error> {
        Ok(Limits::from(self.get_variable_raw(Variable::LimitStatus)?))
    }

    /// Returns positive pulse width of signal on an RC channel. Unit: 0.25 μs. 0xFFFF if no valid signal detected.
    pub fn get_rc_unlimited_raw(&mut self, ch: Channel) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::RC1UnlimitedRaw,
            Channel::Ch2 => Variable::RC2UnlimitedRaw,
        })?)
    }

    /// Returns positive pulse width of signal on an RC channel. Unit: 0.25 μs. 0xFFFF if no valid signal detected or if outside error max/error min channel calibration settings.
    pub fn get_rc_raw(&mut self, ch: Channel) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::RC1Raw,
            Channel::Ch2 => Variable::RC2Raw,
        })?)
    }

    /// Returns scaled version of the RC raw value (based on RC channel calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    pub fn get_rc_scaled(&mut self, ch: Channel) -> Result<i16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::RC1Scaled,
            Channel::Ch2 => Variable::RC2Scaled,
        })? as i16)
    }

    /// Returns the 12-bit ADC reading of an analog channel. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected.
    pub fn get_analog_unlimited_raw(&mut self, ch: Channel) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::AN1UnlimitedRaw,
            Channel::Ch2 => Variable::AN2UnlimitedRaw,
        })?)
    }

    /// Returns the 12-bit ADC reading of an analog channel. 0 (0 V) to 4095 (3.3 V). 0xFFFF if the input is disconnected or outside of the error max/error min channel calibration settings.
    pub fn get_analog_raw(&mut self, ch: Channel) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::AN1Raw,
            Channel::Ch2 => Variable::AN2Raw,
        })?)
    }

    /// Returns the scaled version of the analog raw value (based on analog channel calibration settings). 0 if the raw value is 0xFFFF, otherwise from -3200 to 3200.
    pub fn get_analog_scaled(&mut self, ch: Channel) -> Result<i16, T::Error> {
        Ok(self.get_variable_raw(match ch {
            Channel::Ch1 => Variable::AN1Scaled,
            Channel::Ch2 => Variable::AN2Scaled,
        })? as i16)
    }

    /// Returns motor target speed (-3200 to 3200).
    pub fn get_target_speed(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::TargetSpeed)?)
    }

    /// Returns current speed of the motor (-3200 to 3200).
    pub fn get_speed(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::Speed)?)
    }

    /// When speed is 0, the returnev value indicates whether the controller is braking or not.
    pub fn get_brake_amount(&mut self) -> Result<BrakeAmount, T::Error> {
        Ok(BrakeAmount::from(
            self.get_variable_raw(Variable::BrakeAmount)?,
        ))
    }

    /// Returns measured voltage on the VIN pin, in mV.
    pub fn get_input_voltage(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::InputVoltage)?)
    }

    /// Returns board temperature in 0.1 °C, measured at a separate location from [`SimpleMotorController::get_temperature_b`]. Temperatures below freezing are reported as 0. Errors measuring temperature are reported as 3000.
    pub fn get_temperature_a(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::TemperatureA)?)
    }

    /// Returns board temperature in 0.1 °C, measured at a separate location from [`SimpleMotorController::get_temperature_a`]. Temperatures below freezing are reported as 0. Errors measuring temperature are reported as 3000.
    pub fn get_temperature_b(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::TemperatureB)?)
    }

    /// If there is a valid signal on RC1, this returns the signal period in 0.1ms. Otherwise, this returns value 0.
    pub fn get_rc_period(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::RCPeriod)?)
    }

    /// Returns the value of the controller's baud rate register, in seconds per 72 000 000 bits.
    pub fn get_baud_rate(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::BaudRate)?)
    }

    /// Returns the two lower bytes of the number of milliseconds that have elapsed since the controller was last reset or powered up.
    pub fn get_uptime_low(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::UptimeLow)?)
    }

    /// Returns the two upper bytes of the number of milliseconds that have elapsed since the controller was last reset or powered up.
    pub fn get_uptime_high(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::UptimeHigh)?)
    }

    /// Returns the number of milliseconds since the last reset, by combining the results of [`SimpleMotorController::get_uptime_low`] and [`SimpleMotorController::get_uptime_high`].
    pub fn get_uptime(&mut self) -> Result<u32, T::Error> {
        Ok((u32::from(self.get_uptime_high()?) << 16) + u32::from(self.get_uptime_low()?))
    }

    /// Returns maximum allowed motor speed in direction `dir`, 0 to 3200.
    pub fn get_max_speed(&mut self, dir: Direction) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match dir {
            Direction::Forward => Variable::MaxSpeedFwd,
            Direction::Reverse => Variable::MaxSpeedRev,
        })?)
    }

    /// Returns maximum allowed motor acceleration in direction `dir`, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    pub fn get_max_acceleration(&mut self, dir: Direction) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match dir {
            Direction::Forward => Variable::MaxAccFwd,
            Direction::Reverse => Variable::MaxAccRev,
        })?)
    }

    /// Returns maximum allowed motor deceleration from direction `dir`, in Δspeed per update period. Value range 0 to 3200. 0 means no limit.
    pub fn get_max_deceleration(&mut self, dir: Direction) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match dir {
            Direction::Forward => Variable::MaxDecFwd,
            Direction::Reverse => Variable::MaxDecRev,
        })?)
    }

    /// Returns time spent braking (at speed 0) in ms when transitioning from direction `dir` to the other.
    pub fn get_brake_duration(&mut self, dir: Direction) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match dir {
            Direction::Forward => Variable::BrakeDurationFwd,
            Direction::Reverse => Variable::BrakeDurationRev,
        })?)
    }

    /// Returns minimum allowed motor speed in direction `dir`, 0 to 3200.
    pub fn get_starting_speed(&mut self, dir: Direction) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(match dir {
            Direction::Forward => Variable::StartingSpeedFwd,
            Direction::Reverse => Variable::StartingSpeedRev,
        })?)
    }

    /// Returns the hardware current limit currently being used. See section 5.2 in the official user's guide for instructions on how to interpret the units.
    pub fn get_current_limit(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::CurrentLimit)?)
    }

    /// Returns raw motor current measurement. See section 5.2 in the official user's guide for instructions on how to interpret the units.
    pub fn get_raw_current(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::RawCurrent)?)
    }

    /// Returns a measurement of the motor current in mA.
    pub fn get_current(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::Current)?)
    }

    /// Returns the number of consecutive 10ms time periods in which the hardware current limiting has activated.
    pub fn get_current_limiting_consecutive_count(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::CurrentLimitingConsecutiveCount)?)
    }

    /// Returns the number of 10 ms time periods in which the hardware current limit has activated since the last time 
    /// this variable was read.
    pub fn get_current_limiting_occurence_count(&mut self) -> Result<u16, T::Error> {
        Ok(self.get_variable_raw(Variable::CurrentLimitingOccurenceCount)?)
    }

    /// Indicates the source of the last board reset.
    pub fn get_reset_flags(&mut self) -> Result<ResetSource, T::Error> {
        Ok(ResetSource::from(
            self.get_variable_raw(Variable::ResetFlags)?,
        ))
    }

    /// Sends a command to temporarily set a certain motor limit to some value, until the board is reset.
    pub fn set_motor_limit(
        &mut self,
        limit: MotorLimitKind,
        value: u16,
    ) -> Result<MotorLimitResponse, T::Error> {
        let mut buf: [u8; 1] = [0];
        self.interface.write_read(
            self.device_number,
            &[0xa2, limit as u8, (value % 128) as u8, (value / 128) as u8],
            &mut buf,
        )?;

        match buf[0] {
            0 => Ok(MotorLimitResponse::Ok),
            1 => Ok(MotorLimitResponse::UnableForward),
            2 => Ok(MotorLimitResponse::UnableReverse),
            3 => Ok(MotorLimitResponse::Unable),
            _ => Ok(MotorLimitResponse::Unknown),
        }
    }

    /// Sends a command to read the firmware version and product id from the controller.
    pub fn get_firmware_version(&mut self) -> Result<FirmwareVersion, T::Error> {
        let mut buf: [u8; 4] = [0, 0, 0, 0];
        self.interface
            .write_read(self.device_number, &[0xc2], &mut buf)?;

        Ok(FirmwareVersion {
            product_id: u16::from(buf[0]) + ((u16::from(buf[1])) << 8),
            major_bcd: buf[2],
            minor_bcd: buf[3],
        })
    }
}