VESC UART Control with Spin
Duane Degn
Posts: 10,588
in Propeller 1
As mentioned in another thread, I'm working on an electric skateboard.
I'm hoping to use a Propeller to interface with several VESC (Vedder Electronic Speed Controller) boards. One reason for using VESC boards is VESC is an open source project. The source code for the VESC is available. The C code is out of my comfort zone. Rather than attempting to modify the VESC code, I'm hoping to interface with the VESC board using a Propeller.
There's a guide to Communicating with the VESC using UART but I find the instructions a bit sparse.
I'm attempting to figure out the commands and how they are used from the source code. I'm hopeful I'll be able to figure this all out but I thought I'd check here in case any of you have done something similar.
Do any of you have any suggestions on how to interface a Propeller (Spin or PASM preferred) with a VESC?
Searching the forums for "VESC" didn't produce any hits.
I'm hoping to use a Propeller to interface with several VESC (Vedder Electronic Speed Controller) boards. One reason for using VESC boards is VESC is an open source project. The source code for the VESC is available. The C code is out of my comfort zone. Rather than attempting to modify the VESC code, I'm hoping to interface with the VESC board using a Propeller.
There's a guide to Communicating with the VESC using UART but I find the instructions a bit sparse.
I'm attempting to figure out the commands and how they are used from the source code. I'm hopeful I'll be able to figure this all out but I thought I'd check here in case any of you have done something similar.
Do any of you have any suggestions on how to interface a Propeller (Spin or PASM preferred) with a VESC?
Searching the forums for "VESC" didn't produce any hits.
Comments
Why on earth would you use a stop byte value that is the same as a possible start byte value ?
Bean
I don't know. I think the original comm protocol used CAN. I'm guessing the "3" value for start and stop doesn't present the same issue when using CAN.
The line "The payload of the packet" is disappointingly vague. I'm reading through the code attempting to understand how this payload is structured.
I'll likely load one of the Arduino based examples and monitor the com line with a logic analyzer to see if the payload structure becomes understandable.
I was kind of hoping there would be a list of commands and what they do someplace (other than within the code). Something like this one for Dynamixel actuators would be great.
I like to think I'll be able to figure this out from the code and from monitoring communication from an Arduino. I'll post what I learn to the forum in case anyone else wants to control a VESC using a Propeller.
I believe the commands are listed in this file (part of a Raspberry Pi VESC project).
There's also a Python file to get values from the VESC and set values in the VESC.
Hopefully this is enough info for me to write a Spin program to interface with the VESC. I'll post an update whether or not it works.
Packet structure is described in the blog
the start byte value basically indicates the index where the payload starts (ie length > 255, two bytes needed, use 3 other wise 2)
For the payload you need to look in the bldc_interface.c file. It seems to consist of a command followed by a set of parameters depending on the command. The commands used are defined in datatypes.h.
Example using the command given in the blog:
bldc_interface.c
datatypes.h
packet payload
Problem: my google-fu was not good enough to find how gcc stores a float32 (might be the same as binary32 ??)
CRC = CRC16 value of the payload
I like to think I'm slowly making sense of it. Of course it's easier to think I understand it when I haven't tested my knowledge yet.
I'm pretty sure the float in the VESC is packaged the same way F32.spin packages a 32-bit float.
I've used CRC16 in other projects. I found some code by Mike Green which works great.
Thanks for your help in deciphering how the payload it built.
I just found this reply from Vedder on a forum. It looks really helpful. I had the CRC bytes reversed.
I'll give this another try with this new info.
Here's the code I tried.
The above code sent the following data.
Hopefully it's obvious those are bytes displayed in hexadecimal.
I'll likely purchase the appropriate hardware to test the example code provided by Vedder. Hopefully I'll be able to figure out what I'm doing wrong by studying logic analyzer captures of valid packets.
I found an Arduino program to communicate with the VESC and here's a capture from my logic analyzer.
The output from the Arduino code is:
The proper CRC is $40 $84.
I'll need to figure out the correct way to calculate the CRC.
The array "crc16_tab" has 256 16-bit elements. I'll include this table with the Spin code.
Edit: As JonnyMac points out below, my CRC code is wrong. Look for updated code later in this thread. (Coming soon.)
My Propeller version of the CRC produces the same output as the Arduino.
So far, I've only computed the CRC for a single byte payload. Hopefully this method works for larger payloads as well.
Now I need to figure out how to parse out the reply message. The reply includes 16-bit floats which I haven't used with the Propeller before. Hopefully it won't be hard to convert 16-bit floats into 32-bit floats so I can use F32.spin to perform appropriate operations on the floating point values.
edit: making good progress
Wow! Thank you. That's just what I tried to find but couldn't . I had hoped it would be as easy as shifting some bits.
I didn't realize my C was that rusty. Thank you!
I love this forum!
I finally found my other mistakes and my calculated CRC now agrees with the one sent from the VESC. I also modified my CRC code to deal with a circular buffer. Here's my current CRC code.
The "Get Values" command is successfully sent and I receive a 73 byte payload in return. I haven't started parsing the payload yet but here's one of the payloads returned.
I also included the CRCs to show the calculated value agrees with the received value. The six bytes at the bottom of the text block are the bytes sent to the VESC.
Thanks again to everyone who has helped so far.
The first byte of the payload is 4 which indicates it's a reply for my request for "values" but I doubt I have the various values link to the correct variables.
I'm not sure why "vIn" is set to 30.9. The voltage of the batteries I'm using adds up to about 45V.
The temperature of the first MOSFET seems reasonable. I'm guessing my board only has one temperature sensor which is why the other MOSFETs are reported as zero degrees.
I attempted to set the RPM to 60.
Either I'm doing something wrong with this command or 60 is too low for the RPM to produce motion. I didn't want the motors to go crazy on me since I don't have the secured very well right now. It's also possible I have to set some other parameter in order to set the RPM directly.
The Arduino program I used as a guide for the "Get Values" command, doesn't have a function to set the RPM so I couldn't use my logic analyzer to check to see if my code generated the correct packet.
I've ordered a STM32F0DISCOVERY board from Digikey. There's a very full featured example which uses this board. Hopefully I can monitor the UART with my logic analyzer and figure out how to set parameters and not just read them (not that I have the reading part all figured out).
I'm making some progress, but it's painfully slow progress right now.
Of course this is assuming I'm reading the correct byte in the payload.
I used the VESC-Tool to set up the board initially. I set the battery as a 12 cell LiPo. 30.9V would be too low of a voltage for a 12 cell battery but my battery is closer to 45V. I just measured the battery with my Fluke and it was close to 46V. I'm back to being confused.
At least I was right about something. I'm pretty sure I was using the wrong list to assign the various values to variables. I used this structure:
The code below assigns values to variables one at a time. I'm now using this code to determine which bytes correspond to which variable. Notice the location of the "v_in" variable. I was treating a temperature value as the battery voltage value. Just one reason why I was/am so confused.
When I originally read the above code, I thought the data was packaged as a combination of floats and integers. I'm now confident all the data is packaged as integers and the floats are only used to display the values.
Looking back at the data, I see the battery voltage was read as 46.6V. This is pretty close to what I read using my multimeter. A bit of a silver lining to this confusing dark cloud.
My next attempt will be to set the current value. I'm pretty sure the VESC is set to current control so hopefully this will allow me some control of the motor from the Propeller. As I mentioned previously, my attempt to control the RPM didn't work.
EDIT: mmm that means the same should apply to setting the data in the command buffer, and yes indeed
Sorry for not digging deep enough when looking through the code
I asked about converting 16-bit floats to 32-bit floats and you answered my question. I certainly wasn't expecting anyone to dig through the code. Thanks again for the information your provided.
I have made some more progress on this project. I can controller the two motors independently now.
One of my initial problems was thinking I could just send occasional UART commands. The VESC expects a regular (several times a second?) stream of data over the UART or it will timeout and shut off the motor. This is certainly a reasonable thing to do.
I now have the program send MOTOR_CURRENT commands at about 50 Hz. This seems to work well. I also wired up separate UART lines to each of the two ports on the VESC. There should be a way to send to just one port but I don't know how to do this.
The motor doesn't start turning until the current is set to about 500. I thought this was 500mA but with both motors set to 500, the combined draw is about 160mA from the 47V battery. I don't know what sort of units the "500" is but I'm doubtful it's milliamps.
Now that I can independently control two motors, I kind of agree with you.
I'd like to figure out how to control these motors using RPM rather than current. If needed, I could monitor the encoders and adjust the current to produce the desired RPM. While I think this route is possible, I feel like I'd be duplicating work already possible with the VESC itself.
Edit: I just realized I never tried sending RPM control signals repeatedly. It's possible this would work but I think I have to change some mode setting.