How to Control a DC Motor: Speed and Direction

Controlling a DC motor comes down to three things: adjusting its speed, changing its direction, and optionally using feedback to hold it at a precise target. The most common approach uses a technique called pulse width modulation (PWM) paired with a driver circuit, and you can get a basic setup running with an Arduino and a few dollars in components.

Speed Control With PWM

A DC motor’s speed is proportional to the voltage applied to it. Rather than varying the actual voltage (which requires more complex circuitry), most systems rapidly switch the full supply voltage on and off thousands of times per second. This is pulse width modulation. The motor’s inertia smooths out these pulses, so it behaves as if it’s receiving a steady, lower voltage.

The key variable is the duty cycle: the percentage of each cycle the signal spends in the “on” state. At 100% duty cycle, the motor gets full voltage and spins at top speed. At 50%, it effectively receives half the supply voltage. The math is straightforward: average voltage equals the duty cycle multiplied by the supply voltage. So a 12V supply at 75% duty cycle delivers an average of 9V to the motor.

PWM frequency matters more than people expect. Arduino boards default to about 490 Hz on most pins and 980 Hz on pins 5 and 6. At these low frequencies, you’ll often hear an audible buzz from the motor coils vibrating. Bumping the frequency up to the 5 kHz to 20 kHz range produces smoother, quieter operation. Above 20 kHz, the switching is ultrasonic, eliminating the noise entirely.

Direction Control With an H-Bridge

A DC motor reverses direction when the polarity of its power supply flips. An H-bridge circuit handles this using four transistors arranged in a square, with the motor connected across the middle. The transistors act as switches and are activated in diagonal pairs. Turning on the first and fourth transistor sends current one way through the motor. Turning on the second and third sends it the other way.

There’s one critical safety rule: the two transistors on the same side of the bridge must never be on simultaneously. This creates a direct short across the power supply, a condition called shoot-through, which will likely destroy the transistors instantly. Dedicated driver chips include built-in logic to prevent this, adding a brief “dead time” (around 540 nanoseconds in some chips) where both transistors on a side are off during switching transitions.

By combining PWM with an H-bridge, you get full bidirectional speed control. The PWM signal modulates one diagonal pair while the other stays off, giving you variable speed in either direction.

Choosing a Motor Driver

You don’t need to build an H-bridge from individual transistors. Pre-built driver ICs and modules handle the switching, protection, and heat management for you. The two most common beginner options are the L293D and L298N, and they suit very different loads.

  • L293D: Handles 600 mA continuous per channel (1.2A peak), with a motor voltage range of 4.5V to 36V. It runs cooler and works well for small motors in robotics and hobby projects.
  • L298N: Handles 2A continuous per channel (3A peak), with a motor voltage range of 6V to 46V. It can drive larger motors but has a significant voltage drop, sometimes as high as 4V at heavy loads. That lost voltage becomes heat, which is why L298N modules always ship with a large heatsink bolted on.

For more efficient options, look for MOSFET-based drivers like the DRV8833 or DRV8871. These have much lower voltage drops, meaning less wasted power and less heat, especially important in battery-powered projects.

Wiring a Basic Arduino Setup

A typical setup connects an Arduino’s PWM output pin to the driver’s input, with the motor connected to the driver’s output terminals. The driver gets its own power supply matched to the motor’s voltage rating. Never power a motor directly from an Arduino pin: even a small motor can draw several hundred milliamps, far exceeding the 40 mA limit per pin.

In code, controlling speed is as simple as calling analogWrite(pin, value), where the value ranges from 0 (off) to 255 (full speed). A value of 127 gives roughly 50% duty cycle. For direction, you toggle digital pins connected to the driver’s direction inputs. With an L298N, for example, setting IN1 high and IN2 low spins the motor one way, while reversing those pins reverses the motor.

Protecting Your Circuit

DC motors are electrically noisy. When a motor’s power is cut, the collapsing magnetic field in its coils generates a voltage spike that can damage your driver and microcontroller. A flyback diode placed across the motor terminals absorbs this spike by giving the current a safe path to circulate. Most driver modules include built-in flyback diodes, but if you’re building your own circuit, you’ll need to add them. For a 24V system, select a diode with a reverse voltage rating at least 10 times the nominal system voltage to reduce stress and extend the diode’s lifetime.

Capacitors are the second line of defense. Place a small ceramic capacitor (0.1 µF) directly across the motor’s power terminals to suppress high-frequency electrical noise. Then add a larger bulk capacitor (10 µF or more) near the driver’s power input to smooth out the voltage dips caused by the motor’s current draw. A practical guideline from Texas Instruments suggests 1 to 4 µF of bulk capacitance per watt of motor power. For a motor that draws 200 mA with 100 mV of acceptable voltage ripple at a 20 kHz PWM frequency, you’d want at least 100 µF, and roughly three times that in practice to account for non-ideal capacitor behavior.

Adding Feedback for Precise Speed

The methods above are open-loop: you set a duty cycle and hope the motor holds its speed. But if the load changes (say, a robot hits a hill), the motor slows down with no way to compensate. Closed-loop control solves this by measuring the actual speed and adjusting the PWM signal to match a target.

The most common speed sensor is a rotary encoder attached to the motor shaft. Encoders output a set number of electrical pulses per revolution (PPR). By counting pulses over a known time interval, your microcontroller calculates the actual RPM. Quadrature encoders have two output channels offset by 90 degrees, which lets you detect direction as well as speed. Using both rising and falling edges from both channels gives you four times the resolution of a single channel, so a 100 PPR encoder effectively becomes 400 counts per revolution.

PID Control Explained Simply

Once you have speed feedback, the standard approach is a PID controller: three terms working together to keep the motor at your desired speed.

The proportional term looks at the current error (the gap between your target speed and actual speed) and responds in proportion. A large error produces a large correction. Increasing this gain reduces steady-state error but tends to cause the motor to overshoot the target and oscillate before settling.

The integral term accumulates error over time. If the motor is consistently a little too slow, the integral term builds up and pushes the output higher until the error disappears entirely. A small integral gain eliminates error slowly. A large one eliminates it fast but can increase overshoot significantly.

The derivative term reacts to how quickly the error is changing. When the motor is approaching its target speed rapidly, the derivative term applies a braking force to prevent overshoot. In practice, increasing the derivative gain is the primary way to tame the oscillation caused by aggressive proportional and integral settings.

Tuning a PID controller means finding the right balance among these three values. A common starting method is to set the integral and derivative terms to zero, increase the proportional gain until the motor oscillates steadily, then add derivative gain to dampen the oscillation, and finally add integral gain to eliminate any remaining steady-state error.

Brushless Motors Need Different Hardware

Everything above applies to brushed DC motors, where internal brushes handle the switching of current through the coils. Brushless DC motors (BLDC) eliminate those brushes for longer life and higher efficiency, but they need external electronics to sequence current through three separate coils in the right order.

An electronic speed controller (ESC) handles this. It contains six transistors arranged in three pairs, one pair for each motor wire. The ESC’s microcontroller switches these transistors so that each coil cycles through three states: high voltage, low voltage, or off. This creates a rotating magnetic field that keeps the motor spinning.

The ESC needs to know the rotor’s position to time these switches correctly. Sensored systems use small electronic sensors built into the motor, which work well at low speeds and high-torque applications like ground vehicles. Sensorless systems detect the rotor’s position by reading the voltage generated by the spinning motor (back-EMF). This approach is simpler and more popular, but it struggles at very low speeds where back-EMF is too weak to measure reliably.

Monitoring Current for Torque Control

In a DC motor, the current draw is directly proportional to the torque the motor produces. Monitoring current lets you detect stalls, limit torque to protect mechanical components, or actively control how much force the motor applies. The simplest method is a low-value resistor (called a shunt) placed in series with the motor. The voltage across this resistor, measured by an analog input on your microcontroller, tells you the current flowing through the motor. Many modern driver ICs include a current sense output pin that does this for you, eliminating the need for external components.