Many people have asked me how to communicate with the VESC using UART, but I did not have a good answer since I haven’t written any tutorial about that and the only thing I could refer to was the BLDC Tool code. Now I have created a project for the STM32F4 discovery board that implements UART communication with the VESC where the full interface is implemented. In this post I will try to explain how the code works and how to port it to other platforms.
Getting started
Start by downloading the code from github: https://github.com/vedderb/bldc_uart_comm_stm32f4_discovery
If you have a stm32f4 discovery board, you can upload the code to it and test it by following the tutorial in my VESC Post for installing the toolchain. After that, just connect both USB ports of the discovery board (one for the built in programmer and one for the serial terminal) and type make upload from the project directory. Don’t forget to connect rx (PB11), tx (PB10) and gnd from the discovery board to tx, rx and gnd on the VESC. The discovery board will show up as a USB-serial port and you can use a serial terminal such as screen or gtkterm to access a simple command line interface on it. Only a few commands are implemented for the command line interface (type help to list them), but a good exercise is to write more commands in the main file using the provided interface. Doing that with the bldc_interface code should be quite straight forward.
Understanding the implementation
The VESC communicates over UART using packets with the following format:
- One Start byte (value 2 for short packets and 3 for long packets)
- One or two bytes specifying the packet length
- The payload of the packet
- Two bytes with a CRC checksum on the payload
- One stop byte (value 3)
The higher level of the VESC communication code is the same for the USB, UART and CAN ports, so everything that can be done from BLDC Tool can be done from the other ports as well. Therefore I have abstracted out the higher layer of the communication into separate files so that it can be reused for CAN-bus and USB without modifying the rest of the code later.
The important files in the project, which you can use for your implementation, are the following. They are plain C files and don’t have any hardware dependencies.
bldc_interface.c and bldc_interface.h
These files can assemble the payload for all the commands that the VESC supports. They can also interpret packets from the VESC and extract the data from them. Notice that only the payload is handled in these files, not the start, stop, length and checksum bytes since these are different for the CAN interface.
datatypes.h
The data structures used by the VESC.
buffer.c and buffer.h
Helper functions for for going between C types and byte arrays. These are used by the bldc_interface files.
crc.c and crc.h
For calculating the CRC checksum
packet.c and packet.h
For assembling the packets for the VESC with start, stop, length and checksum bytes. These files also have a state machine where one byte received from the VESC can be added at a time to assemble a packet and check the checksum correctness.
bldc_interface_uart.c and bldc_interface_uart.h
Connects packet and bldc_interface to provide a clean UART interface. This is where the user has to make the connection to the UART interface for the platform of choice.
All of these files rely heavily on function pointers. This might sound complicated at first, but it is actually quite convenient and easy to use. The connection between these files and the UART port is done in the file comm_uart.c, which is the file that you have to implement if you want to port this to a different platform. Also, if you decide to use some other port than UART such as CAN or USB, you only have to re-implement this file and the higher level implementation will work as before.
Making the platform-specific UART connection
This should be rather straight forward. The bldc_interface_uart files have three functions that have to be used:
bldc_interface_uart_init
This is the init function that takes a function pointer to a function that you provide for sending data on the UART. You can use it something like this:
/** * A function that will send the bytes in *data with length len on the UART */ static void send_packet(unsigned char *data, unsigned int len) { // Your implementation } // Your init function void comm_uart_init(void) { // Initialize your UART... // Initialize the bldc interface and provide your send function bldc_interface_uart_init(send_packet); } |
bldc_interface_uart_process_byte
Call this function every time a byte is received on the UART with the received byte. It will run the state machine in the packet assembler and the callbacks in bldc interface will be called when the packets are ready.
bldc_interface_uart_run_timer
Call this function every millisecond to reset the packet state machine after a timeout in case data is lost.
Notice that bldc_interface_uart_process_byte and bldc_interface_uart_run_timer can be omitted it you only plan to send data to the VESC and not read anything.
In this example project this is implemented in comm_uart.c. This implementation is a bit more complicated than necessary because it uses threads to run the data processing to not block the UART while running the callbacks and to not run the callbacks from an interrupt scope, but a much simpler implementation can also be done if you don’t have an RTOS. You could call bldc_interface_uart_process_byte directly from an interrupt handler every time you receive a byte.
Using bldc_interface
After you are done with the hardware specific UART implementation, you can use bldc_interface in your application. To set the current, duty cycle etc. just call the corresponding function from the setters e.g.
// Run the motor in current control mode with 10A commanded current bldc_interface_set_current(10.0); |
You can do everything that BLDC Tool can do, including changing the configuration (you should read the old configuration before updating it though). Notice that you have to call these functions at regular intervals to not trigger the timeout in the VESC that will release the motor if no new commands have been received for longer than the configured time for safety reasons. This can be done either by calling the corresponding setters at regular intervals or by calling the bldc_interface_send_alive function.
Reading data
Reading data is done with getter functions and callback function pointers. For example, to get realtime data from the VESC, first set a callback to your function for handling the data using bldc_interface_set_rx_value_func and then request the data with bldc_interface_get_values. It can look something like this:
// Your callback function for the received data. In this case you simply // print it using printf. void bldc_val_received(mc_values *val) { printf("Input voltage: %.2f V\r\n", val->v_in); printf("Temp: %.2f degC\r\n", val->temp_pcb); printf("Current motor: %.2f A\r\n", val->current_motor); printf("Current in: %.2f A\r\n", val->current_in); printf("RPM: %.1f RPM\r\n", val->rpm); printf("Duty cycle: %.1f %%\r\n", val->duty_now * 100.0); printf("Ah Drawn: %.4f Ah\r\n", val->amp_hours); printf("Ah Regen: %.4f Ah\r\n", val->amp_hours_charged); printf("Wh Drawn: %.4f Wh\r\n", val->watt_hours); printf("Wh Regen: %.4f Wh\r\n", val->watt_hours_charged); printf("Tacho: %i counts\r\n", val->tachometer); printf("Tacho ABS: %i counts\r\n", val->tachometer_abs); printf("Fault Code: %s\r\n", bldc_interface_fault_to_string(val->fault_code)); } |
// Somewhere in your init code you can set your callback function(s). bldc_interface_set_rx_value_func(bldc_val_received); |
// Every time you want to read the realtime data you call the corresponding getter. // This will send the get command to the VESC and return. When the data is received // the callback will be called from the UART interface. bldc_interface_get_values(); |
Have a look at main.c to see some examples of this.
That’s it! I hope this is enough to get started with UART communication to the VESC.
Hi nice work, it would be great UART communication implemented in arduino platform. Arduino is most used platform, so many users would apreciate that
Regards
Hi Vito:
Here you go: https://github.com/RollingGecko/VescUartControl
This is my implementation for the arduino.
Hi Andy, I’m trying your library but it’s missing “Config.h”?
C:\Program Files (x86)\arduino-1.6.1\libraries\VescUartControl/VescUart.h:19:20: fatal error: Config.h: No such file or directory
#include “Config.h”
Thanks,
DougM
Issue fixed. Added Config.h to example.
Otherwise comment out the include statement in VescUart.h.
Thanks Andy, I figured that out the other day and have now successfully implemented your library – Thank you, I can now retire PPM 🙂
Of course, I have a feature creep request – Could you include the ability to pull fault_code (and/or fault message) out as well?
Thanks,
DougM
Hi Benjamin, congratulations for the outstanding work. I want to know if it’s possible to do these things with Vesc, through a serial connection:
– get “current internal state” (or the like);
– get fault condition (or receive a serial message on internal error)
– release motor
Thank you.
Great tutorial. I used it to write an iOS App that can log real time VESC data over bluetooth. I’ve made that open source over here: https://github.com/gpxlBen/VESC_Logger
Is such a communication possible through CAN BUS?
I still don’t get the protocol say I want to get an RPM data off it What do I sent and what is the format for receiving it?
A word of caution – the information, while generally still applicable, is outdated. For example there is no handbrake command, the COM_GET_VALUES return structure is different etc.
Much better to look at the actual bldc code itself to find the packet payload details.
https://github.com/vedderb/bldc/blob/master/commands.c