devdesk 4c30610af0 Stop printing current when motor is stopped
- Remove idle/dot printing logic from MotorController::update()
- Only print current readings when direction != 0 (motor running)
- Simplifies logging by completely silencing output when stopped
2026-02-06 15:21:19 +02:00

Dual BTS7960 Motor Controller

ESP32-based differential drive robot controller with web interface, using two BTS7960 dual H-bridge drivers with current sensing and stall protection.

Features

  • Web-based dual motor control panel with real-time current monitoring
  • Independent Forward/Reverse/Stop control for each motor with speed sliders (20-100%)
  • Current sensing on both H-bridge sides for each motor
  • Sample-based stall detection with automatic motor shutoff (per motor)
  • ADC offset calibration at startup for accurate current readings
  • Stall warning displayed on web interface per motor
  • 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
STALL_THRESHOLD 8.0A Current threshold for stall detection
STALL_CONFIRM_SAMPLES 3 Number of consecutive samples to confirm stall (~300ms)
STALL_STABILIZE_MS 500ms Ignore current spikes after direction change
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 (Left) and Motor 2 (Right)
  • Current Display: Real-time forward (FWD) and reverse (REV) current readings per motor
  • Direction Status: Shows FORWARD, REVERSE, or STOPPED per motor
  • Speed Sliders: Adjustable from 20% to 100% per motor
  • Stall Warning: Red banner per motor when stall is detected
  • 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/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/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,
    "stalled": false
  },
  "motor2": {
    "speed": 50,
    "direction": -1,
    "currentR": 0.00,
    "currentL": 1.87,
    "stalled": false
  }
}

Stall Protection

The stall detection uses a sample-based approach for reliability (independent per motor):

  1. Threshold: Current above 8.0A indicates potential stall (based on observed ~2A running vs ~17A stalled)
  2. Debounce: 3 consecutive samples above threshold confirms stall (~300ms at 100ms intervals)
  3. Stabilization: Ignores current spikes for 500ms after direction changes

When stall is confirmed on a motor:

  1. That motor stops immediately
  2. Serial log: MotorX STALL DETECTED! Current=X.XXA (threshold=8.0A)
  3. Web interface shows red "STALL!" warning on that motor's panel

To resume operation, send a new direction command via the web interface.

Description
a crawling platform
Readme 331 KiB
Languages
C++ 90.2%
C 9.8%