To control a DC motor with an Arduino, you cannot connect the motor directly to the microcontroller's GPIO pins. Arduino pins max out at 40mA (with a recommended limit of 20mA), while even a small hobby DC motor draws 200mA to several amps. You need a motor driver IC—specifically an H-bridge or half-bridge topology—to act as a current-amplifying switch between your low-power Arduino logic and your high-power motor supply. The most reliable approach for general-purpose robotics and automation is pairing a brushed DC gearmotor with a MOSFET-based driver like the TB6612FNG or DRV8871.
Which DC Motor Fits Your Arduino Load Profile?
Before selecting a driver, you must match the motor type to your mechanical load. Treating all "DC motors" as identical is a primary cause of project failure. A high-inertia load requires a different torque curve than a high-speed, low-mass load. Below is a comparison of the four DC motor architectures you will encounter in embedded projects.
| Motor Type | Torque Curve & Characteristics | Control Needs | Typical Cost (2026) | Best Arduino Use Case |
|---|---|---|---|---|
| Brushed DC (Standard) | High starting torque, linear speed/voltage relationship. Brushes cause EMI and wear. | Simple H-Bridge (PWM for speed, 2 pins for direction). | $2 – $8 | Wheeled robots, basic conveyor belts, winches. |
| Brushed DC Gearmotor | Massive torque at low RPM due to planetary or spur reduction. High stall current. | H-Bridge with high peak-current tolerance. | $8 – $25 | Robotic arms, heavy-duty drive trains, camera sliders. |
| Coreless DC | Extremely low rotor inertia, rapid acceleration/deceleration, low cogging. | H-Bridge with high-frequency PWM (>20kHz) for smooth control. | $10 – $30 | Precision gimbals, medical dispensers, haptic feedback. |
| Brushless DC (BLDC) | High efficiency, no brush wear, complex 3-phase commutation required. | Requires a dedicated 3-phase ESC or 3 half-bridges + hall sensor feedback. | $15 – $50+ | Drones, high-speed spindles, continuous-duty cooling fans. |
Sizing the Driver: Match the MOSFET to the Stall Current
The most common mistake when learning to control a DC motor with an Arduino is sizing the motor driver based on the motor's running current. You must size the driver based on the motor's stall current. When a motor starts from a dead stop, or when it jams against a mechanical limit, it acts as a dead short. The current spikes to the stall rating, which is typically 5x to 10x the continuous running current.
The Sizing Rule of Thumb:
1. Driver Continuous Current ≥ 1.5 × Motor Continuous Running Current.
2. Driver Peak Current ≥ Motor Stall Current.
Worked Load Example:
Suppose you are building a robotic rover using two 12V RS-550 brushed DC motors. The datasheet states a continuous running current of 4A and a stall current of 22A.
- If you choose the classic L298N driver (2A continuous, 3A peak), it will instantly trigger its internal thermal shutdown or melt the BJT junctions when the rover hits a carpet edge.
- If you choose a BTS7960 module (rated for 43A peak), it will easily handle the 22A inrush current without voltage sag or overheating.
Here is a data-dense breakdown of the most common Arduino motor drivers on the market to help you select the right silicon for your load.
| Driver IC / Module | Internal Topology | Continuous Current | Peak / Stall Current | Motor Voltage (VM) | Est. Module Price |
|---|---|---|---|---|---|
| L298N | BJT (Bipolar Junction Transistor) | 2.0A per channel | 3.0A (brief) | 5V – 35V | $3.50 – $5.00 |
| TB6612FNG | MOSFET (Dual Channel) | 1.2A per channel | 3.2A (brief) | 2.5V – 13.5V | $5.00 – $7.00 |
| DRV8871 | MOSFET (Single Channel) | 3.6A | 4.5A | 6.5V – 45V | $7.00 – $9.00 |
| BTS7960 | MOSFET (High Power Half-Bridge) | 20A (with heatsink) | 43A | 5.5V – 27V | $12.00 – $18.00 |
Notice the topology difference. The L298N uses older BJT technology, which drops about 2V to 3V across the IC as waste heat. If you supply 12V to an L298N, your motor only sees 9V. Modern MOSFET drivers like the Texas Instruments DRV8871 or the Toshiba TB6612FNG have an on-resistance (R_DS(on)) of roughly 0.5Ω, dropping less than 0.5V and running significantly cooler. For a comprehensive look at modern driver options, Pololu's motor driver catalog remains an excellent benchmark for embedded engineers.
Wiring the TB6612FNG and Reading Failure Signatures
For small to medium 12V robots, the TB6612FNG is the gold standard. It is vastly superior to the L298N in efficiency and size. However, its breakout boards have specific terminal requirements that differ from older drivers.
Terminal Identification and Wiring
- VM (Motor Voltage): Connect to your high-current battery pack (e.g., 3S LiPo or 8x AA). Do not connect this to the Arduino 5V pin.
- VCC (Logic Voltage): Connect to the Arduino 5V or 3.3V pin. This powers the internal logic gates of the IC.
- GND: Must be shared between the Arduino GND, the battery GND, and the driver GND. Without a common ground, the logic signals will float and the motor will jitter.
- STBY (Standby): Must be pulled HIGH (connected to VCC/5V) to enable the driver. If left floating, the driver stays in low-power sleep mode.
- AIN1 / AIN2: Digital logic pins for direction. (HIGH/LOW = Forward, LOW/HIGH = Reverse, LOW/LOW = Coast, HIGH/HIGH = Brake).
- PWMA: Connect to an Arduino hardware PWM pin (e.g., Pin 5 or 6 on an Uno) for speed control.
- AO1 / AO2: The physical output terminals that connect directly to the motor wires.
Diagnosing Failure Signatures on the Bench
When your circuit fails, the motor and driver will give you physical feedback. Learn to read these signatures:
1. Audible Hum or Whine:
If the motor emits a high-pitched whine without turning, your PWM frequency is likely in the human hearing range (Arduino Uno defaults to 490Hz or 980Hz on most pins), causing magnetostriction in the motor laminations. Alternatively, the motor is stalling because the starting torque is insufficient for the load. Fix: Increase the PWM frequency via timer registers, or reduce the mechanical load.
2. Driver Overheat:
If the driver IC is too hot to touch (>60°C), you are exceeding its continuous current rating, or you are using a BJT driver (L298N) without a heatsink. Fix: Measure the running current with a multimeter in series. If it exceeds the driver's continuous rating, upgrade to a higher-amperage MOSFET driver.
3. Arduino Brownout / Reset Loop:
If the Arduino resets every time the motor starts, you are experiencing voltage sag. The motor's inrush current is pulling the shared power rail below the ATmega328P's brownout detection threshold (usually ~4.3V). Fix: Use separate power supplies for logic and motors, or add a large bulk electrolytic capacitor (470μF to 1000μF) across the motor's VM and GND terminals to supply the instantaneous inrush current.
Complete Arduino Code for PWM Speed and Direction Control
Below is a complete, copy-pasteable sketch for the Arduino Uno using the TB6612FNG. It ramps the motor speed up, stops, reverses, and applies an active brake. Active braking (shorting the motor terminals internally via the MOSFETs) stops the motor much faster than simply cutting power (coasting).
// TB6612FNG Motor Control Sketch for Arduino Uno
// Target: Single Brushed DC Motor on Channel A
// Pin Definitions
const int PWMA = 5; // PWM pin for speed (must be hardware PWM)
const int AIN1 = 4; // Direction pin 1
const int AIN2 = 7; // Direction pin 2
const int STBY = 8; // Standby pin (must be HIGH to operate)
void setup() {
// Configure all motor control pins as outputs
pinMode(PWMA, OUTPUT);
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(STBY, OUTPUT);
// Take the driver out of standby mode
digitalWrite(STBY, HIGH);
// Ensure motor is stopped on boot
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, LOW);
analogWrite(PWMA, 0);
}
void loop() {
// 1. Ramp up speed in the forward direction
setMotorDirection(true); // true = forward
for (int speed = 0; speed <= 255; speed += 5) {
analogWrite(PWMA, speed);
delay(50); // 50ms delay for smooth ramping
}
delay(2000); // Run at max speed for 2 seconds
// 2. Active Brake (stops motor quickly)
activeBrake();
delay(1000);
// 3. Run in reverse at 75% speed
setMotorDirection(false); // false = reverse
analogWrite(PWMA, 191); // ~75% duty cycle
delay(3000);
// 4. Coast to a stop (cuts power, motor spins down naturally)
coastStop();
delay(2000);
}
// Helper function to set direction
void setMotorDirection(bool forward) {
if (forward) {
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
} else {
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
}
}
// Helper function for active braking
void activeBrake() {
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, HIGH);
analogWrite(PWMA, 0); // PWM doesn't matter during active brake, but good practice to zero it
}
// Helper function for coasting
void coastStop() {
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, LOW);
analogWrite(PWMA, 0);
}
coastStop() or activeBrake() delay in your code before switching direction polarity to allow the back-EMF to dissipate.
By matching the correct motor architecture to your mechanical load, sizing the driver for the worst-case stall current rather than the nominal running current, and wiring the logic and power domains correctly, you will eliminate the vast majority of hardware failures in your embedded motion projects.






