DC Motor (Transistor Drive) · Astro Tech Blog

DC Motor (Transistor Drive)

A DC motor cannot be driven directly from an Arduino digital pin — the pin can only source ≈ 40 mA at 5V, while a small DC motor draws 100–500 mA under load and generates voltage spikes during commutation. The solution is a transistor switch (BJT or MOSFET) that uses the Arduino’s low-current signal to control a separate high-current supply. This tutorial covers one-direction speed control via PWM with a flyback diode for back-EMF protection.

DC Motor Pinout and Wiring

For this interfacing you need the following components:

  • Arduino board (Uno, Nano, Mega, etc.)
  • Small DC motor (3–9V, 100–500 mA stall current)
  • NPN transistor (2N2222, TIP120, or BC547 for very small motors)
  • 1N4007 flyback diode (or 1N4148 for small motors)
  • 1 kΩ base resistor (for BJT) or 10 kΩ gate resistor (for MOSFET)
  • External power supply (battery pack or lab supply matching motor voltage)
  • Breadboard and jumper wires
  • USB cable to connect Arduino to your computer

Schematic

BJT (2N2222) — one-direction speed control

             +Motor VCC (3–9V)
                 |
            +----+----+
            |         |
           MOTOR    1N4007
            |         |  (cathode to +V)
            |         |
            +----+----+
                 |
              Collector
                 |
              2N2222  (NPN)
                 |
              Emitter
                 |
                GND

Arduino Pin 9 ---/\/\/\--- Base
                 1 kΩ

When the Arduino pin goes HIGH, the transistor saturates and the motor runs. PWM on pin 9 controls speed.

MOSFET (IRLZ44N logic-level) — more efficient for larger motors

             +Motor VCC
                 |
            +----+----+
            |         |
           MOTOR    1N4007
            |         |
            +----+----+
                 |
              Drain
                 |
             IRLZ44N  (N-channel logic-level MOSFET)
                 |
              Source
                 |
                GND

Arduino Pin 9 ---/\/\/\--- Gate
                 10 kΩ
                 |
                GND (10 kΩ gate pulldown)

The 10 kΩ gate-to-GND resistor ensures the MOSFET is off during Arduino boot.

Pin Map

ComponentArduino Pin
Motor control (PWM)9
Motor power (external)Battery +
GND (common)Arduino GND + Battery -

The Arduino and the motor must share a common GND for the transistor switch to work.

Install necessary Library

No library is required. Speed control uses analogWrite() (PWM).

Code with complete explanation

This sketch ramps the motor speed up and down using PWM and reads the current direction from a button.

#define MOTOR_PIN  9
#define BTN_PIN    2

int speed = 0;
int step  = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(MOTOR_PIN, OUTPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
  Serial.println("DC Motor PWM Control");
}

void loop()
{
  // Button reverses direction (changes ramp direction)
  if (digitalRead(BTN_PIN) == LOW)
  {
    step = -step;
    delay(200);
  }

  // Ramp speed
  speed += step;

  if (speed >= 255)
  {
    speed = 255;
    step  = -step;  // Reverse ramp
    Serial.println("Full speed — slowing down");
  }
  else if (speed <= 0)
  {
    speed = 0;
    step  = -step;  // Reverse ramp
    Serial.println("Stopped — speeding up");
  }

  analogWrite(MOTOR_PIN, speed);
  Serial.print("Speed: ");
  Serial.println(speed);
  delay(30);
}

Fixed speed test

void loop()
{
  // Run at 50 % duty cycle
  analogWrite(MOTOR_PIN, 128);
  delay(5000);

  // Run at 75 %
  analogWrite(MOTOR_PIN, 191);
  delay(5000);

  // Stop
  analogWrite(MOTOR_PIN, 0);
  delay(3000);
}

Code breakdown

  • analogWrite(pin, value) — outputs a PWM signal (0–255) on pin 9 (or pins 3, 5, 6, 10, 11 on Uno). A 0% duty cycle = motor off, 100% = full speed.
  • The transistor acts as a switch: PWM HIGH → transistor saturated → motor connected to GND → current flows.
  • The flyback diode (1N4007) is connected reverse-biased across the motor terminals. When the transistor switches off, the motor’s inductive kickback current circulates through the diode instead of arcing across the transistor.
  • The base resistor (1 kΩ for BJT) limits the base current to ≈ 4 mA when the Arduino pin is HIGH (5V), keeping the transistor in saturation without overloading the pin.

Bidirectional control (H-bridge required)

For forward/reverse control, a single transistor is insufficient — an H-bridge (L293D, L298N, or discrete transistors) is needed:

// H-bridge pins
#define MOTOR_IN1 8
#define MOTOR_IN2 9
#define MOTOR_EN  10

void setMotor(int speed)  // -255..+255
{
  if (speed > 0)
  {
    digitalWrite(MOTOR_IN1, HIGH);
    digitalWrite(MOTOR_IN2, LOW);
    analogWrite(MOTOR_EN, speed);
  }
  else if (speed < 0)
  {
    digitalWrite(MOTOR_IN1, LOW);
    digitalWrite(MOTOR_IN2, HIGH);
    analogWrite(MOTOR_EN, -speed);
  }
  else
  {
    digitalWrite(MOTOR_IN1, LOW);
    digitalWrite(MOTOR_IN2, LOW);
    analogWrite(MOTOR_EN, 0);
  }
}

Steps to perform this interfacing

  1. Build the transistor circuit on a breadboard as shown.
  2. Connect a common GND between the Arduino and the external motor power supply.
  3. Do NOT connect the motor to the Arduino’s 5V pin — use an external supply.
  4. Copy the code into the Arduino IDE.
  5. Select the correct board and port (Tools > Board and Tools > Port).
  6. Upload the sketch to the Arduino.
  7. Open the Serial Monitor (Tools > Serial Monitor, set baud rate to 9600).
  8. The motor speed ramps up from 0 to 255, then down, repeating.
  9. Press the button (pin 2 to GND) to reverse the ramp direction.
  10. Observe that the motor stops cleanly and does not cause the Arduino to reset.

Caution

  • Flyback diode is mandatory: Without the 1N4007 (or Schottky) diode across the motor terminals, the inductive voltage spike when the transistor turns off can exceed 50V — destroying the transistor instantly. The diode must be connected reverse-biased (cathode to motor +V, anode to motor GND side). If the motor runs but the transistor gets hot or fails immediately, the diode is likely missing or connected backwards.
  • Transistor current rating: A 2N2222 is rated for 600 mA collector current (800 mA peak) — adequate for small toy motors. For motors drawing > 500 mA, use a TIP120 Darlington pair (5A rated) or a logic-level MOSFET like IRLZ44N (30+A). The TIP120 has a higher saturation voltage (≈ 1V) than a MOSFET, causing more heat dissipation at high currents.
  • Base resistor calculation: For a BJT with a gain of ≈ 100, the base current should be ≈ 1/10 of the collector current (forced beta). For a 200 mA motor: I_b = 20 mA, R_b = (5V - 0.7V) / 0.02A ≈ 220 Ω. For the 2N2222 with a 1 kΩ resistor: I_b ≈ 4.3 mA, max collector current ≈ 430 mA — within limits for most small motors.
  • Common ground required: The transistor switches the motor’s current path to GND. If the Arduino and the motor power supply do not share a common GND, the base-to-emitter circuit is incomplete and the transistor will not turn on. Always connect Arduino GND to the motor power supply’s negative terminal.
  • PWM frequency: The Arduino’s default PWM frequency on pin 9 is ≈ 490 Hz (pins 5 and 6 are ≈ 980 Hz). At low PWM values, the motor may audibly whine at this frequency if the mechanical load is light. Changing TCCR1B prescaler can shift the frequency to ≈ 31 kHz (inaudible) but affects millis() timing.
  • Stall current: When the motor shaft is mechanically prevented from turning (stall), the current can increase 5–10× the running current. A 200 mA motor may draw 1.5–2A when stalled. Ensure your transistor, power supply, and wiring can handle stall current for at least a few seconds. For protection, add a PTC resettable fuse or a current-sense resistor with software shutdown.
  • Motor noise: Brushed DC motors generate electrical noise (sparking at the brushes) that can couple back into the Arduino’s 5V rail, causing resets or sensor glitches. Add a 100 µF electrolytic capacitor + 100 nF ceramic capacitor across the motor terminals (between motor +V and motor GND) to suppress this noise.