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 ;
use ;
// Address servo ID 1 from source ID 0, requesting a reply.
let mut frame = new;
frame.arbitration_id = calculate_arbitration_id;
let cmd = new
.position // revolutions
.velocity; // revolutions / s
cmd.serialize;
// 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 ;
use QueryFormat;
let mut frame = new;
let mut query = default;
query.position = Float; // full precision
query.velocity = Float;
let expected_reply_size = query.serialize;
Parsing a Reply
use ;
use 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 = new;
reply.data.copy_from_slice;
reply.size = 9;
let result = parse;
assert_eq!;
assert_eq!;
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 ;
use QueryResult;
use ;
// Fill a frame as in "Encoding a Command" above.
let frame = new;
let mut line = ;
let len = encode_can_send.unwrap;
let command = &line; // write this to the UART
assert!;
// ... then for each line received from the UART, validate and strip
// the checksum, then classify the line:
let = strip_checksum;
assert!;
assert!; // 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
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: