940c743029b27a0083dcdd7df9baa77622cb3b4c
Dual BTS7960 Motor Controller
ESP32-based differential drive robot controller with web interface, using two BTS7960 dual H-bridge drivers with live current sensing.
Features
- Web-based dual motor control panel with real-time current monitoring
- Two independent spring-to-center sliders (one per motor), each supporting forward and reverse
- Current sensing on both H-bridge sides for each motor
- ADC offset calibration at startup for accurate current readings
- Emergency stop for both motors
Hardware
Components
| Component | Model/Specification |
|---|---|
| Microcontroller | ESP32 LOLIN32 Rev1 |
| Motor Drivers | 2× BTS7960 Dual H-Bridge Module |
| Power Supply | 12V DC |
| Sense Resistors | 4× 1kΩ (for current sensing) |
BTS7960 Module Reference
Wiring
Pin Assignments
Both drivers share enable pins (they enable/disable together).
| Function | Motor 1 | Motor 2 | Notes |
|---|---|---|---|
| R_EN | GPIO 27 | GPIO 27 (shared) | Both drivers enable together |
| L_EN | GPIO 14 | GPIO 14 (shared) | Both drivers enable together |
| RPWM | GPIO 25 | GPIO 32 | Forward PWM |
| LPWM | GPIO 26 | GPIO 33 | Reverse PWM |
| R_IS | GPIO 34 | GPIO 36 (VP) | Current sense - input only |
| L_IS | GPIO 35 | GPIO 39 (VN) | Current sense - input only |
Wiring Diagram
ESP32 LOLIN32 BTS7960 #1 BTS7960 #2
-------------- ---------- ----------
GPIO 27 (R_EN) ------> R_EN -----------> R_EN
GPIO 14 (L_EN) ------> L_EN -----------> L_EN
GPIO 25 (RPWM) ------> RPWM
GPIO 26 (LPWM) ------> LPWM
GPIO 34 (R_IS) <------ R_IS
GPIO 35 (L_IS) <------ L_IS
GPIO 32 (RPWM) --------------------> RPWM
GPIO 33 (LPWM) --------------------> LPWM
GPIO 36 (R_IS) <-------------------- R_IS
GPIO 39 (L_IS) <-------------------- L_IS
Current Sensing Circuit
The BTS7960 has IS (Current Sense) pins that output current proportional to motor load. A resistor converts this to voltage for ESP32 ADC.
BTS7960 Module ESP32
┌─────────────────┐ ┌──────┐
│ R_IS (pin 7) ─┼──────┬───────┤GPIOxx│ (34 for M1, 36 for M2)
│ │ [R] │ │
│ │ 1kΩ │ │
│ GND ─┼──────┴───────┤GND │
│ │ │ │
│ L_IS (pin 8) ─┼──────┬───────┤GPIOxx│ (35 for M1, 39 for M2)
│ │ [R] │ │
│ │ 1kΩ │ │
│ GND ─┼──────┴───────┤GND │
└─────────────────┘ └──────┘
Note: GPIO34, 35, 36, 39 are input-only pins on ESP32, ideal for ADC readings.
Current Sensing Math
| Parameter | Value | Formula |
|---|---|---|
| Sense Ratio | 8500:1 | I_sense = I_motor / 8500 |
| Sense Resistor | 1kΩ | V_sense = I_sense × R |
| At 4A motor current | 0.47V | (4 / 8500) × 1000 |
| Max readable current | 28A | (3.3V × 8500) / 1000 |
Configuration
Key settings in include/config.h:
| Setting | Default | Description |
|---|---|---|
PWM_FREQ |
20kHz | PWM frequency (reduces motor noise) |
MIN_PWM_PERCENT |
20% | Minimum PWM when motor is running |
CURRENT_LOG_INTERVAL_MS |
100ms | Current sampling/logging interval |
CURRENT_SENSING_ENABLED |
true | Enable/disable in src/motor.cpp |
Network
| Setting | Value |
|---|---|
| WiFi SSID | tami |
| Static IP | 10.81.2.185 |
| HTTP Port | 80 |
Build & Upload
pio run # Build
pio run -t upload # Build and upload
pio device monitor # Serial monitor (115200 baud)
Web Interface
Access the control panel at http://10.81.2.185 (or the IP shown on serial monitor).
Features
- Dual Motor Panels: Side-by-side controls for Motor 1 and Motor 2
- Current Display: Real-time forward (FWD) and reverse (REV) current readings per motor
- Spring-to-Center Sliders: Signed sliders (
-100..100) per motor - Bidirectional Drive: Positive values = forward, negative values = reverse,
0= stop - Emergency Stop: Global button to stop both motors immediately
API Endpoints
| Endpoint | Method | Parameters | Description |
|---|---|---|---|
/ |
GET | - | Control panel HTML page |
/status |
GET | - | JSON with both motors' status |
/motor1/drive |
GET | value (-100..100) |
Set motor 1 speed + direction in one command |
/motor1/speed |
GET | value (0-100) |
Set motor 1 speed percentage |
/motor1/direction |
GET | value (-1, 0, 1) |
Set motor 1 direction |
/motor1/stop |
GET | - | Stop motor 1 |
/motor2/drive |
GET | value (-100..100) |
Set motor 2 speed + direction in one command |
/motor2/speed |
GET | value (0-100) |
Set motor 2 speed percentage |
/motor2/direction |
GET | value (-1, 0, 1) |
Set motor 2 direction |
/motor2/stop |
GET | - | Stop motor 2 |
/stop |
GET | - | Emergency stop (both motors) |
/speed |
GET | value |
Legacy: maps to motor1 |
/direction |
GET | value |
Legacy: maps to motor1 |
Status JSON Format
{
"motor1": {
"speed": 50,
"direction": 1,
"currentR": 2.35,
"currentL": 0.00
},
"motor2": {
"speed": 50,
"direction": -1,
"currentR": 0.00,
"currentL": 1.87
}
}
Control Behavior
- Each motor is controlled by its own signed slider in the web UI.
- Releasing a slider returns it to
0and immediately stops that motor. - Current sensing remains active for both directions on both motors and is shown live in the UI.
Description
Languages
C++
90.2%
C
9.8%