L293D vs Modern Drivers: Why We Still Use It (and When to Upgrade)

The Texas Instruments L293D is a legacy dual H-bridge motor driver that has been a staple in robotics education for over two decades. If you are building a basic line-following robot or a simple differential-drive rover, the L293D DIP-16 IC or the Arduino Motor Shield (which uses a surface-mount variant) is likely what you have in your parts bin. It is rugged, features built-in flyback diodes (the 'D' in L293D stands for diodes), and survives the accidental wiring mistakes that instantly fry more sensitive modern MOSFET-based drivers.

However, before we wire it up, you need to understand its primary flaw: voltage drop and heat. The L293D uses Darlington transistor pairs for its output stage. This results in a typical voltage drop of 1.4V per side, meaning you lose roughly 2.8V across the H-bridge before the voltage ever reaches your motor. If you power it with a 6V 4xAA battery pack, your 6V TT gearmotors will only see about 3.2V, resulting in sluggish movement and high stall currents. For this guide, we mandate a 2S (7.4V nominal) 18650 lithium-ion pack to overcome this drop.

When to upgrade: If your project requires high efficiency, battery conservation, or driving motors above 1A continuous, skip the L293D and use a TB6612FNG or DRV8833. Modern MOSFET drivers drop less than 0.5V total and run cool to the touch.

L293D IC Datasheet Specs & Thermal Reality Check

To debug motor issues later, you must understand the silicon limits of the IC. Below are the critical electrical and thermal characteristics from the TI L293D Datasheet. Pay close attention to the power dissipation row—this is where most hobbyist builds fail.

Parameter Symbol Min Typ Max Unit
Logic Supply Voltage Vcc1 4.5 5.0 7.0 V
Motor Supply Voltage Vcc2 4.5 12.0 36.0 V
Peak Output Current (Non-Repetitive, 100µs) I_peak - - 1.2 A
Continuous Output Current (Per Channel) I_out - - 600 mA
High-Level Output Voltage Drop (Vcc2 - V_out) V_H_drop - 1.4 1.8 V
Low-Level Output Voltage Drop V_L_drop - 1.0 1.4 V
Max Power Dissipation (DIP-16, 25°C Ambient) P_D - - 2.07 W

The Math: If you run a motor at 600mA continuous, the total voltage drop is ~2.4V. Power dissipated as heat is P = I × V = 0.6A × 2.4V = 1.44 Watts. The DIP-16 package has a thermal resistance of roughly 70°C/W. That means the chip will rise nearly 100°C above ambient. It will burn your finger. Always use a heatsink or keep continuous loads under 300mA per channel.

Hardware Build: Parts, Pinout, and Wiring Steps

This build targets the Arduino Uno R3 (ATmega328P). If you are using an Uno R4 Minima or an ESP32, the logic levels (5V vs 3.3V) and PWM pin mappings will differ; adjust the code definitions accordingly.

Exact Parts List

  • Microcontroller: Arduino Uno R3 (ATmega328P)
  • Driver IC: L293D DIP-16 (Texas Instruments SN754410NE or ST L293D)
  • Motors: 2x 3-6V TT Gearmotors (1:48 ratio, ~150mA stall current)
  • Power: 2S 18650 Battery Holder (7.4V nominal, 8.4V fully charged) with DC barrel jack adapter
  • Wiring: 22 AWG solid core jumper wires, half-size breadboard

Pin Mapping Table

The L293D has 16 pins. Pins 4, 5, 12, and 13 are ground pins and must all be connected to your common ground to dissipate heat into the breadboard's ground plane.

L293D Pin Function Connects To Notes
1 (EN1)Enable Channel 1Arduino D5 (PWM)Controls Motor A speed
2 (IN1)Input 1AArduino D4Motor A direction logic
3 (OUT1)Output 1AMotor A Terminal 1-
4, 5, 12, 13GND / Heat SinkCommon GroundTie to Arduino GND & Batt GND
6 (OUT2)Output 2AMotor A Terminal 2-
7 (IN2)Input 2AArduino D7Motor A direction logic
8 (Vcc2)Motor Power18650 Pack (+)7.4V - 8.4V DC
9 (EN2)Enable Channel 2Arduino D6 (PWM)Controls Motor B speed
10 (IN3)Input 1BArduino D8Motor B direction logic
11 (OUT3)Output 1BMotor B Terminal 1-
14 (OUT4)Output 2BMotor B Terminal 2-
15 (IN4)Input 2BArduino D9Motor B direction logic
16 (Vcc1)Logic PowerArduino 5VPowers internal logic gates

Wiring Steps

  1. Power Down: Ensure the Arduino is unplugged and the 18650 batteries are removed from the holder.
  2. Seat the IC: Straddle the L293D across the center trench of the breadboard. Pin 1 is identified by the U-shaped notch on the IC casing.
  3. Establish Common Ground: Run a jumper from the Arduino GND pin to the breadboard's negative rail. Connect the negative wire from your 18650 battery holder to this exact same rail. Connect L293D pins 4, 5, 12, and 13 to this rail.
  4. Wire Logic and Motor Power: Connect Arduino 5V to L293D Pin 16 (Vcc1). Connect the positive wire from the 18650 pack to L293D Pin 8 (Vcc2). Do not connect the battery positive to the Arduino VIN unless your battery is strictly under 12V and you accept the onboard regulator heat.
  5. Connect I/O Pins: Follow the pin mapping table above to wire the Arduino digital pins to the L293D inputs and enable pins.
  6. Attach Motors: Connect the TT gearmotor terminals to the OUT pins. Polarity doesn't matter yet; you can swap the wires later if a motor spins backward.

Complete Arduino Code with Serial Control & Error Handling

This code targets the Arduino Uno R3. It uses the hardware PWM capabilities of the ATmega328P on pins 5 and 6. We include a serial command parser with explicit error handling to catch invalid inputs and serial timeouts, preventing runaway robots.

/*
 * L293D Dual Motor Controller with Serial Debugging
 * Target Board: Arduino Uno R3 (ATmega328P)
 * Dependencies: None (Standard Arduino API)
 */

// --- Pin Definitions ---
// Motor A (Left)
const int EN1_PIN = 5;   // Hardware PWM pin on Uno R3
const int IN1_PIN = 4;
const int IN2_PIN = 7;

// Motor B (Right)
const int EN2_PIN = 6;   // Hardware PWM pin on Uno R3
const int IN3_PIN = 8;
const int IN4_PIN = 9;

// --- Configuration ---
const int MOTOR_SPEED = 200; // PWM value (0-255). 200 leaves headroom.
const unsigned long SERIAL_TIMEOUT_MS = 2000; // Halt motors if no serial data for 2s

unsigned long lastCommandTime = 0;

void setup() {
  Serial.begin(115200);
  
  // Verify PWM pin compatibility for the target board variant
  if (!digitalPinHasPWM(EN1_PIN) || !digitalPinHasPWM(EN2_PIN)) {
    Serial.println("FATAL ERR: EN1 or EN2 pin does not support PWM on this board variant.");
    Serial.println("Check pin mapping for your specific microcontroller.");
    while(1); // Halt execution safely
  }

  // Configure all motor control pins as outputs
  pinMode(EN1_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);
  pinMode(EN2_PIN, OUTPUT);
  pinMode(IN3_PIN, OUTPUT);
  pinMode(IN4_PIN, OUTPUT);

  // Initialize in stopped state
  stopMotors();
  
  Serial.println("L293D Controller Ready.");
  Serial.println("Commands: F(orward), B(ack), L(eft), R(ight), S(top).");
  lastCommandTime = millis();
}

void loop() {
  // 1. Handle Serial Input
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    lastCommandTime = millis(); // Reset timeout watchdog
    
    switch (cmd) {
      case 'F': case 'f': moveForward(); break;
      case 'B': case 'b': moveBackward(); break;
      case 'L': case 'l': turnLeft(); break;
      case 'R': case 'r': turnRight(); break;
      case 'S': case 's': stopMotors(); break;
      default:
        // Exact error string for invalid serial commands
        Serial.print("ERR: Invalid command '");
        Serial.print(cmd);
        Serial.println("'. Use F, B, L, R, S.");
        break;
    }
  }

  // 2. Watchdog Timeout Check (Safety Feature)
  if (millis() - lastCommandTime > SERIAL_TIMEOUT_MS) {
    // Check if motors are currently running before spamming the serial monitor
    if (digitalRead(IN1_PIN) != LOW || digitalRead(IN2_PIN) != LOW || 
        digitalRead(IN3_PIN) != LOW || digitalRead(IN4_PIN) != LOW) {
      stopMotors();
      Serial.println("ERR: Motor command timeout - entering safe stop.");
    }
  }
}

// --- Motor Control Functions ---

void setMotorA(int speed, bool forward) {
  digitalWrite(IN1_PIN, forward ? HIGH : LOW);
  digitalWrite(IN2_PIN, forward ? LOW : HIGH);
  analogWrite(EN1_PIN, speed);
}

void setMotorB(int speed, bool forward) {
  digitalWrite(IN3_PIN, forward ? HIGH : LOW);
  digitalWrite(IN4_PIN, forward ? LOW : HIGH);
  analogWrite(EN2_PIN, speed);
}

void moveForward() {
  setMotorA(MOTOR_SPEED, true);
  setMotorB(MOTOR_SPEED, true);
}

void moveBackward() {
  setMotorA(MOTOR_SPEED, false);
  setMotorB(MOTOR_SPEED, false);
}

void turnLeft() {
  setMotorA(MOTOR_SPEED, false); // Left motor backward
  setMotorB(MOTOR_SPEED, true);  // Right motor forward
}

void turnRight() {
  setMotorA(MOTOR_SPEED, true);  // Left motor forward
  setMotorB(MOTOR_SPEED, false); // Right motor backward
}

void stopMotors() {
  analogWrite(EN1_PIN, 0);
  analogWrite(EN2_PIN, 0);
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, LOW);
  digitalWrite(IN3_PIN, LOW);
  digitalWrite(IN4_PIN, LOW);
}

Debugging: The First 3 Things to Check When Motors Won't Spin

You uploaded the code, opened the Serial Monitor, typed 'F', and... nothing happened. The L293D is notorious for silent failures if the support circuitry isn't perfect. Before you throw the IC in the trash, check these three things in order.

1. The Common Ground Loop is Broken

The Symptom: The Arduino is on, the Serial Monitor responds, but the motors twitch weakly or not at all. The L293D gets extremely hot instantly.
The Cause: The logic ground (Arduino) and motor ground (Battery) are not tied together. The L293D needs a shared reference voltage to understand the 5V logic signals from the Arduino.
The Fix: Use your multimeter in continuity mode. Place one probe on the Arduino GND pin and the other on L293D Pin 4. It must read < 1 ohm. If it doesn't, bridge your breadboard ground rails.

2. Vcc2 Voltage Sag at the IC Pins

The Symptom: Motors spin fine when tested directly on the battery, but stall when connected to the L293D outputs.
The Cause: You are measuring 7.4V at the battery, but thin 24 AWG breadboard jumper wires and the internal resistance of the L293D are dropping the voltage. Remember the 2.8V internal drop? If Vcc2 sags to 5V under load, your motor only sees 2.2V.
The Fix: Measure DC voltage directly across L293D Pin 8 (Vcc2) and Pin 4 (GND) while the motors are trying to spin. If it reads below 6V, upgrade your battery wiring to 18 AWG or solder the motor connections directly to a perfboard instead of using breadboard contacts.

3. The Enable Pin is Floating or Mapped Wrong

The Symptom: The compiler throws an error, or the serial monitor outputs ERR: Invalid command but motors don't move.
The Cause: If you accidentally wired EN1 to a non-PWM pin (like D4 instead of D5), analogWrite() will default to a simple digital HIGH or LOW, giving you 100% speed or 0% speed with no control. Worse, if the Enable pin is left completely unconnected (floating), the internal logic gates will behave erratically.
The Fix: Verify EN1 and EN2 are on pins 5 and 6. If you get the compiler error error: 'EN1_PIN' was not declared in this scope, check that your #define statements are at the very top of the sketch, outside of any functions.

Extending and Simplifying the Build

Once you have the baseline differential drive working, you will likely want to modify the hardware footprint.

Lithium Safety Note: When using 18650 cells for Vcc2, never mix old and new cells, and never parallel mismatched cells. Always use a 2S BMS (Battery Management System) module between the cells and your L293D to prevent over-discharge below 2.5V per cell, which will permanently damage the lithium chemistry and create a fire hazard.

How to Simplify: Use an L293D Motor Shield

If breadboard wiring is causing intermittent ground faults, switch to an official Arduino Motor Shield or a third-party L293D shield (like the Adafruit Motor Shield V1). These shields route Vcc2 directly through the Arduino's VIN pin and handle the common ground internally. Note: When using a shield, you must change the code's pin definitions to match the shield's specific hardware routing (usually D11, D3, D12, and D13 for the Adafruit V1).

How to Extend: Add Encoders and Current Sensing

The raw L293D IC does not have a built-in current sense pin (unlike the L298P or modern DRV8833). If you want to implement PID speed control or stall detection, you must add external hardware:

  • Current Sensing: Solder a 0.1-ohm, 2W shunt resistor in series with the motor's ground path. Measure the voltage drop across it using an op-amp (like an LM358) to scale the millivolt signal up to the Arduino's 0-5V ADC range.
  • Encoders: Attach magnetic hall-effect encoders to the back shaft of your TT motors. Wire the encoder A/B phases to Arduino hardware interrupt pins (D2 and D3 on the Uno R3) to count ticks and calculate real-world RPM, closing the loop on your motor control.