Skip to main content

moteus_protocol/
register.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//! Register definitions for moteus controllers.
16//!
17//! The full list can be found at:
18//! <https://mjbots.github.io/moteus/protocol/registers/>
19
20use num_enum::{IntoPrimitive, TryFromPrimitive};
21
22/// Registers exposed for reading or writing from a moteus controller.
23#[non_exhaustive]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
25#[repr(u16)]
26pub enum Register {
27    // === Telemetry Registers (0x000 - 0x00f) ===
28    /// Current mode of operation
29    Mode = 0x000,
30    /// Output shaft position in revolutions
31    Position = 0x001,
32    /// Output shaft velocity in revolutions/second
33    Velocity = 0x002,
34    /// Motor torque in Nm
35    Torque = 0x003,
36    /// Q-axis current in A
37    QCurrent = 0x004,
38    /// D-axis current in A
39    DCurrent = 0x005,
40    /// Absolute encoder position (if configured)
41    AbsPosition = 0x006,
42    /// Electrical power in W
43    Power = 0x007,
44
45    /// Motor temperature in C
46    MotorTemperature = 0x00a,
47    /// 1 if trajectory is complete
48    TrajectoryComplete = 0x00b,
49    /// Home/rezero state
50    HomeState = 0x00c,
51    /// Input voltage in V
52    Voltage = 0x00d,
53    /// Board temperature in C
54    Temperature = 0x00e,
55    /// Fault code (0 = no fault)
56    Fault = 0x00f,
57
58    // === PWM Registers (0x010 - 0x012) ===
59    /// PWM duty cycle for phase A
60    PwmPhaseA = 0x010,
61    /// PWM duty cycle for phase B
62    PwmPhaseB = 0x011,
63    /// PWM duty cycle for phase C
64    PwmPhaseC = 0x012,
65
66    // === Phase Voltage Registers (0x014 - 0x016) ===
67    /// Phase A voltage in V
68    VoltagePhaseA = 0x014,
69    /// Phase B voltage in V
70    VoltagePhaseB = 0x015,
71    /// Phase C voltage in V
72    VoltagePhaseC = 0x016,
73
74    // === VFOC Registers (0x018 - 0x01e) ===
75    /// Voltage FOC theta angle
76    VFocTheta = 0x018,
77    /// Voltage FOC voltage
78    VFocVoltage = 0x019,
79    /// Voltage DQ D component
80    VoltageDqD = 0x01a,
81    /// Voltage DQ Q component
82    VoltageDqQ = 0x01b,
83    /// Command Q current for current mode
84    CommandQCurrent = 0x01c,
85    /// Command D current for current mode
86    CommandDCurrent = 0x01d,
87    /// Voltage FOC theta rate
88    VFocThetaRate = 0x01e,
89
90    // === Position Mode Command Registers (0x020 - 0x02d) ===
91    /// Target position in revolutions
92    CommandPosition = 0x020,
93    /// Target velocity in revolutions/second
94    CommandVelocity = 0x021,
95    /// Feedforward torque in Nm
96    CommandFeedforwardTorque = 0x022,
97    /// Kp scale (0-1)
98    CommandKpScale = 0x023,
99    /// Kd scale (0-1)
100    CommandKdScale = 0x024,
101    /// Maximum torque in Nm
102    CommandPositionMaxTorque = 0x025,
103    /// Stop position (for trajectories)
104    CommandStopPosition = 0x026,
105    /// Watchdog timeout in seconds
106    CommandTimeout = 0x027,
107    /// Velocity limit in revolutions/second
108    CommandVelocityLimit = 0x028,
109    /// Acceleration limit in revolutions/second^2
110    CommandAccelLimit = 0x029,
111    /// Fixed voltage override
112    CommandFixedVoltageOverride = 0x02a,
113    /// Current limit scale
114    CommandIlimitScale = 0x02b,
115    /// Fixed current override
116    CommandFixedCurrentOverride = 0x02c,
117    /// Ignore position bounds flag
118    CommandIgnorePositionBounds = 0x02d,
119
120    // === Position Torque Reporting Registers (0x030 - 0x034) ===
121    /// Position Kp torque
122    PositionKp = 0x030,
123    /// Position Ki torque
124    PositionKi = 0x031,
125    /// Position Kd torque
126    PositionKd = 0x032,
127    /// Position feedforward torque
128    PositionFeedforward = 0x033,
129    /// Position total torque
130    PositionCommand = 0x034,
131
132    // === Control Registers (0x038 - 0x03d) ===
133    /// Control position target
134    ControlPosition = 0x038,
135    /// Control velocity target
136    ControlVelocity = 0x039,
137    /// Control torque target
138    ControlTorque = 0x03a,
139    /// Position error
140    ControlPositionError = 0x03b,
141    /// Velocity error
142    ControlVelocityError = 0x03c,
143    /// Torque error
144    ControlTorqueError = 0x03d,
145
146    // === Stay-Within Mode Command Registers (0x040 - 0x048) ===
147    /// Lower position bound
148    CommandStayWithinLowerBound = 0x040,
149    /// Upper position bound
150    CommandStayWithinUpperBound = 0x041,
151    /// Stay-within feedforward torque
152    CommandStayWithinFeedforwardTorque = 0x042,
153    /// Stay-within Kp scale
154    CommandStayWithinKpScale = 0x043,
155    /// Stay-within Kd scale
156    CommandStayWithinKdScale = 0x044,
157    /// Stay-within maximum torque
158    CommandStayWithinPositionMaxTorque = 0x045,
159    /// Stay-within timeout
160    CommandStayWithinTimeout = 0x046,
161    /// Stay-within current limit scale
162    CommandStayWithinIlimitScale = 0x047,
163    /// Stay-within ignore position bounds
164    CommandStayWithinIgnorePositionBounds = 0x048,
165
166    // === Encoder Registers (0x050 - 0x058) ===
167    /// Encoder 0 position
168    Encoder0Position = 0x050,
169    /// Encoder 0 velocity
170    Encoder0Velocity = 0x051,
171    /// Encoder 1 position
172    Encoder1Position = 0x052,
173    /// Encoder 1 velocity
174    Encoder1Velocity = 0x053,
175    /// Encoder 2 position
176    Encoder2Position = 0x054,
177    /// Encoder 2 velocity
178    Encoder2Velocity = 0x055,
179    /// Encoder validity flags
180    EncoderValidity = 0x058,
181
182    // === GPIO Registers (0x05c - 0x05f) ===
183    /// Aux1 GPIO command
184    Aux1GpioCommand = 0x05c,
185    /// Aux2 GPIO command
186    Aux2GpioCommand = 0x05d,
187    /// Aux1 GPIO status
188    Aux1GpioStatus = 0x05e,
189    /// Aux2 GPIO status
190    Aux2GpioStatus = 0x05f,
191
192    // === Analog Input Registers (0x060 - 0x06c) ===
193    /// Aux1 analog input 1
194    Aux1AnalogIn1 = 0x060,
195    /// Aux1 analog input 2
196    Aux1AnalogIn2 = 0x061,
197    /// Aux1 analog input 3
198    Aux1AnalogIn3 = 0x062,
199    /// Aux1 analog input 4
200    Aux1AnalogIn4 = 0x063,
201    /// Aux1 analog input 5
202    Aux1AnalogIn5 = 0x064,
203    /// Aux2 analog input 1
204    Aux2AnalogIn1 = 0x068,
205    /// Aux2 analog input 2
206    Aux2AnalogIn2 = 0x069,
207    /// Aux2 analog input 3
208    Aux2AnalogIn3 = 0x06a,
209    /// Aux2 analog input 4
210    Aux2AnalogIn4 = 0x06b,
211    /// Aux2 analog input 5
212    Aux2AnalogIn5 = 0x06c,
213
214    // === Timing Registers (0x070 - 0x071) ===
215    /// Millisecond counter
216    MillisecondCounter = 0x070,
217    /// Clock trim value
218    ClockTrim = 0x071,
219
220    // === PWM Input Registers (0x072 - 0x075) ===
221    /// Aux1 PWM input period in microseconds
222    Aux1PwmInputPeriod = 0x072,
223    /// Aux1 PWM input duty cycle (0-1)
224    Aux1PwmInputDutyCycle = 0x073,
225    /// Aux2 PWM input period in microseconds
226    Aux2PwmInputPeriod = 0x074,
227    /// Aux2 PWM input duty cycle (0-1)
228    Aux2PwmInputDutyCycle = 0x075,
229
230    // === PWM Output Registers (0x076 - 0x07f) ===
231    /// Aux1 PWM output 1
232    Aux1Pwm1 = 0x076,
233    /// Aux1 PWM output 2
234    Aux1Pwm2 = 0x077,
235    /// Aux1 PWM output 3
236    Aux1Pwm3 = 0x078,
237    /// Aux1 PWM output 4
238    Aux1Pwm4 = 0x079,
239    /// Aux1 PWM output 5
240    Aux1Pwm5 = 0x07a,
241    /// Aux2 PWM output 1
242    Aux2Pwm1 = 0x07b,
243    /// Aux2 PWM output 2
244    Aux2Pwm2 = 0x07c,
245    /// Aux2 PWM output 3
246    Aux2Pwm3 = 0x07d,
247    /// Aux2 PWM output 4
248    Aux2Pwm4 = 0x07e,
249    /// Aux2 PWM output 5
250    Aux2Pwm5 = 0x07f,
251
252    // === Device Info Registers (0x100 - 0x122) ===
253    /// Model number
254    ModelNumber = 0x100,
255    /// Firmware version
256    FirmwareVersion = 0x101,
257    /// Register map version
258    RegisterMapVersion = 0x102,
259    /// Multiplex CAN ID
260    MultiplexId = 0x110,
261    /// Serial number part 1
262    SerialNumber1 = 0x120,
263    /// Serial number part 2
264    SerialNumber2 = 0x121,
265    /// Serial number part 3
266    SerialNumber3 = 0x122,
267
268    // === Output Control Registers (0x130 - 0x133) ===
269    /// Set output position to nearest (alias: Rezero)
270    SetOutputNearest = 0x130,
271    /// Set output position to exact value
272    SetOutputExact = 0x131,
273    /// Require reindex before motion
274    RequireReindex = 0x132,
275    /// Recapture position and velocity
276    RecapturePositionVelocity = 0x133,
277
278    // === Driver Fault Registers (0x140 - 0x141) ===
279    /// Driver fault register 1
280    DriverFault1 = 0x140,
281    /// Driver fault register 2
282    DriverFault2 = 0x141,
283
284    // === UUID Registers (0x150 - 0x158) ===
285    /// UUID part 1
286    Uuid1 = 0x150,
287    /// UUID part 2
288    Uuid2 = 0x151,
289    /// UUID part 3
290    Uuid3 = 0x152,
291    /// UUID part 4
292    Uuid4 = 0x153,
293    /// UUID mask part 1
294    UuidMask1 = 0x154,
295    /// UUID mask part 2
296    UuidMask2 = 0x155,
297    /// UUID mask part 3
298    UuidMask3 = 0x156,
299    /// UUID mask part 4
300    UuidMask4 = 0x157,
301    /// UUID mask capability flag
302    UuidMaskCapable = 0x158,
303}
304
305impl core::fmt::Display for Register {
306    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307        core::fmt::Debug::fmt(self, f)
308    }
309}
310
311impl Register {
312    /// Returns the register address as a u16.
313    #[inline]
314    pub const fn address(self) -> u16 {
315        self as u16
316    }
317
318    /// Creates a Register from a raw address.
319    ///
320    /// Returns `None` if the address doesn't correspond to a known register.
321    pub fn from_address(addr: u16) -> Option<Register> {
322        Self::try_from(addr).ok()
323    }
324}
325
326#[cfg(test)]
327mod tests {
328    use super::*;
329
330    #[test]
331    fn test_register_addresses() {
332        assert_eq!(Register::Mode.address(), 0x000);
333        assert_eq!(Register::Position.address(), 0x001);
334        assert_eq!(Register::Fault.address(), 0x00f);
335        assert_eq!(Register::CommandPosition.address(), 0x020);
336    }
337
338    #[test]
339    fn test_register_from_address() {
340        assert_eq!(Register::from_address(0x000), Some(Register::Mode));
341        assert_eq!(Register::from_address(0x001), Some(Register::Position));
342        assert_eq!(Register::from_address(0xfff), None);
343    }
344}