If you want to control a DC motor's direction and speed with a microcontroller, you need an H-bridge. An h bridge Arduino setup allows you to safely switch high-current motor loads using the low-current GPIO pins of your board. For loads under 2A, the L298N dual H-bridge module is the standard workhorse. For higher currents (up to 43A), you will need to step up to a BTS7960 module.

This guide targets the Arduino Uno R3 (ATmega328P) and Arduino Nano v3. We will cover the exact wiring, provide a complete, compilable C++ sketch with serial error handling, and break down the physical and logical debugging steps when your motor refuses to spin.

Difficulty: Beginner-Intermediate | Time Required: 45 Minutes | Cost: ~$15 - $25 USD

Spec Sheet & Parts List: L298N vs High-Current Alternatives

The L298N uses bipolar junction transistors (BJTs), which introduces a voltage drop of about 2V across the chip. If you feed it 5V, your motor only sees ~3V. For 12V or 24V systems, this drop is negligible. If you are running 3V to 6V motors, look at MOSFET-based drivers like the DRV8833 or TB6612FNG instead.

Feature HiLetgo L298N Module Generic BTS7960 (43A) Pololu DRV8833 Breakout
Motor Voltage Range 5V - 35V DC 5.5V - 27V DC 2.7V - 10.8V DC
Continuous Current per Ch 2A (Peak 3A) 24A (with heatsink) 1.5A
Logic Voltage 5V (onboard regulator) 3.3V - 5V 2.7V - 5.5V
Switching Technology BJT (High voltage drop) MOSFET (Low drop) MOSFET (Low drop)
Approx. Price (2026) $6.00 - $9.00 $12.00 - $16.00 $8.00 - $11.00

Required Parts for this Build

  • Microcontroller: Arduino Uno R3 (ATmega328P) or compatible clone.
  • Motor Driver: L298N Dual H-Bridge Module (Red PCB variant with onboard 5V regulator).
  • Motor: 12V DC Gear Motor (e.g., JGA25-370 or similar 100RPM N20 variant).
  • Power Supply: 12V 2A DC switching power supply or a 3S LiPo battery pack (11.1V nominal).
  • Wiring: 22 AWG solid core jumper wires for logic; 18 AWG stranded wire for motor/power connections.

Pin Mapping and Wiring Steps

Proper grounding is where most h bridge Arduino builds fail. The Arduino and the H-bridge must share a common ground reference for the logic signals to be read correctly.

Arduino Uno R3 Pin L298N Module Pin Function
GND GND Common Logic Ground
Digital 5 IN1 Motor A Direction Logic 1
Digital 4 IN2 Motor A Direction Logic 2
PWM 9 ENA Motor A Speed Control (PWM)
5V (Optional) 5V Output Power Arduino from H-bridge (See note)
Critical Jumper Note: The L298N has a 5V enable jumper cap next to the power terminals. If your motor power supply is 12V or less, leave this jumper ON to power the onboard logic and optionally back-feed the Arduino. If your motor supply is greater than 12V, you MUST remove this jumper and supply 5V to the logic pin separately, or you will fry the onboard regulator.

Wiring Sequence

  1. De-energize: Ensure the motor power supply is unplugged.
  2. Power Terminals: Connect the 12V positive wire to the 12V screw terminal and the negative wire to the GND terminal. Tighten securely to prevent arcing.
  3. Common Ground: Run a 22 AWG jumper from the Arduino GND pin to the L298N GND terminal (the middle pin on the power block).
  4. Logic Pins: Connect Arduino pins 5, 4, and 9 to IN1, IN2, and ENA respectively.
  5. Motor Output: Connect the two motor leads to OUT1 and OUT2. Polarity doesn't matter yet; we can reverse it in code.
  6. Verify: Double-check that the 5V jumper cap is in the correct position for your supply voltage before applying power.

Complete H-Bridge Arduino Code

This sketch targets the Arduino Uno R3. It uses the hardware PWM on Pin 9 to control speed via the analogWrite() function (Arduino analogWrite Reference). It includes a Serial command interface with explicit error handling to prevent out-of-bounds PWM values that could cause unexpected behavior.

/*
 * H-Bridge Arduino Motor Control with Serial Error Handling
 * Target Board: Arduino Uno R3 (ATmega328P)
 * Driver: L298N
 */

// Pin Definitions
const int ENA_PIN = 9;  // PWM pin for speed (Must be PWM capable)
const int IN1_PIN = 5;  // Direction pin 1
const int IN2_PIN = 4;  // Direction pin 2

// System Constants
const int MAX_PWM = 255;
const int MIN_PWM = 0;
const int BAUD_RATE = 9600;

void setup() {
  // Configure motor control pins as outputs
  pinMode(ENA_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);

  // Initialize motor to stopped state
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, LOW);
  analogWrite(ENA_PIN, 0);

  Serial.begin(BAUD_RATE);
  Serial.println("H-Bridge Controller Ready.");
  Serial.println("Commands: 'F' (Forward), 'R' (Reverse), 'S' (Stop), 'V0-255' (Speed)");
}

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    
    if (input.length() == 0) return;

    char cmd = input.charAt(0);

    if (cmd == 'F' || cmd == 'f') {
      motorForward();
      Serial.println("ACK: Direction set to FORWARD");
    } 
    else if (cmd == 'R' || cmd == 'r') {
      motorReverse();
      Serial.println("ACK: Direction set to REVERSE");
    } 
    else if (cmd == 'S' || cmd == 's') {
      motorStop();
      Serial.println("ACK: Motor STOPPED");
    } 
    else if (cmd == 'V' || cmd == 'v') {
      // Parse speed value with error handling
      String valStr = input.substring(1);
      int speedVal = valStr.toInt();
      
      // Check for parsing failure (non-numeric input returns 0, but 'V0' is valid)
      if (speedVal == 0 && valStr != "0") {
        Serial.println("ERR: SERIAL_PARSE_FAIL");
        return;
      }

      // Bounds checking for PWM limits
      if (speedVal < MIN_PWM || speedVal > MAX_PWM) {
        Serial.println("ERR: PWM_OUT_OF_BOUNDS");
        return;
      }

      setMotorSpeed(speedVal);
      Serial.print("ACK: Speed set to ");
      Serial.println(speedVal);
    } 
    else {
      Serial.println("ERR: UNKNOWN_COMMAND");
    }
  }
}

// --- Motor Control Functions ---

void motorForward() {
  digitalWrite(IN1_PIN, HIGH);
  digitalWrite(IN2_PIN, LOW);
}

void motorReverse() {
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, HIGH);
}

void motorStop() {
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, LOW);
  analogWrite(ENA_PIN, 0);
}

void setMotorSpeed(int pwmValue) {
  analogWrite(ENA_PIN, pwmValue);
}

Debugging: First Three Things to Check When It Fails

When an h bridge Arduino circuit fails, the symptoms usually manifest as either a physical anomaly (motor whines but doesn't spin) or a serial error string. If you send a speed command and receive ERR: PWM_OUT_OF_BOUNDS, the code's error handling caught an invalid integer. But if the code compiles, the serial monitor says ACK, and the motor still does nothing, follow these first three physical checks:

1. Verify the Common Ground

The most common mistake is forgetting to connect the Arduino GND to the L298N GND. Without this shared reference, the 5V logic signal from Arduino Pin 5 looks like floating noise to the H-bridge optocouplers or logic gates. Fix: Measure the resistance between the Arduino GND pin and the L298N GND terminal with a multimeter. It should read < 1 ohm.

2. Check the 5V Enable Jumper and Logic Power

If you are powering the L298N with a 12V battery but removed the 5V jumper cap without supplying external 5V to the logic header, the internal logic IC is completely unpowered. The motor might receive full voltage but the direction pins will be ignored. Fix: Measure the voltage at the 5V output terminal on the L298N. If it reads 0V, replace the jumper cap or wire an external 5V source.

3. Measure Voltage Sag Under Load

If the motor jitters or the Arduino randomly resets when the motor starts, your power supply is sagging. DC motors draw 5x to 10x their continuous current during startup (stall current). A weak 12V wall wart might drop to 4V under load, causing the Arduino's brownout detector to trigger a reset. Fix: Put your multimeter on the power terminals and command the motor to start. If voltage drops below 9V, upgrade your power supply or add a large electrolytic capacitor (e.g., 2200µF 25V) across the power rails to buffer the inrush current.

Extending and Simplifying the Build

Once you have basic direction and speed control working, you will likely want to adapt the circuit for your specific application.

How to Extend the Build

  • Add Encoders for PID Control: If your motor has a quadrature encoder, wire the A/B channels to Arduino interrupt pins (D2 and D3). Use the Encoder library to track ticks and implement a PID loop to maintain exact RPM regardless of load changes.
  • Implement Current Sensing: The L298N doesn't have built-in current feedback. To detect motor stalls, place a 0.1-ohm shunt resistor in series with the motor ground and read the voltage drop across it using an Arduino analog pin and an op-amp.

How to Simplify the Build

  • Single Direction (No H-Bridge Needed): If you only need the motor to spin one way and stop (like a conveyor belt or fan), ditch the H-bridge. Use a single logic-level N-channel MOSFET (like an IRLZ44N) driven directly by an Arduino PWM pin. It's cheaper, generates less heat, and requires half the wiring.
  • Use a Motor Shield: If breadboarding jumper wires is causing intermittent connections, switch to an Adafruit Motor Shield V2. It stacks directly onto the Arduino headers and uses I2C to control the onboard MOSFETs, freeing up your PWM pins.

Frequently Asked Questions

Why does my Arduino reset when the H-bridge motor starts?

This is caused by voltage sag or Back-EMF. When a motor starts, it draws stall current, which can pull the shared power rail voltage down low enough to trigger the Arduino's brownout reset. Additionally, when the motor stops, it acts as a generator, sending voltage spikes back into the circuit. Always use separate power supplies for the motor and the Arduino (tied at ground), and solder 0.1µF ceramic capacitors directly across the motor terminals to suppress EMI noise.

Can I power the Arduino directly from the L298N 5V pin?

Yes, but with strict caveats. The L298N module has an onboard 7805 linear regulator. If your motor supply is between 7V and 12V, and the 5V jumper cap is installed, you can run a wire from the L298N's 5V terminal to the Arduino's 5V or Vin pin. However, the 7805 will dissipate excess voltage as heat. If your motor supply is 24V, the regulator will overheat and fail. For supplies over 12V, remove the jumper and power the Arduino independently.

What is the difference between an H-bridge and a motor shield?

An H-bridge is the underlying electronic circuit (the actual silicon chips and MOSFETs) that switches current polarity. A motor shield is simply a printed circuit board that contains an H-bridge (or multiple), along with level shifters, flyback diodes, and header pins designed to plug directly into the Arduino's footprint. Functionally, they do the exact same thing; a shield just saves you from wiring it manually on a breadboard.