To safely control a DC motor with an Arduino, you cannot connect the motor directly to the microcontroller's GPIO pins. An Arduino Uno's ATmega328P pins are limited to 20mA of continuous current, while even a small hobby DC motor draws hundreds of milliamps to several amps. Furthermore, the inductive kickback (back-EMF) generated when a motor stops can instantly fry the silicon. The direct answer is that you must use a motor driver IC (an H-bridge) acting as a current amplifier, controlled via the Arduino's PWM and digital logic pins. But picking the right motor and the right driver requires matching the physics of your mechanical load to the electrical limits of your silicon.
Selecting the DC Motor: Torque Curves and Load Profiles
Before writing a single line of PWM code, you must select a motor that fits your mechanical load profile. Treating all 'DC motors' as interchangeable is a common bench mistake that leads to stalled mechanisms and melted driver boards. While stepper motors and servos are technically DC-powered, they rely on entirely different commutation and feedback architectures; this guide focuses strictly on continuous-rotation DC motors.
The table below breaks down the four primary DC motor topologies you will encounter in embedded projects, detailing their torque delivery, control complexity, and typical use cases.
| Motor Type | Torque Curve & Characteristics | Control Needs & Driver Architecture | Relative Cost & Best Application |
|---|---|---|---|
| Brushed Iron-Core | High starting torque, linear speed-to-voltage ratio. Torque drops slightly as RPM increases due to brush friction and inductance. | Simple H-bridge (PWM for speed, 2 pins for direction). No position feedback without an external encoder. | $2 - $15. Drive wheels, simple conveyors, winches. |
| Brushed Coreless | Extremely low rotor inertia, near-instant acceleration. Torque is proportional to current but limited by thermal mass. | Standard H-bridge, but requires high-frequency PWM (>20kHz) to prevent acoustic whine and overheating the delicate windings. | $10 - $40. RC servos, camera gimbals, precision linear actuators. |
| Brushless DC (BLDC) | Flat torque curve across a wide RPM range. High efficiency, no brush wear, but suffers from cogging torque at very low speeds. | Requires a 3-phase ESC (Electronic Speed Controller) or a dedicated FOC (Field Oriented Control) driver like the SimpleFOC shield. Cannot use a standard 2-wire H-bridge. | $25 - $100+. Drones, high-speed spindles, continuous-duty robotics. |
| Brushed Gearmotor | Massive low-speed torque due to planetary or spur reduction. High static friction (stiction) means it won't move until PWM crosses a specific threshold. | Standard H-bridge. Requires a 'kickstart' PWM spike in code to overcome stiction before dropping to the holding speed. | $15 - $60. Robotic arm joints, tank treads, heavy linear lifts. |
Sizing the Driver: Match the Silicon to the Stall Current
The most frequent cause of dead motor drivers in hobbyist projects is sizing the H-bridge based on the motor's rated continuous current rather than its stall current. When a motor starts from a dead stop, or jams against a mechanical limit, it draws stall current. This can be 5 to 10 times higher than the running current.
The Sizing Rule of Thumb
Use this engineering baseline when selecting your driver IC:
- Driver Continuous Current ≥ 1.5 × Motor Rated Load Current
- Driver Peak Current ≥ Motor Stall Current (for at least 1-2 seconds)
Worked Load Example: 12V Conveyor Belt
Suppose you are building a small sorting conveyor driven by a 12V brushed gearmotor. The motor datasheet lists a rated continuous current of 1.5A and a stall current of 6.5A.
Applying the rule: Your driver must handle at least 2.25A continuously (1.5A × 1.5) and survive a 6.5A peak during startup. If you use the ubiquitous L298N driver (rated for 2A continuous, 3A peak), the startup inrush will trigger its internal thermal shutdown, or worse, melt the BJT junctions. You need a MOSFET-based driver like the BTS7960 or a dual Texas Instruments DRV8871 configuration.
| Driver IC / Module | Architecture & Voltage Drop | Continuous / Peak Current | Logic Voltage & Cost |
|---|---|---|---|
| L298N | Bipolar Junction Transistor (BJT). High voltage drop (~2.0V lost as heat). | 2.0A Cont / 3.0A Peak | 5V Logic. ~$3 - $5 |
| TB6612FNG | MOSFET. Low voltage drop (~0.5V). Excellent for battery-powered robots. See Pololu TB6612FNG carrier. | 1.2A Cont / 3.2A Peak | 2.7-5.5V Logic. ~$5 - $8 |
| DRV8871 | MOSFET. Single H-bridge, integrated protection, very low Rds(on). | 3.6A Cont / 5.0A Peak | 3.3-5V Logic. ~$6 - $10 |
| BTS7960 | High-Power MOSFET. Half-bridge pairs. Requires massive heatsinking at high loads. | 20A Cont / 43A Peak | 5.5V Logic (Optoisolated). ~$12 - $18 |
Wiring, Terminal Identification, and Code Implementation
Let's wire the high-power BTS7960 module to an Arduino Uno to drive our 12V conveyor gearmotor. The BTS7960 modules found online typically feature optoisolators to protect the Arduino from electrical noise, but they have specific terminal requirements.
Terminal Identification (BTS7960 Module)
- B+ / B-: High-current motor power supply (e.g., 12V battery). Keep these wires thick (14 AWG or larger for >10A) and short.
- M+ / M-: Motor output terminals.
- VCC / GND: Logic power for the optoisolators. Connect to Arduino 5V and GND.
- R_EN / L_EN: Enable pins. Jumper these together and tie to Arduino 5V (or a digital pin if you want a master hardware kill-switch).
- R_PWM / L_PWM: Speed control. Tie one to an Arduino PWM pin (e.g., Pin 5). Tie the other to GND for unidirectional control, or to a second PWM pin for bidirectional.
- R_IS / L_IS: Current sense analog output. Connect to an Arduino Analog pin (e.g., A0) to read motor load and detect stalls in software.
Arduino PWM Control Code
The default PWM frequency on Arduino Uno pins 5 and 6 is 980Hz, while pins 3, 9, 10, and 11 run at 490Hz. A 490Hz frequency will cause many DC motors to emit an audible, annoying whine. The code below uses Pin 5 (980Hz) and includes a soft-start ramp to prevent inrush current brownouts.
// Pin Definitions
const int PWM_PIN = 5; // 980Hz PWM on Uno
const int EN_PIN = 8; // Enable pin
const int IS_PIN = A0; // Analog current sense
// Motor Parameters
const int TARGET_SPEED = 200; // 0-255 PWM
const int STALL_THRESHOLD = 800; // Analog read value indicating stall
void setup() {
pinMode(PWM_PIN, OUTPUT);
pinMode(EN_PIN, OUTPUT);
Serial.begin(115200);
digitalWrite(EN_PIN, HIGH); // Enable driver optoisolators
delay(100);
}
void loop() {
// Soft-start ramp to limit inrush current
for (int speed = 0; speed <= TARGET_SPEED; speed += 5) {
analogWrite(PWM_PIN, speed);
delay(20); // 20ms ramp step
}
// Monitor for stall conditions
int currentSense = analogRead(IS_PIN);
if (currentSense > STALL_THRESHOLD) {
analogWrite(PWM_PIN, 0); // Cut power to prevent motor burnout
Serial.println("STALL DETECTED - Power Cut");
while(1); // Halt until reset
}
delay(100);
}
Diagnosing Failure Signatures: Hum, Overheat, and Stall
When your circuit is powered but the mechanics aren't behaving, the motor and driver will give you physical feedback. Learning to read these failure signatures saves hours of multimeter probing.
1. The Acoustic Hum (Without Movement)
Symptom: The motor emits a loud buzzing or humming sound but the shaft doesn't turn.
Cause: This is usually a stall condition where the load exceeds the motor's starting torque, OR the PWM frequency is too low, causing the motor windings to act like a speaker. If the hum happens at very low PWM values (e.g., `analogWrite(pin, 30)`), you are hitting the motor's static friction threshold. The voltage is high enough to magnetize the coils, but not high enough to break stiction.
Fix: Implement a 'kickstart' routine in your code. Send a PWM value of 255 for 50 milliseconds to break static friction, then immediately drop to your desired low-speed PWM value.
2. Driver Overheat and Thermal Shutdown
Symptom: The motor runs fine for 30 seconds, then stops. The driver IC is too hot to touch. After a minute, it starts again.
Cause: You are exceeding the continuous current rating of the driver, triggering its internal thermal protection. This is incredibly common with the BJT-based L298N. Because BJTs have a high voltage drop (often 2V to 3V at 2A), that lost voltage is dissipated as heat. At 2A, an L298N is burning 4 to 6 watts of heat inside a tiny silicon package with no heatsink.
Fix: Abandon BJT drivers for anything above 1A. Switch to a MOSFET-based driver like the TB6612FNG or DRV8871, where the Rds(on) is measured in milliohms, resulting in negligible heat generation at moderate loads.
3. Arduino Brownout and Reset Loops
Symptom: The moment the motor starts, the Arduino's onboard LED flickers and the board resets.
Cause: Motor startup inrush current is dragging the shared power rail voltage down below the Arduino's brownout detection threshold (usually around 4.0V for the ATmega328P). Alternatively, inductive back-EMF spikes are coupling into the logic lines.
Fix: Never power a motor drawing more than 500mA directly from the Arduino's 5V pin. Use a separate power supply for the motor driver's high-current terminals (B+). Crucially, ensure the motor power supply GND and the Arduino GND are tied together at exactly one point (star grounding) to prevent ground loops from injecting noise into the microcontroller's reset pin.






