Using native protocol with Arduino

I understand that the Arduino library makes use of the ascii protocol to communicate with ODrive.

I would like to modify the library to make use of the native protocol.

However, I’m having trouble finding a specification of the native protocol. I went through theprotocol interface, but I’m not sure what the content of the messages needs to be.

Is there any specification available? Alternatively, could anyone provide a simple example of using the native protocol outside the Python library?

Thank you.

2 Likes

Not sure where, or if, it’s linked to in the doc index or similar, but here are details on the packet format: https://github.com/madcowswe/ODrive/blob/master/docs/protocol.md

Also keep in mind this particular workaround https://github.com/madcowswe/ODrive/blob/eb07260c3ea57e74c59432fd036b275b608d85d0/Firmware/fibre/python/fibre/protocol.py#L276 when writing your client code. e.g. if you’re sending message with sequence number “1”, the firmware is going to reply to that message with 129 instead of echoing back your “1”.

Although the sequence number is a short, you’ll need to treat it as a ushort since that bit is being used as a flag instead. That is, when you hit sequence number 32767 you’ll want to reset to 0 prior to your increment call.

I recommend looking at things with a USB packet inspector/monitor while using odrivetool and examining the payloads. I’d assume the UART packets will be the same. I didn’t even know you could use the native protocol with UART until this post.

Hi @metanoic,

Thanks for your reply.

I’m aware of the document you linked, but what I’m looking for is the actual specifications of the message content.

Using a USB packet monitor is a good idea so I’ll try that and post back.

In the meantime, if anyone could provide some background on the messages content it would be a great help.

Ah, I understand your question now I think. In short, the specification is dynamic.

The message contents themselves (both transmitted and received) will vary depending on the “endpoint” you’re dealing with (byte index 2 and 3 (3rd and 4th)) - though there is a CRC that should be sent with each message; that CRC is static between messages (it is not a CRC of the message itself, but rather a CRC of the schema). “Endpoint” not in the USB sense… think of it merely as the numeric address of a remote property, method or function.

You can get a list of endpoints by querying endpoint 0 and providing 1 for the CRC. That will return a JSON representation of all the other endpoints, their accepted parameters, their return type and flags for read and write. You can see a non-JSON version of the schema in this file: https://github.com/madcowswe/ODrive/blob/master/Arduino/ArduinoI2C/odrive_endpoints.h and an outdated JSON version here: https://pastebin.com/GF9M5Hu6 (this is what endpoint 0 returns, minus any whitespace)

For example: To set the Axis0 position setpoint, you’d use EndpointID 87 and provide a float value starting at the packet’s 7th byte followed by the CRC.

Note that all communication is Little Endian rather than network byte order / Big Endian. The CRC will change any time the firmware changes in a manner that alters the schema in any way. It’s basically just a safeguard against communicating with a different version of the protocol than your program is anticipating, which is fairly important since the order/number of endpoint is dictated by the execution order in the firmware and some users machines can kill or maim.

Hop on the discord and say hi in the development channel.

Ah, that makes perfect sense. So a library using the binary protocol would query endpoint 0 and get an endpoint schema, whch would subsequently be used to get the ids of endpoints of interest to read from/write to.

Thanks a lot for the help! Will hop on discord soon :slight_smile:

1 Like

Correct. If it’s a private project then you can just read/write directly to endpoint X without querying for the schema. When the device stops accepting your commands after updating firmware then just ensure the endpoints you’re writing to haven’t changed their numbers or signatures and update your CRC and continue on. If it’s a public library or similar, you may want to use the schema to generate your library’s code or deal with it dynamically like odrivetool does.

I started digging into this one but got stuck fairly early on while trying to get the JSON schema using UART. I’ve reflashed ODrive with CONFIG_UART_PROTOCOL=native and issued the following from Arduino:

unsigned char pk[] = {0x80, 0x00, 0x00, 0x01};
Serial1.write(pk, sizeof(pk));

But I get no response. A similar request using the ascii protocol works fine by the way, i.e. it returns the values as expected.

Obviously I got the packet formating/content wrong. So I’d greatly appreciate it if anyone could share an example of a simple schema request packet that works. Then I can take it from there for the rest.

Thanks!

Three years later, I have the exact same question. I have followed the exact same steps as @yconst and got the exact same result: a sullenly silent Odrive.

The documentation as written is probably very useful for someone, but whatever knowledge you’re expected to already know to make sense of it, I don’t have.

Is there nowhere we can find a simple example of this?

I think i can help you out on this one, tho i’m stuck myself a bit further on. I managed to get the correct answer from the odrive, but only sometimes, here is a link to my issue: ODrive ignores most requests in UART native mode - #5 by HerrNamenlos123

This video is very helpful for understanding why it is that way:

Basically the main issue you’re having is that you’re not sending a stream, just the naked packet. You can use a USB sniffer with the odrivetool to see what bytes are being sent and this is the same packet format for UART like in the document mentioned above. However, since UART is not packet based it must be wrapped in a stream. The stream the ODrive expects is the stream format from the document mentioned above: A stream header (0xAA), the size of the inner packet taken from the usb sniffer and then a crc-8 checksum across the first 2 bytes. Then the actual packet itself must be sent (from the usb sniffer) and then after that 2 additional bytes which are the crc-16 checksum across the inner packet.

If you look at my issue above you can see an example code, see if it works for you, it only partially works for me.

Also, to enable the native protocol i did not need to reflash the firmware, i simply used the ODriveGUI to set uart0_protocol from 3 to 0 according to this doc: ODrive.StreamProtocolType | ODrive. And then call save_configuration().

Check if my example code works for you and if you consistently get a response please tell me because i don’t : P

EDIT: You must change the last 2 bytes of the inner packet. This is the crc checksum across the JSON definition and it’s probably different for you. Use a USB sniffer to check what it must be in your case. However as the inner packet changes, you must also adapt the last 2 bytes of the stream (checksum of the inner packet). For that you can use the calculator described at the bottom of this page: ODrive Communication Protocol # | ODrive. Make sure you set up the parameters correctly and check if it works with the examples.

1 Like