| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# _VESC CAN ESP-IDF Component_
This component provides an ESP-IDF driver for controlling and monitoring VESC-based motor controllers over the CAN bus, built on the node-based TWAI driver introduced in ESP-IDF v5.5.
The driver offers a clean interface for sending motor commands and decoding VESC status broadcasts, handling the CAN frame construction, big-endian byte ordering, and fixed-point scaling defined by the VESC protocol.
## Features
- Command transmission across all VESC single-frame targets:
- Duty cycle, RPM, position
- Current, brake current, handbrake current
- Relative variants of current, brake current, and handbrake current
- Off delay support for current commands (`vesc_set_target_off_delay()`)
- Status frame decoding via a drop-in TWAI receive callback (`vesc_can_on_rx_cb()`)
- Decodes all six VESC status broadcasts (`CAN_PACKET_STATUS_1` through `CAN_PACKET_STATUS_6`) into a single `vesc_data_t` struct
- Argument validation with `ESP_ERR_INVALID_ARG` on out-of-range values
- Integer-only decoded output, no floating point in the receive path
- Configurable via Kconfig
- Built on the node-based TWAI driver (`esp_twai.h` / `esp_twai_onchip.h`)
## Component Structure
```bash
vesc-can/
├── include/
│ └── vesc-can.h # Public API
├── src/
│ └── vesc-can.c # Implementation
├── Kconfig.projbuild # Configuration options
├── CMakeLists.txt # Component build definition
├── idf_component.yml # Component manifest for ESP Registry
└── examples/
├── set-commands/ # Sending motor commands
└── feedback-receive/ # Receiving and decoding status frames
```
## Configuration (Kconfig)
This component uses the following Kconfig options:
| Setting | Description | Default |
| ------------------------------- | ---------------------------- | ------- |
| `CONFIG_VESC_CAN_BUS_FREQUENCY` | CAN bus frequency (kHz) | `1000` |
| `CONFIG_VESC_ID` | VESC ID over the CAN bus | `0x60` |
Modify these using `idf.py menuconfig → VESC CAN Configuration`.
`CONFIG_VESC_ID` must match the value set in VESC Tool under App Settings → General → VESC ID.
## API
### Sending Commands
```c
esp_err_t vesc_set_target(twai_node_handle_t *handle, uint8_t vesc_id,
vesc_target_t target, float value);
esp_err_t vesc_set_target_off_delay(twai_node_handle_t *handle, uint8_t vesc_id,
vesc_target_t target, float value,
float off_delay);
```
Valid targets and their value ranges:
| Target | Unit | Range |
| ---------------------------- | ------- | ------------------- |
| `VESC_DUTY` | ratio | -1.0 to 1.0 |
| `VESC_CURRENT` | A | Motor limit |
| `VESC_CURRENT_BRAKE` | A | Motor limit |
| `VESC_RPM` | RPM | Motor limit |
| `VESC_POSITION` | degrees | 0 to 360 |
| `VESC_CURRENT_REL` | ratio | -1.0 to 1.0 |
| `VESC_CURRENT_BRAKE_REL` | ratio | -1.0 to 1.0 |
| `VESC_CURRENT_HANDBRAKE` | A | Motor limit |
| `VESC_CURRENT_HANDBRAKE_REL` | ratio | -1.0 to 1.0 |
Values bounded by the motor limit are not range-checked by the driver; the VESC firmware truncates them to its configured maximum.
`vesc_set_target_off_delay()` accepts only `VESC_CURRENT` and `VESC_CURRENT_REL`. The off delay is specified in seconds and keeps the current controller running after the commanded current falls below the minimum.
### Receiving Status Frames
```c
bool vesc_can_on_rx_cb(twai_node_handle_t handle,
const twai_rx_done_event_data_t *edata, void *user_ctx);
```
Assign directly to `twai_event_callbacks_t.on_rx_done`, passing a pointer to a `vesc_data_t` as the user context:
```c
twai_event_callbacks_t can_callbacks = {
.on_rx_done = vesc_can_on_rx_cb,
};
vesc_data_t motor;
ESP_ERROR_CHECK(twai_node_register_event_callbacks(node_handle, &can_callbacks, &motor));
```
The callback returns `true` when a recognized status frame was decoded, `false` otherwise.
### Decoded Data
`vesc_data_t` fields, populated as the corresponding status frames arrive:
| Field | Unit | Source |
| ------------------------------ | ------- | ----------------- |
| `erpm` | RPM | `STATUS_1` |
| `current` | mA | `STATUS_1` |
| `duty_cycle` | % | `STATUS_1` |
| `amp_hours_used` | mAh | `STATUS_2` |
| `amp_hours_regen` | mAh | `STATUS_2` |
| `watt_hours_used` | mWh | `STATUS_3` |
| `watt_hours_regen` | mWh | `STATUS_3` |
| `mosfet_temp` | °C | `STATUS_4` |
| `motor_temp` | °C | `STATUS_4` |
| `input_current` | mA | `STATUS_4` |
| `pid_pos` | degrees | `STATUS_4` |
| `tacho` | erev | `STATUS_5` |
| `input_voltage` | mV | `STATUS_5` |
| `adc1_voltage` to `adc3_voltage` | mV | `STATUS_6` |
| `ppm` | % | `STATUS_6` |
| `vesc_id` | — | All status frames |
Status messages must be enabled in VESC Tool under App Settings → General → CAN Status Messages Rate.
## Usage
Refer to the `/examples` directory for:
- set-commands: Sending current and RPM commands to a VESC
- feedback-receive: Receiving and decoding status broadcasts
## Notes
- Requires ESP-IDF ≥ 5.5 for the node-based TWAI driver. The legacy `driver/twai.h` driver is not compatible and must not be used alongside it.
- A CAN transceiver is required between the ESP and the VESC. The ESP cannot drive the differential bus directly.
- The VESC stops the motor if no CAN frame is received within its configured timeout (0.5 s by default). Re-send commands at a fixed rate to prevent this.
- `vesc_can_on_rx_cb()` runs in ISR context. Keep any additional processing short.
- The CAN bitrate configured here must match the VESC's CAN baud rate setting.
- Tested on ESP32-S3 with two Flipsky FSESC 6.7 Mini Pro controllers on the bus at 1 Mbit/s, in both transmit and receive.
## References
- [VESC CAN-Bus Communication documentation](https://github.com/vedderb/bldc/blob/master/documentation/comm_can.md)
- [VESC6 CAN Commands and Telemetry](https://vesc-project.com/sites/default/files/imce/u15301/VESC6_CAN_CommandsTelemetry.pdf)
## License
Licensed under the **MIT License**. \
See the [LICENSE](LICENSE) file for full text.
## Maintainer
**Author:** Adithya Venkata Narayanan ([Adithya-187326](https://github.com/adithya-187326))
**Version:** 1.0.0
**Registry:** ESP Component Registry
idf.py add-dependency "adithya-187326/vesc-can^1.0.1"