Motors are where robots come alive, and also where most first projects go wrong.
Plug a small motor straight into a microcontroller pin and one of two things happens. Either it twitches and does nothing, or it works for a moment and quietly kills the pin. Get it right, and the same pin can run a motor at any speed, in either direction, and stop it on a coin.
This article covers what sits between the pin and the motor, and why. It builds on Electricity, explained with water and PWM: faking analog with fast switching.
Why a pin can't drive a motor
A microcontroller pin is a signal, not a power supply. An ESP32-S3 pin can safely supply around 20 mA. A small hobby gearmotor draws a couple of hundred milliamps running freely, and far more when it's working hard.
That's ten times too much, before the motor has even been given a job to do.
The answer is the transistor from Electricity, explained with water: a valve in a big pipe, worked by a much smaller control. The pin works the valve. The motor's current comes from a supply that can actually deliver it.
One direction: a transistor and a diode
The simplest motor driver is one MOSFET and one diode:
- The motor connects between the motor supply's positive terminal and the MOSFET.
- The MOSFET connects the motor's other wire to ground when it's switched on.
- The pin drives the MOSFET's gate, through a small resistor of around 100 Ω. A 10 kΩ resistor from gate to ground keeps it off while the board is starting up.
- A flyback diode sits across the motor, pointing back toward the positive supply.
That diode is not optional. A motor is a coil of wire, which makes it an inductor: the heavy water wheel from Electricity, explained with water. When the transistor switches off, the motor's current refuses to stop, and without somewhere to go it produces a spike of tens or hundreds of volts. That's water hammer, and it destroys transistors. The diode gives the current a loop to circulate in until it dies away.
Pick a logic-level MOSFET: one that's fully on with only 3.3 V on its gate. Many MOSFETs are specified at 10 V, and at 3.3 V they're only part open, which means they sit in the pipe as a narrow section and get hot. Check the on-resistance at your gate voltage in the datasheet, as described in Staying inside the limits.
Speed with PWM
Once the transistor is there, speed control is just PWM on the pin. The motor's weight does the averaging, exactly as PWM: faking analog with fast switching describes:
const int MOTOR_PIN = 5;
void setup() {
ledcAttach(MOTOR_PIN, 20000, 8); // 20 kHz, so the motor doesn't whine; steps 0 to 255
}
void loop() {
ledcWrite(MOTOR_PIN, 180); // about 70% speed
}Two things to expect. Running at 20 kHz keeps the whine above human hearing. And at low duty cycles, most motors won't start at all: they need a certain push to overcome friction before they turn. A common trick is to start at a higher duty cycle for a fraction of a second, then drop to the speed you want.
The current nobody plans for
A motor's current isn't fixed. It depends on how hard the motor is working.
Spinning freely, a motor draws very little. As you load it, it slows down and pulls more. Hold the shaft completely still and it stalls: it draws the most current it ever will, and does no useful work at all. Every watt goes into heat.
Here's the part that catches almost everyone: every start is a brief stall. At the moment you switch a motor on, it isn't turning yet, so for a split second it draws its full stall current. For a small gearmotor that's often five to ten times its running current.
A small 6 V gearmotor, running against a load you control. The harder it has to work, the slower it turns and the more current it pulls. Push it all the way to a stall, then see what happens in the first moment after it starts.
Motor
0% spins freely. 100% holds the shaft still: a stall.
What's supplying the current
Fine for now: 640 mA out of 1 A. But push the load up, or press "Start the motor", and see what it needs for a moment.
The first 400 ms after switching on
Press the button to switch the motor on.
- Speed
- 160 rpm
- at the gearbox output
- Current
- 640 mA
- 3× the free-running current
- Power in
- 3.84 W
- at 6 V
- Start-up peak
- 2.4 A
- every single start
That start-up surge is what resets boards. The motor pulls more than the supply can give, the voltage sags, and the microcontroller browns out, exactly as Powering your project describes. It looks like a code bug. It isn't.
So when you choose a driver and a supply:
- Find the stall current. It's usually in the motor's datasheet or product listing. If it isn't, measure it: hold the shaft briefly and read the current, or measure the winding resistance and divide the supply voltage by it.
- Size the driver for stall current, or at least for the start-up peak. Many drivers list a continuous current and a higher short peak.
- Size the supply for every motor starting at once. Two motors starting together is two stall currents together.
Changing direction: the H-bridge
To reverse a DC motor, you reverse the current through it. With one transistor you can't: the current can only flow one way. The standard answer is four switches arranged around the motor in an H shape, which is why it's called an H-bridge.
Four switches around a motor. Close them in pairs to drive it forward or backward, brake it, or let it coast. Click a switch to flip it, or use the presets. Then try closing S1 and S3 together.
H-bridge
Forward
S1 and S4 are closed. Current runs from the supply, left to right through the motor, and down to ground. The motor spins forward.
How much of each cycle the driving pair is switched on
- Mode
- Forward
- Motor
- stopped
- 0% speed
- Supply current
- 0.42 A
- Closed
- S1 + S4
Close one top switch and the opposite bottom switch, and current crosses the motor one way. Close the other pair and it crosses the other way. Two more combinations are useful:
- Coast: everything open. The motor freewheels to a stop.
- Brake: both bottom switches closed (or both top). The motor's two wires are joined, so the spinning motor drives current round that loop as a generator, and that current fights its own spin. It stops much faster than coasting.
And one combination is forbidden. Close both switches on the same side and you've built a direct path from the supply to ground through nothing but two transistors. That's called shoot-through, and it ends in a flash. Real H-bridge chips prevent it for you, and add a brief dead time when switching, so one transistor is fully off before its partner turns on.
Use a driver chip
You can build an H-bridge from four MOSFETs, but for most projects you shouldn't. A motor driver chip or module does it properly: it includes the four switches, the flyback protection, shoot-through prevention, and usually a current limit and thermal shutdown. A dual H-bridge driver module runs two motors, which is exactly what a two-wheeled robot needs.
When choosing one, check:
- Motor voltage range: it must cover your motor supply, with margin.
- Current per channel: both continuous and peak, against your motors' running and stall currents.
- Logic voltage: its inputs must read 3.3 V as HIGH. Some older modules expect 5 V logic.
- Losses: modern MOSFET-based drivers waste very little. Some classic bipolar driver boards, like the popular L298N module, drop around 2 V or more inside the chip, so the motor gets less voltage and the driver gets hot.
Many drivers use two inputs per motor. PWM on one input and LOW on the other sets speed in one direction; swap them to reverse:
const int IN1 = 5;
const int IN2 = 6;
void setup() {
ledcAttach(IN1, 20000, 8);
ledcAttach(IN2, 20000, 8);
}
// speed from -255 (full reverse) to 255 (full forward)
void drive(int speed) {
if (speed >= 0) {
ledcWrite(IN1, speed);
ledcWrite(IN2, 0);
} else {
ledcWrite(IN1, 0);
ledcWrite(IN2, -speed);
}
}
void loop() {
drive(200); // forward
delay(2000);
drive(-200); // reverse
delay(2000);
}On many drivers of this type, both inputs LOW coasts and both HIGH brakes. Others use a separate PWM and direction pin instead. The driver's datasheet has a truth table showing exactly which is which.
Power the motors separately, and share the ground
Motors should never draw their current through your microcontroller's regulator. Feed the motor driver straight from the battery or its own supply, and power the microcontroller separately.
On the Vector S3, the BAT+ pin gives you the LiPo battery directly, 3.0 to 4.2 V, for exactly this purpose, as the power docs describe. It keeps motor current away from the 600 mA, 3.3 V regulator that the chip, sensors and LED matrix share. If your motors need more voltage than one cell gives, use a separate supply for them, or a converter as described in Getting the voltage you need.
Whatever powers the motors, connect its ground to the board's ground. The driver reads your control signals relative to ground. Without a shared ground, the signals mean nothing.
Noise: why motors crash microcontrollers
A brushed DC motor makes electrical noise. Inside, small metal brushes slide over a spinning contact, sparking constantly, and the spikes travel back along the wires. Common symptoms are resets when a motor starts, sensor readings that jump, I2C devices dropping out, and a compass that points the wrong way whenever the wheels turn.
Cheap fixes that help a lot:
- A 100 nF ceramic capacitor soldered straight across the motor's terminals soaks up the fastest spikes.
- A large capacitor, a few hundred microfarads, across the driver's power input covers the start-up surge.
- Twist each motor's two wires together, and keep them away from sensor and signal wires.
- Keep the magnetometer well away from motors. Their magnets and currents shift its readings, which is why the Vector docs recommend calibrating the compass in its final position.
Servos: motors that hold a position
A hobby servo is a motor, a gearbox, a position sensor and a small control circuit, all in one box. You don't drive its current. You tell it where to point, and its own circuit does the rest.
It has three wires: power, ground and signal. The signal is a pulse sent 50 times a second, and the width of the pulse is the message.
A servo listens for one pulse every 20 ms, and the width of that pulse is the message: how far to turn. Slide the pulse width and watch the horn follow.
Signal on the servo's control wire
About 1 ms is one end, 1.5 ms the middle, 2 ms the other end. The exact range varies between servos.
- Pulse
- 1.50 ms
- Target
- 90°
- Horn now
- 90°
- it takes time to get there
- Duty cycle
- 7.5%
- tiny, and that's fine
On the ESP32, the widely used ESP32Servo library generates the pulses for you:
#include <ESP32Servo.h>
Servo arm;
void setup() {
arm.attach(7, 1000, 2000); // pin, and the pulse widths in µs for 0° and 180°
}
void loop() {
arm.write(0);
delay(1000);
arm.write(180);
delay(1000);
}The signal is easy, but the power isn't. A servo is still a motor, and when it starts moving or strains against a load it draws a surge. A small servo can pull half an amp or more; bigger ones can exceed 2 A. Power servos from a 5 to 6 V supply that can handle every servo straining at once, never from the board's 3.3 V pin, and connect the grounds.
Stepper motors, which move in precise fixed steps and are used in 3D printers, work differently again. They're a topic for another article.
A checklist for your first motor
- Driver between pin and motor: a logic-level MOSFET with a flyback diode for one direction, or a driver chip for two.
- Stall current known: the driver and supply handle it, or at least the start-up peak, for every motor starting at once.
- PWM at about 20 kHz, so the motor doesn't whine.
- Separate motor power, with the grounds connected.
- Noise handled: a 100 nF capacitor across each motor, a bulk capacitor at the driver, and twisted wires.
Get these right and the board stops resetting, the pins stay alive, and the motors do exactly what the code says.
