A fullstack embedded RC car project built around the Freenove ESP32 WROVER. This repository contains both the C++ firmware (motor control, sensors, asynchronous web server) and the React + Vite frontend web app used as a remote control interface.
The UI is compiled into a Single Page Application (SPA) and served directly from the ESP32's onboard LittleFS filesystem, creating a fully standalone system.
- Real-time WebSocket Control: Low-latency differential drive steering via a virtual joystick.
- Smart Safety Systems: Features a hardware Watchdog (stops motors on connection loss) and an active collision prevention system using the HC-SR04 ultrasonic sensor.
- Dynamic Network Setup: Supports both Station (STA) mode to connect to your home Wi-Fi, and a fallback Access Point (AP) mode if the network is unavailable.
- Onboard State Persistence: Wi-Fi credentials and admin access data are securely stored in the ESP32's NVS (Non-Volatile Storage).
- Visual Status Indicators: Onboard LEDs provide real-time feedback for turn signals (left/right), hazard lights, and a dedicated brake/stopped indicator.
- Board: Freenove ESP32 WROVER
- Drive: 2 DC Motors (Differential Drive) + L298N Motor Driver (H-bridge)
- Sensors: HC-SR04 Ultrasonic distance sensor
- Power: 2x 18650 Li-Ion battery pack (7.4V) for stable Wi-Fi and motor current (Highly Recommended)
C++/Arduino Framework/PlatformIOESPAsyncWebServer&AsyncTCP(Async HTTP & WebSockets)ArduinoJson(Serialization) &LittleFS(File system)
React/Vite/TypeScriptZustand(State management + LocalStorage persistence)nipplejs(Virtual joystick handling)
This project uses specific wiring configurations to ensure the safety of the ESP32 WROVER module and stable operation of the motors. Please pay close attention to the power supply and sensor logic levels.
DC Motors draw significant current spikes, which can cause the ESP32 to restart (brownout) if the power source cannot handle the load. This project supports two power configurations:
- Recommended (Single Power Source): Use a 2S Li-Ion battery pack (two 18650 cells, 7.4V - 8.4V). Connect it to the
12VandGNDterminals of the L298N. Because the voltage is above 7V, the L298N's onboard regulator can generate a stable 5V. You can then power the ESP32 by connecting the L298N's5Voutput terminal to the ESP32'sVINor5Vpin. - Development / Budget (Split Power Source): If using standard AA Alkaline batteries (which drop voltage significantly under load), you must split the power. Power the motors by connecting the AA pack (6V) to the L298N's
12Vterminal. Power the ESP32 separately via a USB Powerbank.
- Left DC Motor: Connect the two wires to
OUT1andOUT2on the L298N. - Right DC Motor: Connect the two wires to
OUT3andOUT4.
Tip: If a wheel spins backward during forward commands, simply swap its two wires at the OUT terminal block. No code changes needed!
- PWM Control: Remove the default hardware jumpers from the
ENAandENBpins on the L298N to allow the ESP32 to control motor speed via PWM.
The pins below were carefully selected to avoid ESP32 WROVER strapping pins and internal flash/PSRAM collisions.
| Component | Signal | ESP32 GPIO | Notes |
|---|---|---|---|
| Left Motor | ENA |
14 | PWM Speed control |
IN1 |
27 | Direction 1 | |
IN2 |
26 | Direction 2 | |
| Right Motor | ENB |
32 | PWM Speed control |
IN3 |
25 | Direction 1 | |
IN4 |
33 | Direction 2 | |
| HC-SR04 | TRIG_PIN |
13 | 5V Output from ESP32 |
ECHO_PIN |
35 | Must use a Voltage Divider! (See notes below) | |
| LEDs | LED_LEFT |
18 | Left turn signal (Requires 220Ξ© resistor) |
LED_RIGHT |
19 | Right turn signal (Requires 220Ξ© resistor) | |
LED_STOP |
21 | Brake/Stop indicator (Requires 220Ξ© resistor (I used 10kΞ© to decrease brightness)) |
- Common Ground (Mandatory): You must connect a wire between the
GNDterminal of the L298N and anyGNDpin on the ESP32. Without this, the PWM and direction control signals will not work. - HC-SR04 Voltage Divider (Protect Your ESP32): The HC-SR04 sensor is powered by 5V (connect its VCC to the ESP32's
VIN/5Vpin, not 3.3V). However, itsEchopin outputs a 5V signal. The ESP32 logic level is 3.3V. Connecting Echo directly to GPIO 35 will damage the board.- Solution: Build a simple voltage divider using a 1kΞ© and a 2kΞ© resistor:
Sensor Echo Pinβ1kΞ© Resistorβ (Point A) β2kΞ© ResistorβGND.- Connect Point A to ESP32 GPIO 35.
- Bare Wires: Ensure no bare copper wire is exposed outside the screw terminals on the L298N power inputs to prevent catastrophic short circuits from the battery pack.
esp-rc-car/
ββ firmware/ # PlatformIO-based ESP32 firmware
β ββ src/
β β ββ main.cpp # Main logic, web server, and pin definitions
β β ββ testwheels.cpp # Debug script to verify L298N motor wiring
β ββ data/ # Web UI static files (served by ESP32)
β ββ platformio.ini # Firmware build configuration
ββ frontend/ # React + Vite UI project
β ββ src/
β β ββ layout/
β β ββ pages/
β β ββ store/ # Zustand state management
β β ββ App.tsx
β ββ package.json
β ββ vite.config.ts
ββ README.md
This project consists of two parts: a React frontend (web interface) and a C++ backend firmware for the ESP32. You will need Node.js and VS Code with the PlatformIO extension installed.
First, you need to build the web application so it becomes a set of static files that will later be stored in the device memory.
- Go to the frontend directory:
cd frontend- Install dependencies:
npm install- Build the project (the output will be generated directly into
firmware/data/instead offrontend/dist/due to the configuration invite.config.ts):
npm run buildNow you can flash the microcontroller. Make sure you open the firmware folder as the workspace in VS Code when using PlatformIO.
- Connect the ESP32 to your computer via USB.
- Open the
firmwarefolder in VS Code. - In the PlatformIO sidebar, open your environment menu (for example,
env:esp32dev). - Build File System Image: First, build the SPIFFS/LittleFS image that contains the web interface:
- Run:
PlatformIO> Build Filesystem Image
- Run:
- Upload File System Image: Then upload the generated filesystem image to the ESP32:
- Run:
PlatformIO> Upload Filesystem Image
- Run:
- Upload Firmware: After the file system upload completes, flash the actual firmware:
- Click the Upload button (arrow
βin the bottom bar of VS Code) or runGeneral > Upload.
- Click the Upload button (arrow
- After flashing successfully, the ESP32 will create its own Wi-Fi network.
- Connect to the network
Freenove_Car_AP(default password:12345678). - Open a browser and navigate to
http://esp-car.local(orhttp://192.168.4.1). - Use the default login credentials:
admin/admin. - Go to "Settings" to connect the car to your home Wi-Fi network.
If your motors are not spinning or are spinning in the wrong direction, you can isolate hardware issues using the provided debug script before running the full web server.
- In
firmware/src/, temporarily renamemain.cpptomain_backup.cpp. - Rename
testwheels.cpptomain.cpp. - Build and upload.
- This minimal script will cycle the motors forward and backward, allowing you to easily verify your L298N wiring, common ground, and power supply without any Wi-Fi or WebSockets overhead.
If you found this project helpful, consider starring the repository.