Expand description
§moteus-protocol
Low-level CAN-FD protocol types for moteus brushless motor controllers.
This crate encodes and decodes the CAN-FD frames used to communicate with moteus controllers. It performs no I/O of its own: you bring the CAN-FD transport, and this crate builds the frames you send and parses the frames you receive.
It is no_std compatible and requires no allocator, so it is usable on
embedded systems as well as in standard environments.
Most applications should use the higher-level
moteus crate instead, which builds on
this one to add blocking and async controllers, transport implementations
(fdcanusb, SocketCAN), and device discovery. Reach for moteus-protocol
directly when you are on an embedded target or have your own CAN-FD
transport.
§Encoding a Command
Commands use a builder pattern, and serialize into a CanFdFrame:
use moteus_protocol::{calculate_arbitration_id, CanFdFrame};
use moteus_protocol::command::{PositionCommand, PositionFormat};
// Address servo ID 1 from source ID 0, requesting a reply.
let mut frame = CanFdFrame::new();
frame.arbitration_id = calculate_arbitration_id(0, 1, 0, true);
let cmd = PositionCommand::new()
.position(0.5) // revolutions
.velocity(1.0); // revolutions / s
cmd.serialize(&mut frame, &PositionFormat::default());
// frame.data and frame.size now contain the encoded command, ready
// to hand to any CAN-FD transport.§Requesting Telemetry
A query describes which registers the controller should report, and at what resolution. It can be appended to the same frame as a command, or sent on its own:
use moteus_protocol::{CanFdFrame, Resolution};
use moteus_protocol::query::QueryFormat;
let mut frame = CanFdFrame::new();
let mut query = QueryFormat::default();
query.position = Resolution::Float; // full precision
query.velocity = Resolution::Float;
let expected_reply_size = query.serialize(&mut frame);§Parsing a Reply
use moteus_protocol::{CanFdFrame, Mode};
use moteus_protocol::query::QueryResult;
// A reply frame as received from the transport. This one reports the
// mode register as an int8 and the position register as a float.
let mut reply = CanFdFrame::new();
reply.data[..9].copy_from_slice(&[
0x21, 0x00, 0x0A, // reply int8, register 0x000: Mode = 10 (position)
0x2D, 0x01, // reply f32, register 0x001: Position
0x00, 0x00, 0x00, 0x3F, // 0.5f32, little endian
]);
reply.size = 9;
let result = QueryResult::parse(&reply);
assert_eq!(result.mode, Mode::Position);
assert_eq!(result.position, 0.5);§Key Types
CanFdFrame: a raw CAN-FD frame (arbitration ID, payload, flags), independent of any particular transport.command: builder-style command types such asPositionCommand,CurrentCommand,VFOCCommand,StayWithinCommand,StopCommand, andBrakeCommand, with matching*Formatresolution descriptions.query::QueryFormat/query::QueryResult: telemetry requests and replies.Register,Mode,Resolution: the moteus register map, operating modes, and wire resolutions.WriteCanData,WriteCombiner,parse_frame: multiplex primitives for reading and writing arbitrary registers.calculate_arbitration_id/parse_arbitration_id: CAN ID routing helpers.fdcanusb: an allocation-free codec for the fdcanusb text line protocol, which moteus also speaks directly over a TTL UART. Encodecan sendlines into a caller-provided buffer, parsercv/OK/ERRlines, and compute the CRC-8 checksums used over UART.diagnostic: payload framing for the tunneled diagnostic stream, including the flow-controlled poll variant for lossy transports.
§UART Hosts
To command a moteus over a TTL UART from an embedded host (see the UART integration reference), encode each CAN frame as an fdcanusb line and parse the response lines:
use moteus_protocol::fdcanusb::{
encode_can_send, parse_line, strip_checksum, EncodeOptions, Line,
MAX_LINE_LENGTH,
};
use moteus_protocol::query::QueryResult;
use moteus_protocol::{CanFdFrame, Mode};
// Fill a frame as in "Encoding a Command" above.
let frame = CanFdFrame::new();
let mut line = [0u8; MAX_LINE_LENGTH];
let len = encode_can_send(
&frame,
&EncodeOptions { disable_brs: false, checksum: true },
&mut line,
).unwrap();
let command = &line[..len]; // write this to the UART
assert!(command.starts_with(b"can send"));
// ... then for each line received from the UART, validate and strip
// the checksum, then classify the line:
let (content, _had_checksum, valid) = strip_checksum(b"OK *BD");
assert!(valid);
assert!(matches!(parse_line(content), Line::Ok)); // command acknowledged
// `rcv` lines carry a CAN frame from the device, parsed like any
// other reply — this one reports the mode and position registers:
match parse_line(b"rcv 0100 21000A2D010000003F") {
Line::Rcv(reply) => {
let result = QueryResult::parse(&reply);
assert_eq!(result.mode, Mode::Position);
assert_eq!(result.position, 0.5);
}
Line::Ok | Line::Err(_) | Line::Other(_) => panic!("expected a reply frame"),
}The retry, timeout, and checksum-escalation policy is up to the host;
the moteus crate’s transports implement one for std environments.
§Building with Bazel
When building from the moteus repository:
tools/bazel build //lib/rust/moteus-protocolRe-exports§
pub use scaling::Scaling;
Modules§
- command
- Command types for moteus motor control.
- diagnostic
- Payload framing for the tunneled diagnostic stream.
- fdcanusb
- Allocation-free codec for the fdcanusb text line protocol.
- query
- Query types for reading telemetry from moteus controllers.
- scaling
- Scaling constants and functions for moteus register values.
Structs§
- CanFd
Frame - A CAN-FD frame.
- Frame
Parser - Iterator over subframes in a multiplex protocol frame.
- Write
CanData - Writer for appending data to a CAN-FD frame.
- Write
Combiner - Combines consecutive register writes of the same resolution for efficiency.
Enums§
- Home
State - The homing/rezero state of the controller.
- Mode
- The operating mode of a moteus controller.
- Register
- Registers exposed for reading or writing from a moteus controller.
- Resolution
- The resolution (data type) used when encoding or decoding a register value.
- Subframe
- A single parsed subframe from a multiplex protocol frame.
- Subframe
Type - The type of a parsed subframe.
- Toggle
- Toggle state for CAN-FD frame options.
- Value
- A decoded register value from the multiplex protocol.
Constants§
- CLIENT_
POLL_ SERVER - Tunneled stream: client poll server
- CLIENT_
POLL_ SERVER_ FLOW - Tunneled stream: client poll server, acknowledging a flow control packet number
- CLIENT_
TO_ SERVER - Tunneled stream: client to server
- CURRENT_
REGISTER_ MAP_ VERSION - The current register map version expected from moteus controllers. If the version differs, semantics of one or more registers may have changed.
- NOP
- No operation
- READ_
ERROR - Read error
- READ_
FLOAT - Read Float values
- READ_
INT8 - Read Int8 values
- READ_
INT16 - Read Int16 values
- READ_
INT32 - Read Int32 values
- REPLY_
FLOAT - Reply Float values
- REPLY_
INT8 - Reply Int8 values
- REPLY_
INT16 - Reply Int16 values
- REPLY_
INT32 - Reply Int32 values
- SERVER_
TO_ CLIENT - Tunneled stream: server to client
- SERVER_
TO_ CLIENT_ FLOW - Tunneled stream: server to client, with flow control packet number
- WRITE_
ERROR - Write error
- WRITE_
FLOAT - Write Float values
- WRITE_
INT8 - Write Int8 values
- WRITE_
INT16 - Write Int16 values
- WRITE_
INT32 - Write Int32 values
Functions§
- calculate_
arbitration_ id - Computes a moteus CAN arbitration ID from routing fields.
- parse_
arbitration_ id - Extracts routing fields from a moteus CAN arbitration ID.
- parse_
frame - Creates a parser that iterates over all subframes in a multiplex protocol frame.