UUID based addressing and python multiple device support

I’m excited to announce a significant usability improvement for all moteus controllers, command line tools and the python library! If your moteus controllers are on firmware version 2025-09-20 or newer and your moteus client tools are at 0.3.91 or newer, (and if you are using a pi3hat, it is on firmware 2025-09-20 or newer) there are some big improvements that you transparently get:

  1. You no longer need to assign unique CAN IDs before daisy chaining controllers
  2. The python transport lets client programs transparently operate across multiple CAN-FD interfaces simultaneously
  3. tview will automatically open all controllers attached to all CAN-FD interfaces on the computer

Let’s dig into what this looks like from the command line, tview and what the practical constraints are.

UUID based addressing

Historically, each moteus device has a integer CAN ID that is between 1 and 126. In order to communicate on the CAN-FD bus, each device had to have a unique ID assigned. Since every moteus controller ships from the factory with ID 1, this meant that they had to be connected one by one on the CAN-FD bus in order to assign IDs. Further, because changing the CAN ID takes effect immediately, it is easy to not correctly save the configuration afterward. If you change the ID in tview, it renders further communication inoperative, so unless you remember to restart tview with the new ID, any conf write you issue will go into the void.

Now, there are two mechanisms for selecting which moteus to communicate with. The CAN ID method remains functional and is preferred for deployed systems. Additionally now though, the unique UUID embedded into the OTP of each moteus controller can also be used to select which device is communicated with. As a technical matter, the broadcast CAN ID is used, then the first part of the CAN frame is a write to a special UUID mask register. If a controller sees a write to this register with a UUID that does not match its own, then it assumes it is not being addressed.

Practically, this means a few things. First, if you do not specify an address to moteus_tool or tview and multiple devices with the same CAN ID are on the bus, the tool will find all of them.

moteus_tool discovering devices with the same CAN ID

moteus_tool discovering devices with the same CAN ID

Second, when manually specifying a device either on the command line or in the python library, you can use either the CAN ID or a prefix of the UUID. The prefix needs to be an even 4 bytes, so you can use 4, 8, 12 or 16 byte long strings with or without dashes. For example:

python -m moteus.moteus_tool -t 647093ea --info

Will show the information from the controller with a UUID that starts with 647093ea. Usage in the python library looks like:

c = moteus.Controller(moteus.DeviceAddress(uuid=bytes.fromhex('647093ea')))
await c.set_stop()

To make ID assignment even easier, tview will switch to UUID based addressing if it detects that you are attempting to change the CAN-ID and the controller supports it.

Addressing controllers by their UUID imposes a moderate penalty in terms of CAN frame size, as the UUID must be sent in every frame. Thus, it is still recommended to set unique CAN IDs for each device. However, now it is much easier because you can connect everything, open up tview once, then change all the IDs at once. The old method required many cable reconnections and restarts of tview. You of course still need to ensure that you are changing the correct device! One easy method is to look at the reported position in motor_position.output, then wiggle the motor by hand.

Multiple device support

The other big change is that the python library now transparently supports interacting with devices on multiple physical CAN-FD interfaces at the same time. That can be multiple mjcanfd-usb-1x’s, any supported python-can devices like socketcan interfaces or PCAN library based peripherals, or any of the channels on the pi3hat. By default, the library will attempt to use every connected transport so simple usage is really simple. For example, using:

python -m moteus.moteus_tool --info

Would simultaneously find devices attached to multiple mjcanfd-usb-1x’s and a socketcan, reporting which transport each device was discovered on. tview operates the same way, and will show the transport, if there are multiple possible ones, in the tree view along with the ID.

moteus_tool discovering devices on multiple transports

moteus_tool discovering devices on multiple transports

When using the python library, the transport you want to use does not need to be specified in advance, only the device address, either CAN ID or UUID. When an interaction is first performed with a device, a short discovery message is sent on all connected CAN-FD interfaces to find which one the device is located on, after which further communication is directed specifically at that CAN-FD bus.

If necessary, there are facilities for manually specifying which CAN-FD interfaces to use, and for manually associating devices with CAN-FD interfaces, although in practice they probably won’t be used all that often. That means that for many cases even with many different transports, your python code can look like:

parser = argparse.ArgumentParser()
moteus.make_transport_args(parser)
args = parser.parse_args()

transport = moteus.get_singleton_transport(args)
controllers = {x: moteus.Controller(id=x, transport=transport)
               for x in [1, 2, 3, 4, 5, 6, 7, 8]}

await transport.cycle([c.make_stop() for c in controllers.values()])
results = await transport.cycle(
    [c.make_position(position=position[id], query=True)
    for id, c in controllers.items()])

Similarly, tview now usually needs no arguments whatsoever, and shows all the devices attached simultaneously.

tview operating on devices across multiple transports

tview operating on devices across multiple transports

pi3hat operation

Multiple device support changes how usage of the pi3hat happens both in the python library and with the command line tools. Basically, in the new world, no pi3hat specific python or command line interface is required any more. The old Pi3HatRouter and --pi3hat-cfg continue to work as before, but if omitted, then all channels of the pi3hat are probed and devices are automatically allocated to the pi3hat ports where they are connected.

How to use

Unfortunately, to get the full benefit of all these features, all controllers in your system need to be at firmware 2025-09-20 or newer, your pi3hat firmware needs to be 2025-09-20 or newer, your mjcanfd-usb-1x or fdcanusbs need to be on version 2025-09-19 and your moteus and moteus-gui python library need to be 0.3.91 or newer. Some capabilities will work in a degraded manner with mixed versions, but enough fixes were required across the board that a full upgrade is required to get the full experience.

To upgrade the moteus firmware, follow these instructions: https://github.com/mjbots/moteus/blob/main/docs/reference.md#flashing-over-can

To upgrade the python library:

python -m pip install --upgrade moteus moteus-gui

To upgrade the pi3hat firmware, see this blog post: https://blog.mjbots.com/2025/10/02/pi3hat-firmware-release-2025-09-20/

To upgrade the mjcanfd-usb-1x or fdcanusb firmware, see this blog post: Improved performance for mjcanfd-usb-1x

To upgrade the pi3hat python library:

./venv/bin/python -m pip install --upgrade moteus-pi3hat