Connecting an L293D dual H-bridge motor driver to an Arduino Uno requires separating your logic power (5V) from your motor power (up to 36V), tying their grounds together, and routing PWM signals to the enable pins. The L293D can drive up to 600mA continuously per channel (1.2A peak), but it suffers from a significant internal voltage drop of roughly 1.4V, meaning a 6V motor supply will only deliver about 4.6V to the motor. If you are building a permanent robot, you should eventually upgrade to a MOSFET-based driver like the TB6612FNG or DRV8833, but the classic bipolar L293D remains the standard for learning H-bridge logic on a breadboard.
The L293D to Arduino Decision Matrix: Which Form Factor?
Before wiring, you must choose which physical version of the L293D to use. The silicon is identical, but the packaging drastically changes your build time and debugging experience.
| Form Factor | Example Part | Approx. Cost | Pros | Cons |
|---|---|---|---|---|
| Bare DIP IC | TI L293DNE (16-pin) | $1.50 - $2.50 | Cheapest; fits standard breadboards; teaches fundamental pinouts. | Requires manual flyback diode verification; pins can bend. |
| Breakout Board | Generic L293D Module | $3.00 - $5.00 | Pre-soldered; often includes 5V regulator and screw terminals. | Wasted space if you only need one channel; cheap modules often lack bulk capacitors. |
| Motor Shield | Arduino Motor Shield R3 | $15.00 - $25.00 | Plugs directly into Uno headers; includes onboard current sensing. | Expensive; locks you into specific pinouts; bulky for small chassis. |
Parts List and Wiring Spec Sheet
This build targets the Arduino Uno R3 (ATmega328P). Do not use 5V logic from the Arduino to power the motor directly; the ATmega328P absolute maximum DC current per I/O pin is 40mA, and the total package limit is 200mA. A stalled motor will draw amps and instantly fry the microcontroller.
Required Materials
- Microcontroller: Arduino Uno R3 (or compatible clone with ATmega328P)
- Motor Driver: Texas Instruments L293DNE (16-pin DIP)
- Motor: 6V to 12V DC Gear Motor (e.g., standard TT motor, ~$2.00)
- Motor Power Supply: 4x AA Battery Pack (6V nominal) or 9V battery clip
- Protection: 1x 100µF electrolytic capacitor (across motor supply), 4x 1N5819 Schottky diodes (optional but recommended for high-inductance loads)
- Hardware: Half-size breadboard, 22 AWG solid core jumper wires
L293D 16-Pin Mapping Table (Channel 1 Focus)
The L293D contains two independent H-bridges. We will wire Channel 1 (pins 1-8) for a single motor. If using a second motor, mirror the logic on pins 9-15.
| L293D Pin | Function | Connect To | Notes |
|---|---|---|---|
| 1 (1,2EN) | Enable 1 | Arduino Pin 9 (PWM) | Must be HIGH to drive motor. PWM controls speed. |
| 2 (1A) | Input 1 | Arduino Pin 8 (Digital) | Logic HIGH = Forward, LOW = Reverse (with 2A inverted). |
| 3 (1Y) | Output 1 | Motor Terminal 1 | Carries high current. Use thicker wire if possible. |
| 4 (GND) | Ground | Common Ground Rail | Must tie to Arduino GND and Battery GND. |
| 5 (GND) | Ground | Common Ground Rail | Acts as a heatsink to the breadboard copper. |
| 6 (2Y) | Output 2 | Motor Terminal 2 | Completes the H-bridge circuit. |
| 7 (2A) | Input 2 | Arduino Pin 7 (Digital) | Logic HIGH = Reverse, LOW = Forward (with 1A inverted). |
| 8 (VCC2) | Motor Supply | Battery Positive (+) | Accepts 4.5V to 36V. Do not connect to Arduino 5V. |
| 16 (VCC1) | Logic Supply | Arduino 5V Pin | Powers the internal logic gates. Must be 4.5V to 7V. |
Step-by-Step Wiring Procedure
- Seat the IC: Straddle the L293DNE across the center trench of the breadboard. Ensure pin 1 (marked by a semi-circle notch) is in the top-left quadrant.
- Establish Common Ground: Connect the Arduino GND pin to the breadboard's negative rail. Connect the battery pack's negative wire to the same negative rail. Without a common ground, the Arduino's 5V logic signals will have no reference point against the motor supply, and the driver will not trigger.
- Wire Logic Power: Connect Arduino 5V to L293D Pin 16 (VCC1).
- Wire Motor Power: Connect the battery pack positive wire to L293D Pin 8 (VCC2). Place the 100µF capacitor directly across the battery positive and negative rails to absorb voltage sags during motor startup.
- Route Control Pins: Connect Arduino Pin 9 to L293D Pin 1 (Enable). Connect Arduino Pin 8 to L293D Pin 2 (1A). Connect Arduino Pin 7 to L293D Pin 7 (2A).
- Connect the Motor: Attach the motor wires to L293D Pin 3 (1Y) and Pin 6 (2Y). Polarity does not matter here; swapping them simply reverses your software-defined 'forward' direction.
- Tie Unused Pins: If you are only using one motor, connect L293D Pin 9 (3,4EN) to GND to disable the second H-bridge and prevent floating logic noise from causing phantom current draw.
Compilable Arduino Code with Error Handling
This code targets the Arduino Uno R3. It implements a serial-controlled state machine. Unlike modern drivers (e.g., DRV8833), the L293D lacks an open-drain nFAULT pin. Therefore, software error handling must rely on serial command validation and timing bounds to prevent infinite stall conditions.
// Target Board: Arduino Uno R3 (ATmega328P)
// Library Dependencies: None (Standard Arduino API)
#define EN1_PIN 9 // PWM pin for speed control (Must be PWM capable: 3,5,6,9,10,11 on Uno)
#define IN1_PIN 8 // Digital pin for direction logic A
#define IN2_PIN 7 // Digital pin for direction logic B
// Safety limits
const int MAX_PWM = 255;
const int MIN_PWM = 0;
const unsigned long MAX_RUN_TIME_MS = 10000; // Auto-stop after 10 seconds to prevent thermal meltdown
unsigned long motorStartTime = 0;
bool motorRunning = false;
void setup() {
Serial.begin(115200);
pinMode(EN1_PIN, OUTPUT);
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
// Initialize in brake mode (coast is achieved by setting EN1 LOW)
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
analogWrite(EN1_PIN, 0);
Serial.println(F("L293D Controller Ready."));
Serial.println(F("Commands: F[0-255] (Forward), R[0-255] (Reverse), S (Stop)"));
Serial.println(F("Example: F150 sets forward at 150 PWM."));
}
void loop() {
// 1. Thermal/Stall Safety Timeout Check
if (motorRunning && (millis() - motorStartTime > MAX_RUN_TIME_MS)) {
stopMotor();
Serial.println(F("[ERROR] Safety timeout triggered. Motor stopped to prevent L293D thermal shutdown."));
}
// 2. Serial Command Parsing with Error Handling
if (Serial.available() > 0) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd.length() < 1) {
Serial.println(F("[WARN] Empty command received."));
return;
}
char action = cmd.charAt(0);
int speedVal = 0;
if (cmd.length() > 1) {
String valStr = cmd.substring(1);
// Error handling: check if the substring is a valid integer
for (unsigned int i = 0; i < valStr.length(); i++) {
if (!isDigit(valStr.charAt(i))) {
Serial.print(F("[ERROR] Invalid non-numeric speed value: "));
Serial.println(valStr);
return;
}
}
speedVal = valStr.toInt();
}
// Constrain bounds to prevent hardware damage
if (speedVal < MIN_PWM) speedVal = MIN_PWM;
if (speedVal > MAX_PWM) speedVal = MAX_PWM;
switch (action) {
case 'F':
case 'f':
runMotor(speedVal, true);
Serial.print(F("Moving FORWARD at PWM: ")); Serial.println(speedVal);
break;
case 'R':
case 'r':
runMotor(speedVal, false);
Serial.print(F("Moving REVERSE at PWM: ")); Serial.println(speedVal);
break;
case 'S':
case 's':
stopMotor();
Serial.println(F("Motor STOPPED."));
break;
default:
Serial.print(F("[ERROR] Unknown command: ")); Serial.println(action);
break;
}
}
}
void runMotor(int speed, bool forward) {
if (forward) {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
} else {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
}
analogWrite(EN1_PIN, speed);
motorStartTime = millis();
motorRunning = true;
}
void stopMotor() {
// Active braking: short the motor terminals via the H-bridge
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, HIGH);
analogWrite(EN1_PIN, 0); // Disable the bridge
motorRunning = false;
}
Troubleshooting: First Three Things to Check When It Fails
When the motor refuses to spin, do not immediately rewrite your code. Hardware faults account for 90% of L293D failures. Follow this ranked diagnostic path.
1. Symptom: Motor hums, L293D gets burning hot, but shaft doesn't turn.
- Cause: Insufficient VCC2 voltage or missing common ground. If VCC2 is below 4.5V, the internal logic gates fail to fully saturate the output transistors, resulting in a high-resistance state that dissipates power as heat rather than torque.
- Fix: Measure VCC2 with a multimeter under load. If it drops below 4.5V, swap your battery chemistry (e.g., move from a 9V carbon-zinc to a 4x AA NiMH pack). Verify continuity between Arduino GND and Battery GND.
2. Symptom: Arduino resets or screen goes blank the moment the motor starts.
- Cause: Voltage sag on the 5V logic rail due to back-EMF or inrush current coupling through the breadboard rails. The ATmega328P brownout detector triggers at ~2.7V, causing a hard reset.
- Fix: Ensure the 100µF bulk capacitor is placed as close to the L293D VCC2/GND pins as physically possible. If the issue persists, power the Arduino via the barrel jack (7-12V) instead of USB, and ensure the motor ground wire is thick enough (minimum 22 AWG) to handle the return current without voltage drop.
3. Symptom: IDE Compile Error: 'enablePin' was not declared in this scope
- Cause: Copy-pasting code snippets from older tutorials that used different variable names or relied on the deprecated
AFMotorlibrary syntax. - Fix: Ensure you are using the exact
#definemacros provided in the code block above. The standard Arduino API requiresanalogWrite()anddigitalWrite()mapped to the correct Uno pin numbers. Verify your board selection in the IDE is set to 'Arduino Uno' and not a 3.3V variant like the Arduino Due, which would require logic level shifters.
Extending and Simplifying the Build
Once you have the basic open-loop control working, you will quickly hit the physical limits of the L293D. Here is how to scale the project based on your end goal.
Extending: Adding Closed-Loop Feedback
The L293D cannot tell you if the motor is stalled. To add stall detection without upgrading the driver, wire a 0.1Ω shunt resistor in series with the motor's ground return path. Measure the voltage drop across the resistor using an op-amp (like the LM358) fed into the Arduino's analog pins. If the voltage spikes while the PWM is high and the encoder reads zero RPM, your software can trigger a fault state and cut the Enable pin.
Simplifying: The Modern Upgrade Path
If you are tired of the L293D's 1.4V voltage drop and the lack of hardware fault reporting, simplify your next revision by switching to the Toshiba TB6612FNG or TI DRV8833. Both are surface-mount ICs, but they are widely available on breakout boards from Adafruit, SparkFun, and Pololu for under $6.00. They operate at the same logic levels, use the exact same analogWrite() and digitalWrite() code structure shown above, but they drop only ~0.2V, run cool to the touch, and include dedicated nFAULT pins that you can wire to an Arduino interrupt to instantly catch overcurrent events in software.






