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 0 and immediately stops that motor.
  • Current sensing remains active for both directions on both motors and is shown live in the UI.
Description
a walker
Readme 331 KiB
Languages
C++ 90.2%
C 9.8%