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.
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 1 | Arduino D5 (PWM) | Controls Motor A speed |
| 2 (IN1) | Input 1A | Arduino D4 | Motor A direction logic |
| 3 (OUT1) | Output 1A | Motor A Terminal 1 | - |
| 4, 5, 12, 13 | GND / Heat Sink | Common Ground | Tie to Arduino GND & Batt GND |
| 6 (OUT2) | Output 2A | Motor A Terminal 2 | - |
| 7 (IN2) | Input 2A | Arduino D7 | Motor A direction logic |
| 8 (Vcc2) | Motor Power | 18650 Pack (+) | 7.4V - 8.4V DC |
| 9 (EN2) | Enable Channel 2 | Arduino D6 (PWM) | Controls Motor B speed |
| 10 (IN3) | Input 1B | Arduino D8 | Motor B direction logic |
| 11 (OUT3) | Output 1B | Motor B Terminal 1 | - |
| 14 (OUT4) | Output 2B | Motor B Terminal 2 | - |
| 15 (IN4) | Input 2B | Arduino D9 | Motor B direction logic |
| 16 (Vcc1) | Logic Power | Arduino 5V | Powers internal logic gates |
Wiring Steps
- Power Down: Ensure the Arduino is unplugged and the 18650 batteries are removed from the holder.
- 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.
- 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.
- 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.
- Connect I/O Pins: Follow the pin mapping table above to wire the Arduino digital pins to the L293D inputs and enable pins.
- 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.
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.






