Code for my ESP32 Rover Control Project via ELRS and CAN
  • C++ 90.9%
  • C 9.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-03 21:13:01 +02:00
include Initial commit: ESP32 CRSF/CAN rover firmware 2026-08-03 11:30:31 +02:00
legacy code Slip Control und Websteuerungsverbesserungen 2026-08-13 13:36:31 +02:00
lib Initial commit: ESP32 CRSF/CAN rover firmware 2026-08-03 11:30:31 +02:00
src add readme and final commit 2026-09-03 21:13:01 +02:00
test Initial commit: ESP32 CRSF/CAN rover firmware 2026-08-03 11:30:31 +02:00
.gitignore Initial commit: ESP32 CRSF/CAN rover firmware 2026-08-03 11:30:31 +02:00
platformio.ini Rework von Steuerung und Web UI 2026-08-11 22:55:34 +02:00
README.md add readme and final commit 2026-09-03 21:13:01 +02:00

ESP32 Dual-VESC Rover Controller

Firmware for an experimental two-wheel rover using an ESP32, an ELRS/CRSF radio link and two VESC motor controllers connected through CAN/TWAI.

The controller implements differential steering, four drive modes, configurable speed limiting, acceleration/deceleration ramping, safety handling, traction control, a serial diagnostic console, OTA updates and a browser-based fallback controller.

Project status: Experimental / under active development. This software controls real motors and can cause physical movement. Test with the drive wheels lifted from the ground before operating the rover normally.

Overview

The rover uses an ESP32 as its central controller.

Control data is normally received from an ELRS receiver using the CRSF protocol. The ESP32 converts throttle and steering inputs into separate target eRPM values for the left and right motors and transmits them to two VESCs over CAN.

The firmware currently provides:

  • four configurable drive modes
  • user-adjustable speed limiting
  • differential/skid steering
  • speed-dependent steering sensitivity
  • proportional acceleration and braking ramps
  • CRSF link-loss failsafe
  • arm/disarm handling
  • Mode 1 traction/slip control
  • buzzer and status LED support
  • auxiliary relay output
  • serial debug/test console
  • Wi-Fi access point
  • ArduinoOTA firmware updates
  • browser-based fallback control
  • WebSocket status and serial-log output

Hardware

Current design:

  • ESP32-based controller
  • ELRS receiver using CRSF
  • two VESC motor controllers
  • two brushless direct-drive hub motors
  • CAN/TWAI connection between ESP32 and VESCs
  • buzzer
  • status LED
  • auxiliary relay

Pin configuration

Function GPIO
CAN TX 2
CAN RX 3
ELRS RX 0
ELRS TX 1
Buzzer 45
Status LED 47
Relay 48

These are the currently intended rover pin assignments.


VESC configuration

The firmware communicates with the VESCs using extended CAN frames.

Current VESC IDs:

Motor VESC ID
Left 1
Right 2

CAN bitrate:

500 kbit/s

Motor commands are transmitted every:

20 ms

The firmware uses VESC RPM commands.

The values referred to as RPM in this project are electrical RPM (eRPM).

They must not be interpreted as mechanical wheel RPM.

Conversion between eRPM, mechanical RPM and vehicle speed depends on motor pole-pair count and wheel dimensions.


CRSF channel mapping

Channel Function
1 Steering
2 Throttle
3 Speed limit
4 unused
5 Buzzer enable
6 Drive mode
7 Arm
8 Relay
9 Mode 4 / Kick
10 unused

The firmware currently assumes approximately:

Minimum: 988
Center:  1500
Maximum: 2012

Drive modes

Three positions of the mode switch select Modes 13.

Mode 4 is activated by holding the Kick button while Mode 3 is selected.

Mode Maximum eRPM Steering sensitivity Ramp limit
1 1,000 2.0 50 eRPM/tick
2 4,000 1.0 200 eRPM/tick
3 6,000 0.2 300 eRPM/tick
4 10,000 0.1 500 eRPM/tick

Mode 1 is intended as the conservative low-speed maneuvering mode and is also the only mode in which traction control is enabled.

The speed-limit control on CRSF channel 3 scales the maximum allowed motor eRPM between zero and the maximum of the selected mode.

Direction handling

The current firmware does not automatically reverse the driving direction in Modes 3 or 4.

Motor direction must therefore be configured and tested on the physical rover.

Support for explicitly inverting the right motor is present in the source but currently disabled.


Steering

The rover uses differential steering.

The basic mixer works as follows:

Left motor  = throttle + steering
Right motor = throttle - steering

The result is normalized when necessary to prevent either motor command from exceeding the allowed range.

Steering sensitivity depends on the selected drive mode.

Steering authority is additionally reduced as motor speed increases. This allows strong steering response during low-speed maneuvering while reducing aggressive differential steering at higher speeds.


Acceleration and braking ramp

Motor commands do not immediately jump to the requested target eRPM.

The controller gradually moves the current command toward the requested target.

The proportional component currently uses:

12% of the remaining difference per 20 ms update

Each drive mode additionally defines a hard maximum change per update.

This produces relatively large changes when the current speed is far away from the requested target and progressively smaller changes when approaching it.


Traction control

Experimental traction/slip control is active only in Mode 1.

Because the VESCs operate in RPM-control mode, the controller monitors motor current independently for each wheel.

A moving current baseline represents the recent normal load of each motor.

If the measured current drops substantially below its previous baseline while a meaningful target eRPM is requested, the controller interprets this as possible loss of traction.

The target eRPM of the affected motor is then reduced.

Current parameters:

Minimum target eRPM:       100
Minimum current baseline:  200 mA
Current-drop threshold:    50 %
RPM reduction factor:      0.4
Baseline EMA factor:       0.05

This system is experimental and must be tuned using the physical rover.


Safety and failsafe

Normal CRSF motor control requires:

  1. an active CRSF link
  2. the Arm channel to be enabled
  3. CRSF communication to remain inside the configured timeout

Current CRSF timeout:

500 ms

If these conditions are not satisfied, both VESC targets are set to zero.

The auxiliary relay is also disabled during the normal failsafe condition.

The status LED is switched off when the radio link is completely unavailable and blinks when a link exists but normal driving conditions are not satisfied.

Physical safety

Firmware safety systems are not substitutes for physical safety measures.

Initial motor testing should be performed with:

  • the driven wheels lifted from the ground
  • an accessible main power disconnect
  • conservative VESC current limits
  • conservative motor-speed limits
  • sufficient free space around the rover

Web fallback controller

The ESP32 hosts a browser interface containing:

  • rover status
  • virtual joystick
  • fallback-control enable switch
  • live firmware log
  • current left/right eRPM
  • CRSF link status
  • arm state
  • relay state
  • buzzer state

Web fallback control is only accepted when the normal RC link is unavailable.

When the CRSF link returns, web control is automatically disabled and radio control receives priority.

Fallback control is restricted to Mode 1.

A command watchdog requires fresh web-control messages within:

300 ms

Wi-Fi and OTA

The ESP32 creates its own Wi-Fi access point.

Before compiling the firmware, configure:

#define WIFI_SSID "SET OWN SSID"
#define WIFI_PASSWORD "SET OWN PASSWORD"
#define OTA_PASSWORD "SET OWN PASSWORD"

Replace these values with your own credentials in your local source tree if required.

Do not commit personal or production credentials to a public repository.

The web interface is served on:

http://<ROVER-IP>/

The ESP32 prints its access-point IP address to the serial console after Wi-Fi startup.

ArduinoOTA is also enabled on the access point.


Building with PlatformIO

The firmware is intended to be built using PlatformIO.

From the project directory:

pio run

Clean the build directory with:

pio run -t clean

Flashing over USB

Connect the ESP32 using USB.

PlatformIO can normally detect the appropriate serial port automatically:

pio run -t upload

Available serial devices can be displayed with:

pio device list

A port can also be specified explicitly.

Linux example:

pio run -t upload --upload-port /dev/ttyACM0

or:

pio run -t upload --upload-port /dev/ttyUSB0

Windows example:

pio run -t upload --upload-port COM3

After flashing, the serial monitor can be started with:

pio device monitor

The rover firmware currently uses:

115200 baud

Flashing over Wi-Fi / OTA

The rover firmware supports ArduinoOTA.

The computer performing the update must first be connected to the Wi-Fi access point created by the rover.

The rover prints its current IP address to the serial console when the access point starts.

PlatformIO requires an OTA upload environment using the espota upload protocol.

A PlatformIO environment can for example contain:

[env:rover-ota]
platform = espressif32
board = YOUR_BOARD
framework = arduino

upload_protocol = espota
upload_port = ROVER_IP

upload_flags =
    --auth=YOUR_OTA_PASSWORD

ROVER_IP must be replaced with the IP address printed by the rover.

YOUR_OTA_PASSWORD must match the value configured as:

OTA_PASSWORD

The firmware can then be compiled and uploaded over WLAN using:

pio run -e rover-ota -t upload

The IP address can also be supplied directly:

pio run -e rover-ota -t upload --upload-port ROVER_IP

For example:

pio run -e rover-ota -t upload --upload-port 192.168.x.x

The first firmware installation should be performed using USB. OTA can only be used once firmware containing the OTA functionality is already running on the ESP32.


Serial debug console

Debug functionality is controlled by:

#define DEBUG_LEVEL

Current levels:

Level Behavior
0 Debugging disabled
1 Periodic information output
2 Information + verbose diagnostics + interactive console

The current firmware uses:

#define DEBUG_LEVEL 1

The interactive console is therefore present in the firmware but only processed when DEBUG_LEVEL is changed to 2.

Available commands:

HELP

STATUS

MANUAL ON
MANUAL OFF

RPM L <value>
RPM R <value>
RPM BOTH <value>

BUZZER <frequency>
BUZZER OFF

LED ON
LED OFF

RELAY ON
RELAY OFF

OTA ON
OTA OFF

Manual motor test mode

MANUAL ON disables normal CRSF motor control and allows direct eRPM target commands through the serial console.

Example:

MANUAL ON
RPM BOTH 500
RPM BOTH 0
MANUAL OFF

Commands are restricted by the firmware's absolute eRPM limit.

The manual-control watchdog automatically leaves manual mode when no command has been received for:

30 seconds

Motor testing should only be performed with the rover physically secured.


Software dependencies

The firmware currently uses:

  • Arduino framework
  • AlfredoCRSF
  • ESP32 TWAI driver
  • WiFi
  • ArduinoOTA
  • ESPAsyncWebServer
  • ArduinoJson

The exact board, platform and library dependencies are defined through the PlatformIO project configuration.


Important configuration values

Most rover-specific configuration is located near the beginning of main.cpp.

Important values include:

CAN_TX_PIN
CAN_RX_PIN

ELRS_RX_PIN
ELRS_TX_PIN

VESC_ID_LEFT
VESC_ID_RIGHT

BUZZER_PIN
LED_PIN
RELAY_PIN

MODE1_MAX_RPM
MODE2_MAX_RPM
MODE3_MAX_RPM
MODE4_MAX_RPM

STEERING_SENSE_MODE1
STEERING_SENSE_MODE2
STEERING_SENSE_MODE3
STEERING_SENSE_MODE4

CRSF_TIMEOUT_MS

WIFI_SSID
WIFI_PASSWORD
OTA_PASSWORD

Development status

The project is experimental and under active development.

Important areas still requiring real-world validation and tuning include:

  • physical motor direction
  • VESC CAN configuration
  • motor pole-pair count
  • eRPM-to-wheel-speed conversion
  • maximum safe eRPM
  • steering sensitivity
  • acceleration and braking behavior
  • traction-control thresholds
  • CRSF failsafe behavior under real link loss
  • web fallback behavior
  • OTA/update recovery behavior

The comments inside main.cpp are also still undergoing cleanup and may contain outdated development notes. The actual code should be considered authoritative where comments and implementation disagree.


License

No license has been selected yet.

Until a license file is added, normal copyright restrictions apply even if the source repository is publicly accessible.