An H-bridge is a fundamental circuit that allows you to control both the speed and direction of a DC motor by selectively switching the polarity of the voltage applied to it. When building an h bridge with Arduino, the L298N dual full-bridge module is the most common starting point due to its low cost and breadboard-friendly headers. However, while the L298N is great for prototyping, its outdated bipolar transistor design causes significant voltage drops and heat generation.
This guide provides the exact pinout, a data-driven comparison of modern alternatives, and complete, compilable code with a built-in safety watchdog for the Arduino Uno R3. We will also cover the most common hardware and software failure modes so you can debug motor jitter or compiler errors without guessing.
H-Bridge Module Comparison: L298N vs TB6612FNG vs DRV8833
Before wiring your circuit, it is critical to understand the electrical limitations of your chosen driver. The L298N uses Darlington bipolar junction transistors (BJTs), which inherently drop 2V to 3V across the junction. If you supply 12V, your motor only sees ~9.5V, and the remaining 2.5V is dissipated as heat. Modern drivers use MOSFETs, which drop less than 0.5V and run significantly cooler.
| Module / IC | Max Continuous Current | Logic Voltage | Voltage Drop | Typical Price (2026) | Best Use Case |
|---|---|---|---|---|---|
| L298N (Classic Red Module) | 1.2A (per channel, without active cooling) | 5V (onboard 7805 regulator) | ~2.0V - 3.0V (BJT) | $4.00 - $6.00 | High-voltage (up to 35V) prototyping, heavy robotics |
| TB6612FNG (Pololu Carrier) | 1.2A (up to 3.2A peak) | 2.7V - 5.5V | ~0.5V (MOSFET) | $6.00 - $9.00 | Battery-powered rovers, efficiency-critical builds |
| DRV8833 (TI Breakout) | 1.5A (per channel) | 2.7V - 10.8V | ~0.4V (MOSFET) | $3.00 - $5.00 | Low-voltage (3V-10V) micro-motors, ESP32/3.3V logic |
Note: The L298N datasheet claims 2A per channel, but in practical bench tests without a heatsink and forced air, the internal thermal shutdown triggers around 1.2A to 1.5A. For continuous 2A loads, refer to the Pololu TB6612FNG carrier board or add a dedicated heatsink and cooling fan to the L298N.
Parts List and Pin Mapping for Arduino Uno R3
The code and wiring below specifically target the Arduino Uno R3 (ATmega328P microcontroller) operating at 5V logic. If you are using an ESP32 or Arduino Nano 33 IoT (3.3V logic), you must use a logic level shifter or choose the DRV8833 module listed above to avoid damaging the driver's logic pins.
Required Components
- Microcontroller: Arduino Uno R3 (or compatible clone with ATmega328P)
- Motor Driver: L298N Dual H-Bridge Module
- Motor: 12V DC brushed gearmotor (e.g., TT motor or 12V windshield wiper motor)
- Power Supply: 12V 2A+ DC switching power supply or 3S LiPo battery (11.1V nominal)
- Control Input: 10kΩ linear potentiometer (for manual speed testing)
- Wiring: 22 AWG solid core for logic, 16 AWG stranded for motor power
Pin Mapping Table
Ensure your PWM (Pulse Width Modulation) pins are correctly assigned. On the Uno R3, pins 3, 5, 6, 9, 10, and 11 support hardware PWM via analogWrite(). Pins 7 and 8 are digital-only and must be used for direction control.
| Arduino Uno R3 Pin | L298N Module Pin | Function | Wire Color (Suggested) |
|---|---|---|---|
| D9 (PWM) | ENA | Motor A Speed Control (PWM) | Blue |
| D8 (Digital) | IN1 | Motor A Direction Logic 1 | Green |
| D7 (Digital) | IN2 | Motor A Direction Logic 2 | Yellow |
| GND | GND | Common Ground (Critical) | Black |
| 5V (Optional) | 5V Output | Logic power (if jumper removed) | Red |
| N/A (Power Supply) | 12V / VCC | Motor Power Input | Red (Heavy Gauge) |
Step-by-Step Wiring and Compilable Control Code
Wiring Sequence
- Establish Common Ground: Connect the negative terminal of your 12V power supply to the GND terminal on the L298N. Connect a jumper wire from this same GND terminal to the GND pin on the Arduino Uno. Without a common ground, the logic signals have no reference voltage and the motor will not spin.
- Configure the 5V Jumper: If your motor supply is under 12V, leave the 5V jumper cap ON the L298N's ENA/5V pins to power the Arduino via the onboard 7805 regulator. If your supply is >12V, remove the jumper and power the Arduino via its own USB or barrel jack, connecting the Arduino 5V pin to the L298N 5V logic input.
- Connect Logic Pins: Wire D9 to ENA, D8 to IN1, and D7 to IN2 as per the table above.
- Connect Motor Outputs: Attach your DC motor wires to OUT1 and OUT2. Polarity doesn't matter yet; you can swap them later if the motor spins the wrong way.
- Apply Power: Connect the 12V positive lead to the VCC/12V terminal on the L298N.
Compilable Arduino Code with Safety Watchdog
This code targets the Arduino Uno R3. It reads a 10kΩ potentiometer on pin A0 to control speed, uses a toggle switch on pin D2 for direction, and includes a serial watchdog. If the serial monitor sends a 'STOP' command, or if the board resets, the motors default to a safe, stopped state.
// H-Bridge with Arduino Uno R3 Control Code
// Target Board: Arduino Uno R3 (ATmega328P)
// Author: ElectricalFlux
// --- Pin Definitions ---
#define EN_A 9 // PWM pin for Motor A speed
#define IN_1 8 // Digital pin for Motor A direction
#define IN_2 7 // Digital pin for Motor A direction
#define POT_PIN A0 // Analog pin for speed potentiometer
#define DIR_PIN 2 // Digital pin for direction toggle switch
// --- Global Variables ---
int motorSpeed = 0;
bool motorDirection = true; // true = Forward, false = Reverse
unsigned long lastSerialUpdate = 0;
const unsigned long SERIAL_TIMEOUT = 2000; // 2 second safety timeout
void setup() {
// Initialize Serial for debugging and emergency stops
Serial.begin(115200);
Serial.println("H-Bridge Controller Initialized. Send 'STOP' to halt.");
// Configure motor control pins as outputs
pinMode(EN_A, OUTPUT);
pinMode(IN_1, OUTPUT);
pinMode(IN_2, OUTPUT);
// Configure input pins
pinMode(POT_PIN, INPUT);
pinMode(DIR_PIN, INPUT_PULLUP); // Use internal pull-up for switch
// Ensure motor is stopped on boot (Safety First)
stopMotor();
}
void loop() {
// 1. Check for Serial Emergency Stop
handleSerialCommands();
// 2. Read Direction Switch (Active LOW due to INPUT_PULLUP)
motorDirection = digitalRead(DIR_PIN);
// 3. Read Potentiometer and map to PWM range (0-255)
int potValue = analogRead(POT_PIN);
motorSpeed = map(potValue, 0, 1023, 0, 255);
// 4. Apply deadzone to prevent low-voltage motor whine/jitter
if (motorSpeed < 40) {
motorSpeed = 0;
}
// 5. Drive the Motor
driveMotor(motorSpeed, motorDirection);
delay(50); // Small delay for stability
}
void driveMotor(int speed, bool forward) {
if (speed == 0) {
stopMotor();
return;
}
if (forward) {
digitalWrite(IN_1, HIGH);
digitalWrite(IN_2, LOW);
} else {
digitalWrite(IN_1, LOW);
digitalWrite(IN_2, HIGH);
}
analogWrite(EN_A, speed);
}
void stopMotor() {
digitalWrite(IN_1, LOW);
digitalWrite(IN_2, LOW);
analogWrite(EN_A, 0);
}
void handleSerialCommands() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
command.trim();
lastSerialUpdate = millis();
if (command == "STOP" || command == "stop") {
Serial.println("EMERGENCY STOP TRIGGERED VIA SERIAL");
stopMotor();
while(true) { // Halt execution until hardware reset
delay(1000);
}
}
}
}
Debugging: Exact Errors and the "First Three Checks"
When an h bridge with Arduino fails, the issue is almost always a missing common ground or a misunderstood PWM limitation. Below are the exact software and hardware failure modes you will encounter.
Software Compiler Error
If you copy code from a forum without the header definitions, the Arduino IDE will throw this exact error string during compilation:
error: 'EN_A' was not declared in this scope
error: 'IN_1' was not declared in this scope
The Fix: Ensure the #define block at the very top of the sketch matches your physical wiring. The compiler processes top-to-bottom; if these definitions are placed inside setup() or after they are first called, the compiler will fail. Keep all pin definitions as global macros at the top of the file.
Hardware Symptom: Motor Hums, Jitters, or Won't Spin
If your code compiles and uploads, but the motor just vibrates or emits a high-pitched whine without rotating, perform these first three checks in order:
- Verify the Common Ground: Use your multimeter in continuity mode. Place one probe on the Arduino GND pin and the other on the L298N GND screw terminal. It must read less than 1 ohm. If they are floating relative to each other, the 5V logic signals from the Arduino are unreadable by the L298N's optocouplers.
- Check the 5V Jumper and Logic Voltage: Measure the voltage between the L298N's GND and the 5V output pin. If it reads below 4.5V, the onboard 7805 regulator is failing or your input voltage is too low. The L298N requires at least 5V on the logic pins to register a HIGH signal from the Arduino.
- Confirm PWM Pin Capability: Check the wire connected to ENA. If you accidentally moved it to Pin 8 or Pin 7 (which are digital-only on the Uno R3),
analogWrite()will just output a static 5V HIGH or 0V LOW. The motor will snap to full speed or stop entirely, with no speed control. Move it to Pin 9.
For deeper diagnostics on PWM frequency and motor inductance, refer to the official Arduino analogWrite() documentation, which details the 490Hz default frequency that can cause audible whine in certain DC motors.
How to Extend or Simplify Your Motor Build
Once you have the basic H-bridge circuit working, you will likely need to adapt it for your specific project constraints. Here is how to scale the design up or down.
Simplifying the Build (Space & Cost Reduction)
If you only need to drive a single, low-power motor (under 600mA) and want to eliminate the bulky L298N module, strip the design down to a bare L293D DIP-16 IC. The L293D includes built-in flyback diodes (which the L298N module also has, but bare ICs usually require external 1N4007 diodes). You can wire the L293D directly to an Arduino Nano on a half-size breadboard, reducing the footprint by 70% and the cost to under $2.00.
Extending the Build (Precision and Automation)
For autonomous rovers or robotic arms, open-loop PWM control is insufficient because battery voltage sag will change your motor speed. To extend this build:
- Add Quadrature Encoders: Attach magnetic or optical encoders to the motor shaft. Use hardware interrupts on the Arduino (Pins 2 and 3) to count pulses.
- Implement PID Control: Use the Arduino PID Library to compare the encoder's actual RPM against your target RPM, dynamically adjusting the
analogWrite()PWM value to maintain constant speed regardless of terrain or battery drain. - Upgrade to I2C Drivers: If you run out of GPIO pins, swap the parallel L298N for an I2C motor shield (like the Adafruit Motor Shield V2) which uses a PCA9685 PWM controller, freeing up your Uno's digital pins for sensors.
Building a reliable h bridge with Arduino requires respecting the physical limits of the driver IC and ensuring rock-solid grounding. By choosing the right module for your voltage requirements and implementing software safety watchdogs, you prevent both hardware damage and runaway robotic projects.






