The pi3hat, among other things has 5 CAN-FD ports. You can use them to drive a lot of moteus servos, but they are perfectly fine CAN-FD ports generally. The C++ library has always been able to send and receive arbitrary frames (and recently at arbitrary bitrates), but the python interface was lacking, only exposing a portion of this functionality.
As of version 0.3.11, the python library (pip3 install moteus-pi3hat
) now exposes everything you need to be able to send and receive arbitrary CAN frames from any of the ports, as well as configure all the timing options for waiting for responses from slave devices.
This is what a sample usage of raw frames, mixed in with moteus frames, looks like:
# To send a raw CAN, you must manually instantiate a
# 'moteus.Command' and fill in its fields, along with which
# bus to send it on.
raw_message = moteus.Command()
raw_message.raw = True
raw_message.arbitration_id = 0x0405
raw_message.bus = 5
raw_message.data = b'1234'
raw_message.reply_required = False
# A single 'transport.cycle' call's message list can contain a
# mix of "raw" frames and those generated from
# 'moteus.Controller'.
#
# If you want to listen on a CAN bus without having sent a
# command with 'reply_required' set, you can use the
# 'force_can_check' optional parameter. It is a 1-indexed
# bitfield listing which additional CAN buses should be
# listened to.
results = await transport.cycle([
raw_message,
controller.make_query(),
], force_can_check = (1 << 5))
# If any raw CAN frames are present, the result list will be a
# mix of moteus.Result elements and can.Message elements.
# They each have the 'bus', 'arbitration_id', and 'data'
# fields.
#
# moteus.Result elements additionally have an 'id' field which
# is the moteus servo ID and a 'values' field which reports
# the decoded response.
for result in results:
if hasattr(result, 'id'):
# This is a moteus structure.
print(f"{time.time():.3f} MOTEUS {result}")
else:
# This is a raw structure.
print(f"{time.time():.3f} BUS {result.bus} " +
f"ID {result.arbitration_id:x} DATA {result.data.hex()}")
It isn’t the cleanest API, but it does get the job done!