To control a brushed DC motor with an ESP32 or Arduino using PWM, you need a dedicated MOSFET-based H-bridge or half-bridge driver (like the VNH5019 or BTS7960) sized for at least 150% of the motor's stall current. For most hobby and light-industrial applications, a PWM frequency between 2 kHz and 5 kHz provides the best balance of silent operation and minimal switching losses. Never wire a motor directly to a microcontroller GPIO pin; the inductive kickback and stall currents will instantly destroy the silicon.

The Core Mechanics of PWM for DC Motor Control

Pulse Width Modulation (PWM) does not actually lower the voltage supplied to the motor. Instead, it switches the full supply voltage on and off at a high frequency. The motor’s internal winding inductance acts as a low-pass filter, smoothing the square voltage wave into a relatively continuous trapezoidal current wave. The speed of the motor is proportional to the duty cycle—the percentage of time the voltage is in the "ON" state.

When implementing pwm for dc motor control, frequency selection is critical. If the frequency is too low (below 1 kHz), the current ripple becomes large enough that the motor physically vibrates at the switching frequency, producing an audible, high-pitched whine. If the frequency is too high (above 20 kHz), the MOSFETs in your driver circuit spend too much time in the linear transition region between fully ON and fully OFF, generating excessive heat due to switching losses.

Microcontroller Nuance: The standard Arduino analogWrite() function defaults to roughly 490 Hz on most pins, which guarantees audible motor whine. You must modify the hardware timer registers (e.g., TCCR1B) to push this to 4 kHz or higher. The ESP32 handles this much more elegantly via its LEDC (LED Control) peripheral, which defaults to a motor-friendly 5 kHz and allows easy frequency configuration in code.

Motor Type Comparison & Load Profiling

Before selecting a driver, you must confirm that a brushed DC motor is actually the right tool for your mechanical load. Treating different motor topologies as interchangeable is a common design flaw that leads to stalled systems and burnt drivers. Below is a breakdown of how common motor types map to specific load profiles and control requirements.

Motor Type Torque Curve Profile Control Needs Typical Cost (USD) Best Use Case
Brushed DC Maximum torque at zero RPM (stall), drops linearly as speed increases. Simple 2-wire H-bridge. Speed via PWM, direction via logic pins. $5 - $25 Wheeled robots, winches, conveyor belts, high-starting-torque loads.
Brushless DC (BLDC) High torque across a wide RPM range, highly efficient at continuous duty. 3-phase Electronic Speed Controller (ESC) with Hall sensors or sensorless back-EMF sensing. $20 - $80+ Drones, high-speed cooling fans, continuous-duty propulsion.
Stepper High holding torque at zero RPM, torque drops off sharply at high speeds. Step/Dir pulse train via a dedicated chopper driver (e.g., A4988, TMC2209). $10 - $40 3D printers, CNC routers, open-loop precise positioning.
Servo (RC) High torque within a limited angular range, internal gearbox. 50 Hz PWM pulse on a single signal wire (1ms to 2ms pulse width). $8 - $50 Robotic arms, RC steering, pan/tilt camera mounts.

Which motor fits your load? If your application requires moving a heavy load from a dead stop and you only need approximate speed control, the Brushed DC motor is your best choice. Its natural torque curve peaks exactly where you need it most: at startup. If you need precise angular positioning without an external encoder, use a Stepper. If you need high RPM and high efficiency for continuous runtime, use a BLDC.

Wiring, Terminals, and Sizing Your Driver

Once you have selected a brushed DC motor, you need an H-bridge driver to handle the heavy lifting. Modern designs use MOSFET-based drivers rather than older BJT (Bipolar Junction Transistor) designs like the infamous L298N, which wastes significant power as heat.

Terminal Identification (Standard H-Bridge)

Whether you are using a Texas Instruments DRV8871 for small loads or a VNH5019 for medium loads, the terminal architecture follows a standard pattern:

  • VM / VCC (Motor Power): Connected directly to your main power supply (e.g., 12V battery). Must include a bulk decoupling capacitor (typically 100µF - 470µF electrolytic) placed as close to the terminal as possible to absorb inductive voltage spikes.
  • GND (Power Ground): The high-current return path to the power supply. Must be wired with thick gauge wire (e.g., 14 AWG for 10A+ loads).
  • VDD / 5V (Logic Power): Powers the internal optocouplers or logic gates. Often tied to the microcontroller's 5V or 3.3V rail.
  • IN1 / IN2 (Logic Inputs): Connected to microcontroller GPIO pins. Dictate direction and braking states.
  • PWM / EN (Enable): The pin that receives the PWM signal to control speed. (On some drivers, PWM is applied directly to IN1/IN2 instead).
  • OUT1 / OUT2 (Motor Outputs): The high-current switched outputs connected directly to the two motor terminals.

Sizing Rule of Thumb & Worked Example

The golden rule for motor driver sizing is: Driver Continuous Current Rating ≥ 1.5 × Motor Stall Current. Sizing based on the motor's "rated load" current is a rookie mistake that will result in a melted driver the moment the mechanical load binds.

Worked Load Example:
You are building an autonomous rover using a 12V DC planetary gearmotor. The datasheet lists a no-load current of 0.3A, a rated load current of 1.5A, and a stall current of 6.0A.
Calculation: 6.0A (Stall) × 1.5 (Safety Margin) = 9.0A minimum continuous driver rating.
Selection: The popular L298N is rated for only 2A continuous—it will instantly overheat and trigger thermal shutdown. Instead, you select a Pololu Dual VNH5019 Motor Driver, which is rated for 12A continuous per channel. This provides the necessary headroom to handle the 6A stall current without exceeding the MOSFETs' thermal limits.

Failure Signatures: Hum, Overheat, and Stall

When a motor drive system fails, it rarely does so silently. Recognizing the physical and auditory signatures of a failing setup will save you from replacing fried components.

1. Audible Hum or Buzzing
If the motor emits a high-pitched whine, your PWM frequency is likely too low (under 1 kHz). Increase the frequency via your microcontroller's timer registers. If the motor hums but the shaft does not turn, the duty cycle is too low to overcome the static friction of the load, or the motor is mechanically bound. The driver is rapidly switching, but the current never reaches the threshold required to generate breakaway torque.

2. Driver Overheat
Touch the driver heatsink (carefully). If it is too hot to hold, check your switching frequency. Pushing a standard MOSFET driver past 20 kHz increases switching losses exponentially. Alternatively, if you are using a BJT-based driver like the L298N, expect a voltage drop of roughly 2V across the transistors. At 3A, that is 6W of pure heat dissipated directly into the silicon. Switch to a MOSFET driver like the Texas Instruments DRV series where the voltage drop is measured in millivolts.

3. Motor Stall and Thermal Runaway
When a motor stalls, back-EMF drops to zero, and the only thing limiting current is the DC resistance of the copper windings (which is very low). The current spikes to the stall value. If your driver lacks Overcurrent Protection (OCP) and the microcontroller doesn't cut the PWM, the $I^2R$ heating inside the motor windings will melt the internal insulation, shorting the turns and permanently destroying the motor. Always implement software timeouts that cut power if encoder feedback stops while PWM is still being applied.

FAQ: Advanced PWM for DC Motor Control Questions

What is the ideal PWM frequency for DC motor speed control?

The sweet spot for most brushed DC motors is between 2 kHz and 5 kHz. This range is high enough to push the switching noise out of the most sensitive human hearing ranges (eliminating the annoying whine) but low enough to keep MOSFET switching losses manageable. For very small coreless motors, you may need to push to 10 kHz to 20 kHz to ensure the winding inductance is high enough to smooth the current ripple.

Why does my DC motor whine when using Arduino analogWrite()?

The standard Arduino analogWrite() function operates at a default frequency of approximately 490 Hz (or 980 Hz on pins 5 and 6). Because these frequencies fall squarely in the middle of the human audible spectrum, the physical vibration of the motor windings translates directly into sound. To fix this, you must bypass analogWrite() and configure the hardware timers directly (e.g., setting the TCCR1B register to increase Timer1 to roughly 4 kHz), or switch to an ESP32 which handles high-frequency PWM natively via the LEDC API.

Can I use PWM to control a brushless (BLDC) motor directly?

No. A BLDC motor requires three-phase alternating current to spin, which means you must sequentially energize three separate sets of windings in a precise commutation sequence. You cannot connect a BLDC motor directly to a standard 2-wire DC H-bridge. Instead, you must use an Electronic Speed Controller (ESC). In this setup, your microcontroller outputs a standard 50 Hz RC-style PWM signal to the ESC's signal wire, and the ESC's internal microcontroller handles the complex 3-phase high-current switching.

How do I implement dynamic braking with an H-bridge?

Dynamic braking is used to stop a motor quickly by turning it into a generator and shorting its terminals. To achieve this with a standard H-bridge, you set both logic inputs (IN1 and IN2) to the same state—either both HIGH or both LOW, depending on the driver's specific truth table. This connects both motor terminals to Ground (or both to VM), creating a short circuit across the motor's back-EMF. The kinetic energy of the spinning rotor is rapidly dissipated as heat in the windings and the driver MOSFETs, bringing the shaft to a halt much faster than simply letting it coast.