Skip to main content

moteus_protocol/
mode.rs

1// Copyright 2026 mjbots Robotic Systems, LLC.  info@mjbots.com
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Mode and state enumerations for moteus controllers.
16
17use num_enum::{IntoPrimitive, TryFromPrimitive};
18
19/// The operating mode of a moteus controller.
20///
21/// This corresponds to the value in the `Register::Mode` register.
22#[non_exhaustive]
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, IntoPrimitive, TryFromPrimitive)]
24#[repr(u8)]
25pub enum Mode {
26    /// Motor is stopped and unpowered
27    #[default]
28    Stopped = 0,
29    /// Controller is in a fault state
30    Fault = 1,
31    /// Controller is enabling (transitioning to active)
32    Enabling = 2,
33    /// Controller is calibrating
34    Calibrating = 3,
35    /// Calibration is complete
36    CalibrationComplete = 4,
37    /// Raw PWM mode
38    Pwm = 5,
39    /// Voltage mode
40    Voltage = 6,
41    /// Voltage FOC mode
42    VoltageFoc = 7,
43    /// Voltage DQ mode
44    VoltageDq = 8,
45    /// Current (torque) mode
46    Current = 9,
47    /// Position mode (primary operating mode)
48    Position = 10,
49    /// Position timeout (watchdog expired)
50    Timeout = 11,
51    /// Zero velocity mode (hold position with damping)
52    ZeroVelocity = 12,
53    /// Stay within bounds mode
54    StayWithin = 13,
55    /// Measure inductance mode (calibration)
56    MeasureInd = 14,
57    /// Brake mode (short motor windings)
58    Brake = 15,
59}
60
61impl core::fmt::Display for Mode {
62    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63        match self {
64            Mode::Stopped => write!(f, "stopped"),
65            Mode::Fault => write!(f, "fault"),
66            Mode::Enabling => write!(f, "enabling"),
67            Mode::Calibrating => write!(f, "calibrating"),
68            Mode::CalibrationComplete => write!(f, "calibration complete"),
69            Mode::Pwm => write!(f, "pwm"),
70            Mode::Voltage => write!(f, "voltage"),
71            Mode::VoltageFoc => write!(f, "voltage foc"),
72            Mode::VoltageDq => write!(f, "voltage dq"),
73            Mode::Current => write!(f, "current"),
74            Mode::Position => write!(f, "position"),
75            Mode::Timeout => write!(f, "timeout"),
76            Mode::ZeroVelocity => write!(f, "zero velocity"),
77            Mode::StayWithin => write!(f, "stay within"),
78            Mode::MeasureInd => write!(f, "measure inductance"),
79            Mode::Brake => write!(f, "brake"),
80        }
81    }
82}
83
84impl Mode {
85    /// Returns `true` if the controller is in an error state.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use moteus_protocol::Mode;
91    ///
92    /// let mode = Mode::Position;
93    /// assert!(mode.is_active());
94    /// assert!(!mode.is_error());
95    ///
96    /// let fault = Mode::Fault;
97    /// assert!(fault.is_error());
98    /// assert!(!fault.is_active());
99    /// ```
100    pub fn is_error(&self) -> bool {
101        matches!(self, Mode::Fault | Mode::Timeout)
102    }
103
104    /// Returns `true` if the controller is actively controlling the motor.
105    pub fn is_active(&self) -> bool {
106        matches!(
107            self,
108            Mode::Position
109                | Mode::Current
110                | Mode::VoltageFoc
111                | Mode::ZeroVelocity
112                | Mode::StayWithin
113                | Mode::Brake
114        )
115    }
116}
117
118impl From<Mode> for i8 {
119    fn from(mode: Mode) -> i8 {
120        let v: u8 = mode.into();
121        v as i8
122    }
123}
124
125/// The homing/rezero state of the controller.
126///
127/// This indicates the reference frame for position values.
128#[non_exhaustive]
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, IntoPrimitive, TryFromPrimitive)]
130#[repr(u8)]
131pub enum HomeState {
132    /// Position is relative to power-on position
133    #[default]
134    Relative = 0,
135    /// Position is relative to rotor position
136    Rotor = 1,
137    /// Position is absolute (output-referenced)
138    Output = 2,
139}
140
141impl core::fmt::Display for HomeState {
142    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143        match self {
144            HomeState::Relative => write!(f, "relative"),
145            HomeState::Rotor => write!(f, "rotor"),
146            HomeState::Output => write!(f, "output"),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_mode_try_from() {
157        assert_eq!(Mode::try_from(0), Ok(Mode::Stopped));
158        assert_eq!(Mode::try_from(10), Ok(Mode::Position));
159        assert!(Mode::try_from(255).is_err());
160    }
161
162    #[test]
163    fn test_mode_into() {
164        let v: u8 = Mode::Position.into();
165        assert_eq!(v, 10);
166    }
167
168    #[test]
169    fn test_mode_is_error() {
170        assert!(Mode::Fault.is_error());
171        assert!(Mode::Timeout.is_error());
172        assert!(!Mode::Position.is_error());
173        assert!(!Mode::Stopped.is_error());
174    }
175
176    #[test]
177    fn test_mode_is_active() {
178        assert!(Mode::Position.is_active());
179        assert!(Mode::Current.is_active());
180        assert!(Mode::Brake.is_active());
181        assert!(!Mode::Stopped.is_active());
182        assert!(!Mode::Fault.is_active());
183    }
184
185    #[test]
186    fn test_home_state_try_from() {
187        assert_eq!(HomeState::try_from(0), Ok(HomeState::Relative));
188        assert_eq!(HomeState::try_from(2), Ok(HomeState::Output));
189        assert!(HomeState::try_from(3).is_err());
190    }
191
192    #[test]
193    fn test_home_state_into() {
194        let v: u8 = HomeState::Output.into();
195        assert_eq!(v, 2);
196    }
197}