diff --git a/include/config.h b/include/config.h index 820ceef..fc54a99 100644 --- a/include/config.h +++ b/include/config.h @@ -33,8 +33,8 @@ #define SENSE_RESISTOR 1000.0f // 1kΩ sense resistor (ohms) #define ADC_MAX 4095.0f // 12-bit ADC max value #define ADC_VREF 3.3f // ADC reference voltage -#define STALL_CURRENT_THRESHOLD 5.0f // Current (amps) that indicates stall -#define STALL_DETECT_TIME_MS 500 // Time to confirm stall (ms) +#define STALL_CURRENT_THRESHOLD 4.0f // Current (amps) that indicates stall +#define STALL_DETECT_TIME_MS 2500 // Time to confirm stall (ms) - accounts for startup inrush // Web Server #define HTTP_PORT 80 diff --git a/readme.md b/readme.md index dd65463..6844a50 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,108 @@ -### inital prompt +# BTS7960 Motor Controller -start platformio project. -using esp32 lolin32 rev1. -we will connect a dual h-bdrige module based on BTS7960 module. -12v dc input. +ESP32-based DC motor controller with web interface, using BTS7960 dual H-bridge driver with current sensing and stall protection. -module guide - https://deepbluembedded.com/arduino-bts7960-dc-motor-driver/ +## Hardware + +### Components + +| Component | Model/Specification | +|-----------|---------------------| +| Microcontroller | ESP32 LOLIN32 Rev1 | +| Motor Driver | BTS7960 Dual H-Bridge Module | +| Power Supply | 12V DC | +| Sense Resistors | 2× 1kΩ (for current sensing) | + +### BTS7960 Module Reference + +- [DeepBlue Embedded Guide](https://deepbluembedded.com/arduino-bts7960-dc-motor-driver/) +- [BTN7960 Datasheet](https://www.infineon.com/dgdl/Infineon-BTN7960-DS-v01_01-EN.pdf) + +## Wiring + +### Motor Control Pins + +| BTS7960 Pin | ESP32 GPIO | Function | +|-------------|------------|----------| +| RPWM | GPIO25 | Forward PWM | +| LPWM | GPIO26 | Reverse PWM | +| R_EN | GPIO27 | Right Enable | +| L_EN | GPIO14 | Left Enable | +| VCC | 3.3V | Logic Power | +| GND | GND | Ground | + +### 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) ─┼──────┬───────┤GPIO34│ +│ │ │ │ │ +│ │ [R1] │ │ +│ │ 1kΩ │ │ +│ │ │ │ │ +│ GND ─┼──────┴───────┤GND │ +│ │ │ │ +│ L_IS (pin 8) ─┼──────┬───────┤GPIO35│ +│ │ │ │ │ +│ │ [R2] │ │ +│ │ 1kΩ │ │ +│ │ │ │ │ +│ GND ─┼──────┴───────┤GND │ +└─────────────────┘ └──────┘ +``` + +| Connection | Details | +|------------|---------| +| R_IS → GPIO34 | Through 1kΩ resistor to GND | +| L_IS → GPIO35 | Through 1kΩ resistor to GND | + +**Note:** GPIO34 and GPIO35 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`](include/config.h): + +| Setting | Default | Description | +|---------|---------|-------------| +| `STALL_CURRENT_THRESHOLD` | 4.0A | Current triggering stall detection | +| `STALL_DETECT_TIME_MS` | 500ms | Duration before stall confirmed | +| `PWM_FREQ` | 20kHz | PWM frequency (reduces motor noise) | +| `CURRENT_SENSING_ENABLED` | true | Enable/disable in [`src/motor.cpp`](src/motor.cpp) | + +## Network + +| Setting | Value | +|---------|-------| +| WiFi SSID | tami | +| Static IP | 10.81.2.185 | +| HTTP Port | 80 | + +## Build & Upload + +```bash +pio run # Build +pio run -t upload # Build and upload +pio device monitor # Serial monitor (115200 baud) +``` + +## Stall Protection + +When motor current exceeds 4.0A for 500ms continuously: +1. Stall is detected +2. Motor stops immediately +3. Serial log: `STALL PROTECTION: Stopping motor (current: X.XXA)` + +To resume operation, send a new speed/direction command via the web interface. diff --git a/src/main.cpp b/src/main.cpp index cba2dc5..ce0c250 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,6 +33,12 @@ void setupWiFi() { } } +// Stall protection callback - stops motor immediately when stall detected +void onMotorStall(float current) { + Serial.printf("STALL PROTECTION: Stopping motor (current: %.2fA)\n", current); + motor.stop(); +} + void setup() { Serial.begin(115200); delay(1000); @@ -44,6 +50,9 @@ void setup() { // Initialize motor controller motor.begin(); + // Register stall protection callback + motor.setStallCallback(onMotorStall); + // Connect to WiFi setupWiFi(); diff --git a/src/motor.cpp b/src/motor.cpp index 83f4652..5269a86 100644 --- a/src/motor.cpp +++ b/src/motor.cpp @@ -1,7 +1,7 @@ #include "motor.h" // Set to true to enable current sensing (requires R_IS and L_IS connected) -#define CURRENT_SENSING_ENABLED false +#define CURRENT_SENSING_ENABLED true MotorController motor; @@ -62,10 +62,19 @@ void MotorController::stop() { void MotorController::update() { #if CURRENT_SENSING_ENABLED + static unsigned long lastPrintTime = 0; + // Read current sensors _currentRight = readCurrentSense(R_IS_PIN); _currentLeft = readCurrentSense(L_IS_PIN); + // Log current readings every 500ms when motor is running + if ((_direction != 0 || _speed != 0) && (millis() - lastPrintTime > 500)) { + lastPrintTime = millis(); + Serial.printf("Current: R=%.2fA L=%.2fA Active=%.2fA (threshold=%.1fA)\n", + _currentRight, _currentLeft, getCurrentActive(), STALL_CURRENT_THRESHOLD); + } + // Check for stall condition checkStall(); #endif diff --git a/src/webserver.cpp b/src/webserver.cpp index cb97731..b795db2 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -28,17 +28,44 @@ const char index_html[] PROGMEM = R"rawliteral( text-align: center; } h1 { color: #00d9ff; margin-bottom: 30px; } - .status { - background: #16213e; - padding: 15px; - border-radius: 10px; + .status { + background: #16213e; + padding: 15px; + border-radius: 10px; margin-bottom: 20px; } - .status span { - font-size: 24px; - font-weight: bold; + .status span { + font-size: 24px; + font-weight: bold; color: #00d9ff; } + .current-display { + background: #16213e; + padding: 15px; + border-radius: 10px; + margin-bottom: 20px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + } + .current-value { + font-size: 28px; + font-weight: bold; + color: #00d9ff; + } + .current-label { + font-size: 12px; + color: #888; + } + .stall-warning { + background: #ff5252; + color: white; + padding: 10px; + border-radius: 5px; + margin-bottom: 20px; + display: none; + } + .stall-warning.active { display: block; } .slider-container { background: #16213e; padding: 20px; @@ -92,6 +119,21 @@ const char index_html[] PROGMEM = R"rawliteral(