Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vedderb/bldc/llms.txt

Use this file to discover all available pages before exploring further.

The VESC uses a consistent framed serial protocol over both UART and USB. Understanding the packet format lets you communicate with a VESC from any microcontroller or host with a serial port. The protocol is implemented in comm/packet.c and comm/packet.h.

Packet structure

Every packet is wrapped in a frame that provides length and integrity information.
Byte(s)FieldValue
0Start byte0x02
1Payload length1 – 255
2 … (1 + length)PayloadCommand ID + data
last 2CRC16Big-endian CRC of payload
lastStop byte0x03
  • The maximum payload length is 512 bytes (PACKET_MAX_PL_LEN in packet.h).
  • The first byte of the payload is always the COMM_PACKET_ID command byte.
  • The CRC is computed over the payload bytes only (not the start byte, length, or stop byte).
  • The stop byte is 0x03 for both short and long frames.
The start byte distinguishes frame types: 0x02 means a one-byte length field follows; 0x03 means a two-byte length field follows.

Packet layer API

The packet.h API manages framing state for each communication channel:
// State for one communication channel
typedef struct {
    void(*send_func)(unsigned char *data, unsigned int len);
    void(*process_func)(unsigned char *data, unsigned int len);
    unsigned int rx_read_ptr;
    unsigned int rx_write_ptr;
    int bytes_left;
    unsigned char rx_buffer[PACKET_BUFFER_LEN]; // PACKET_MAX_PL_LEN + 8
    unsigned char tx_buffer[PACKET_BUFFER_LEN];
} PACKET_STATE_t;

// Initialize a channel with send and process callbacks
void packet_init(
    void (*send_func)(unsigned char *data, unsigned int len),
    void (*process_func)(unsigned char *data, unsigned int len),
    PACKET_STATE_t *state
);

// Reset framing state (call on reconnect or error)
void packet_reset(PACKET_STATE_t *state);

// Feed one received byte into the framing state machine
void packet_process_byte(uint8_t rx_data, PACKET_STATE_t *state);

// Encode and send a payload via the channel's send_func
void packet_send_packet(unsigned char *data, unsigned int len, PACKET_STATE_t *state);

Communicating from an external microcontroller

1

Connect UART

Connect your microcontroller’s TX to the VESC RX pin and RX to the VESC TX pin. Use the baud rate configured in VESC Tool under App Settings -> UART -> Baud Rate (default: 115200). Ensure common ground.
2

Build a packet

Construct a payload starting with the COMM_PACKET_ID byte followed by any required data bytes for that command. Encode it into a frame (start byte, length, payload, CRC16, stop byte).
3

Send and receive

Transmit the frame over UART. For commands that return data (e.g., COMM_GET_VALUES), wait for the VESC to send a response frame and decode it the same way.

Minimal C example: set duty cycle

#include <stdint.h>
#include <string.h>

// CRC-16/CCITT-FALSE
static uint16_t crc16(const uint8_t *buf, uint32_t len) {
    uint16_t crc = 0;
    for (uint32_t i = 0; i < len; i++) {
        crc ^= (uint16_t)buf[i] << 8;
        for (int j = 0; j < 8; j++) {
            crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : crc << 1;
        }
    }
    return crc;
}

// Send a framed packet over your UART hardware
void uart_send_bytes(const uint8_t *data, uint16_t len) {
    // hardware-specific transmit
}

#define COMM_SET_DUTY 5

void vesc_set_duty(float duty) {
    int32_t duty_scaled = (int32_t)(duty * 100000.0f);

    // Payload: command byte + 4 data bytes
    uint8_t payload[5];
    payload[0] = COMM_SET_DUTY;
    payload[1] = (duty_scaled >> 24) & 0xFF;
    payload[2] = (duty_scaled >> 16) & 0xFF;
    payload[3] = (duty_scaled >> 8)  & 0xFF;
    payload[4] =  duty_scaled        & 0xFF;

    uint16_t crc = crc16(payload, sizeof(payload));

    // Short frame: start=0x02, 1-byte length, payload, CRC, stop=0x03
    uint8_t frame[9];
    frame[0] = 0x02;
    frame[1] = sizeof(payload);
    memcpy(&frame[2], payload, sizeof(payload));
    frame[7] = (crc >> 8) & 0xFF;
    frame[8] =  crc       & 0xFF;
    // Note: stop byte 0x03 appended after CRC
    uint8_t stop = 0x03;

    uart_send_bytes(frame, sizeof(frame));
    uart_send_bytes(&stop, 1);
}

COMM_PACKET_ID command reference

The first byte of every payload is a COMM_PACKET_ID value, defined in datatypes.h.

Motor control commands

IDNameDirectionDescription
5COMM_SET_DUTYHost → VESCSet duty cycle (int32, scale 100000)
6COMM_SET_CURRENTHost → VESCSet motor current (int32, scale 1000)
7COMM_SET_CURRENT_BRAKEHost → VESCSet braking current (int32, scale 1000)
8COMM_SET_RPMHost → VESCSet RPM (int32, scale 1)
9COMM_SET_POSHost → VESCSet rotor position in degrees (int32, scale 1000000)
10COMM_SET_HANDBRAKEHost → VESCSet handbrake current (int32, scale 1000)
84COMM_SET_CURRENT_RELHost → VESCSet relative current -1.0 to 1.0 (int32, scale 100000)

Telemetry commands

IDNameDirectionDescription
4COMM_GET_VALUESHost → VESCRequest full motor/system telemetry
50COMM_GET_VALUES_SELECTIVEHost → VESCRequest a bitmask-selected subset of values
47COMM_GET_VALUES_SETUPHost → VESCRequest setup values
22COMM_ROTOR_POSITIONVESC → HostRotor position (streamed)

Configuration commands

IDNameDirectionDescription
13COMM_SET_MCCONFHost → VESCWrite motor configuration
14COMM_GET_MCCONFHost → VESCRead motor configuration
15COMM_GET_MCCONF_DEFAULTHost → VESCRead default motor configuration
16COMM_SET_APPCONFHost → VESCWrite app configuration
17COMM_GET_APPCONFHost → VESCRead app configuration
18COMM_GET_APPCONF_DEFAULTHost → VESCRead default app configuration

System commands

IDNameDescription
0COMM_FW_VERSIONGet firmware version
29COMM_REBOOTReboot the device
30COMM_ALIVEKeepalive (no response)
156COMM_SHUTDOWNShut down the device
20COMM_TERMINAL_CMDSend a terminal command string
21COMM_PRINTPrint message from VESC

Firmware update commands

IDNameDescription
1COMM_JUMP_TO_BOOTLOADERJump to bootloader for firmware update
2COMM_ERASE_NEW_APPErase application flash area
3COMM_WRITE_NEW_APP_DATAWrite firmware image chunk

CAN forwarding

IDNameDescription
34COMM_FORWARD_CANForward packet to another VESC on CAN bus by ID
62COMM_PING_CANPing a device on the CAN bus
85COMM_CAN_FWD_FRAMEForward a raw CAN frame
For the complete list of all 159+ commands, see datatypes.h and the handler in comm/commands.c.

UART configuration in VESC Tool

To enable the UART app, go to App Settings -> App to Use and select UART (or a combination that includes UART). Configure the baud rate under App Settings -> UART -> Baud Rate. The default is 115200 bps.
The VESC UART and USB interfaces share the same packet processing path (commands_process_packet). Do not connect both simultaneously from separate hosts, as they will interfere with each other.