Native USB C++ interface

I wanted to learn some C++, so I had a go at developing a C++ native USB interface for the ODrive. It is in a working state at the moment and has been tested on an ODrive 3.2. This is still a work in progress and I intend to add extra doodads as I need them. So far it has been developed on a Windows platform but I see no reason why it wont work on Linux too.
The repo can be found here:

4 Likes

Awesome, I think several people have requested this in the past.

I had a quick look at the code and it seems pretty user friendly. I like for instance how you overload the assignment and cast-operators to make property reads/writes as simple as assignments.

A couple of general comments:

  • Currently you hard-code the JSON CRC. This means the ODrive will reject your requests whenever the JSON changes in any way (probably you’re aware of this). Feel free to use ODrive’s CRC functions and just throw the raw JSON into calc_crc (probably in this function).
    In case you wondered: By using the correct CRC the PC promises something like “I have read and understood your JSON”. That means you could optimistically cache or hardcode the entire JSON and if it changes the communication would only break in a safe, predictable way. But since you already read the JSON dynamically from the device you can easily calculate the checksum dynamically too.

  • The set() functions should not be defined “const” since their operation does indeed logically change the state of the underlying object.

  • Maybe make the cast overloads non-explicit? I’m sure some die-hard best-practice-people would hate me for this suggestion but it seems tempting to write float motor_position = motor0("pos_setpoint"); instead of float motor_position = (float)motor0("pos_setpoint");.

  • At some point it would be desirable to use the native protocol to control the ODrive from embedded platforms like Arduino. It would make sense to have some shared code.

Just to give you a general heads up of where we intend to go with the protocol: I’m currently developing the Fibre protocol as a side project. Currently the master branch is roughly equal to the ODrive protocol (same code).
In the near future we plan to include this repo as a Git subtree to the main ODrive repo. For ODrive this means all protocol code will live in it’s own folder, thus being more separated from the MotorControl code. For ODrive users this means they can more easily integrate the protocol as a library into their own projects. I think that would be a good place to integrate your client-side C++ library if you’re interested.
The mid-term goal of Fibre is however to be useful for a much wider range of applications. The design for this is still not finalized but will most likely include a significant refactoring and breaking changes.

1 Like

Thanks for the feedback.
I am working on getting the CRC working, for initial debugging I was using what was calculated in the python code. I will more than likely use the ODrive CRC functions.

I used explicit cast overloads to be extra safe, although the code does check what type the ODrive expects and what type its about to set. I also did it as a learning exercise.

The Fibre protocol looks interesting, I would be happy to try adapt my client-side library to suit.

You have an awesome integration; thanks for taking the first steps on this!

Just as a note to those that come across this, I also created an extremely simple integration in C++ on the legacy protocol, to avoid having any external dependencies.

any chance one of those two implementations supports native-stream protocol ? (instead of default native i mean)

I updated https://github.com/moorage/odrive_cpp_sdk to now work with the latest native (USB) protocol.

@alexisdal : why do you want the native-stream rather than native? It turns out native works in macOS afterall…

1 Like

Because it was suggested by Samuel while trying to use the USB interface in stress tests.

I honnestly didn’t really look at the code and don’t know the difference between the two. My intuition (derived from the name of the protocol) was that native-stream was designed to be a stream of commands, whereas native as more for general purpose.

The reason why i want to explore a native c++ client is because i observe too small performance in python3 on the latest raspberry pi while trying the send positions commands from rpi to workaround the limitation that the trajectory planning is not implemented in the firmware for now.

The ascii protocol actually uses a completely different communication method through the kernel, compared to native . ascii opens /dev/ttyACM* or /dev/tty.usbmodem* (depending on linux or mac). So It doesn’t sound like you tried native?

I ran into issues with ascii because depending on the kernel in use, I couldn’t get it to flush commands to the odrive correctly. With native in C++, I haven’t had this problem.

Would you be willing to try the odrive_cpp_sdk with native? I’m curious how it would work on a raspberry pi.

yes. will do. but not right now.

I have recently used native and native-stream (defined at compile time through config.tup). Unless I’m mistaken, I haven’t used the legacy-ascii protocol in months I believe.

I may be missing something here. Is there any documentation on setZeroethRadianInEncoderTicks, setGoalMotorPositions, etc? I hope to use velocity control, but looking through the code this doesn’t seem to be possible.

Yeah sorry, I didn’t implement velocity control, just position goals!

No worries, that’ll give me something to do. Any advice if I’d implement velocity control on top of your code?