Disclaimer: This post is a way for me to document my progress communicating with the ODrive from Java. Maybe it will help somebody later on.
The basis of my control setup is java, so I need to find a way to communicate with the ODrive from Java. A number of options I considered:
- Connecting through Serial
- Calling Python code from Java.
- Accessing ODrive directly through USB.
Serial
I have earlier experience communicating over serial. However, from what I have read on this forum the serial protocol is not very stable, nor the preferred method of communicating with the ODrive.
Also I hope my control loop can run at 1kHz, and I think this will be an issue over the current serial implementation. I can probably make it work at 100Hz, but faster would really be nice. At any speed my PC will need to read as many values as possible (two at the least) and send one value to the ODrive.
So i have decided against this option.
Calling Python from Java
This seems like an interesting route since the Odrivetool was written in Python. However, going from Java to Python to C++ seems very circuitous, and could make things difficult to maintain.
Accessing ODrive directly through USB
This is the option I am now exploring. It is made a lot easier by looking at the code in https://github.com/madcowswe/ODrive/blob/master/Firmware/fibre/python/fibre/ . But, my java is rusty, and I have never worked with the USB interface before. So please bear with me.
Connecting to device
To access the USB interface I am using usb4java.
First thing is to connect to a UsbDevice. According to the code below I should be looking for a device with one of three possible combinations of prodId and vendorId.
# Currently we identify fibre-enabled devices by VID,PID
# TODO: identify by USB descriptors
WELL_KNOWN_VID_PID_PAIRS = [
(0x1209, 0x0D31),
(0x1209, 0x0D32),
(0x1209, 0x0D33)
]
lines 13-19 of usbbulk_transport.py
That works quite well, and i succesfully connect to:
Product: ODrive 3.5 CDC Interface
Device info: Bus 002 Device 007: ID 1209:0d32
Manufacturer: ODrive Robotics
Serial number: 386837583437
Choosing an interface
Having chosen a device I now need to select an interface. The python code appears to have two possible preferences.
custom_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x00 and i.bInterfaceSubClass == 0x01]
cdc_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x0a and i.bInterfaceSubClass == 0x00]
So either Class 0 Subclass 1 or Class 10 Subclass 0. There are 3 available:
Interface 0, Class 2 Communications, Subclass 2
Interface 1, Class 10 Data, Subclass 0
Interface 2, Class 0 Per Interface, Subclass 1
Interface 0 does not appear to be relevant based on the python code. I am not able to claim() interface 1. So I opt for interface 2.
Endpoints
The chosen interface has two endpoints, one for direction IN and one for direction OUT. I assume that I will need both so I save the handles.
0x83, EP 3, IN
0x03, EP 3, OUT
Both have maxpacketsize 64 and Transfer Type Bulk.
Sniffing
Now that I have the endpoints, I need to figure out what to send and receive. To figure out what I should be sending I am going to try sniffing the packages my python script sends. Having a working python script, and reverse engineering the packages, seems like a nice complement to reading the code (since I have trouble following the code in fibre at this point).
My Python test script is:
1 - odrv0 = odrive.find_any()
2 - print(str(odrv0.vbus_voltage))
The first message that is sent between the ODrive and my script seems to be from PC to ODrive over Endpoint 0x03, EP 3, OUT. The packet and my understanding of it are as follows:
0000 1b 00 00 37 e7 37 8e c3 ff ff 00 00 00 00 09 00
0010 00 01 00 01 00 03 03 0c 00 00 00 81 00 00 80 00
0020 02 00 00 00 00 01 00
Of which only this part is actual package data.
1 - 81 00 00 80 00 02 00 00 00 00 01 00
I have included a number at the beginning to indicate the sequence of messages. In the following packages sent to the ODrive (3 - 9) this part barely changes.
3 - 82 00 00 80 00 02 1e 00 00 00 01 00
5 - 83 00 00 80 00 02 3c 00 00 00 01 00
7 - 84 00 00 80 00 02 5a 00 00 00 01 00
9 - 85 00 00 80 00 02 78 00 00 00 01 00
Looks like some kind of acknowledgement. Lets take a look at what the ODrive is sending back. Packet 2 contains the following package data:
2 - 81 80 5b 7b 22 6e 61 6d 65 22 3a 22 22 2c 22 69 64 22 3a 30 2c 22 74 79 70 65 22 3a 22 6a 73 6f
Which when converted to ASCII is:
2 - [{“name”:"",“id”:0,“type”:“jso
4 - n”,“access”:“r”},{“name”:“vbus
6 - _voltage”,“id”:1,“type”:"float
This exchange goes one for quite a while. So it looks like the ODrive is explaining how it’s data is configured (in json).
That’s as far as I got today.