Control 28BYJ-48 Stepper Motor with ULN2003 Driver & Arduino
We’re surrounded by stepper motors more than we realize. They’re quietly working behind the scenes in all kinds of everyday devices—window blinds, 3D printers, security cameras, CNC machines, and more.
Stepper motors sit somewhere between regular DC motors and servo motors. Like DC motors, they can spin continuously, but like servos, they can also move in precise, controlled steps—making them perfect for tasks that require accurate positioning.
If you’re just getting started with stepper motors, the 28BYJ-48 is a fantastic choice. It’s affordable, widely available, and easy to use with Arduino. In this tutorial, we’ll show you how to hook it up, and we’ll explore two ways to control it: one using Arduino’s built-in Stepper library, and another using the more advanced AccelStepper library.
Let’s get started and bring some motion to your next project!
Do you know how stepper motors work?
A stepper motor is a type of brushless DC motor that moves in precise, small steps instead of spinning continuously. The working principle of a stepper motor is based on magnetic fields. It has two main components: a rotor (the moving part) with magnetic teeth, and a stator (the stationary part) with electromagnetic coils.
When electricity flows through these coils in a specific order, each coil creates a magnetic field. This magnetic field attracts the nearest rotor teeth, causing the motor to advance by one precise, fixed angular increment, known as a step.
The way the coils are pulsed determines exactly how the motor moves:
- The sequence of pulses controls which direction the motor turns
- The frequency of the pulses determines how fast the motor spins
- The total number of pulses we send determines exactly how far the motor turns
By energizing the coils, step by step, one after another in a particular order, we can make the motor turn exactly how much we want, how fast we want, and in the direction we want. This precise control makes stepper motors perfect for things like 3D printers, CNC machines, or robot arms where exact positioning is important.
The 28BYJ-48 Stepper Motor
The 28BYJ-48 is a small, 5-wire unipolar stepper motor that runs on 5 volts. It’s perfect for projects that need precise, controlled movement, like moving a robotic arm to exact positions.
This motor has a compact, round body that’s about 28 mm in diameter. It comes with two small flanges for attaching to other surfaces. The shaft has a “Double-D” shape, which means it’s partly flat on two sides. This shape makes it easy to attach wheels or other parts using a set screw.
Despite its small size, the motor delivers a solid amount of torque—around 34.3 mN·m, while rotating at approximately 15 RPM.
The entire motor, including its built-in gearbox, weighs only 37 grams. This lightweight design makes it ideal for small robots or portable projects.
PinoutThe 28BYJ-48 has five wires. The pinout is as follows:
Inside the motor are two electromagnetic coils, and each coil has a center connection point called a center tap. These two center taps are joined together inside the motor and come out as a single red wire.
Each coil end, along with its center tap, forms what’s called a phase. Since there are two coils and each has a center tap, the motor has four phases total.
The red wire should always be connected to positive voltage (HIGH). When you connect any of the other four wires to negative voltage (LOW), that phase becomes energized and helps move the motor.
To make the motor spin, you energize these four phases in a specific pattern. This pattern is called a step sequence.
Gear Reduction RatioAccording to the datasheet, without any gears, each step would move the shaft by 11.25°. This means it would take 32 steps to complete one full rotation (since 360° ÷ 11.25° = 32 steps).
However, this motor includes a built-in gearbox with a 64:1 reduction ratio. This means the internal rotor must rotate 64 times before the output shaft completes just one full turn. With this gearbox, it now takes 32 × 64 = 2048 steps for the output shaft to make one complete rotation.
The built-in gearbox serves an important purpose: it slows down the motor’s spinning speed while increasing its torque. This trade-off explains why the motor rotates at only 15 RPM but can generate a relatively strong torque of about 34.3 mN·m.
Not all 28BYJ-48 motors have the same gear ratio. Some versions (depending on the manufacturer) may use a 1:16 ratio, which gives 512 steps per rotation instead of 2048.
Power ConsumptionThe 28BYJ-48 stepper motor runs on 5 volts but requires a fair amount of current—about 240 milliamps (mA) per phase.
It’s also important to note that stepper motors, including the 28BYJ-48, consume power even when not actively rotating. This is because they need to maintain their position and exert torque to resist external forces. To do this, the motor keeps a small amount of electric current flowing through its coils. This current creates a magnetic field that locks the motor’s shaft into place. The amount of current required to hold the position is known as holding current. This is something you need to take into account when creating projects.
Technical SpecificationsHere are the specifications:
Operating Voltage5VDCOperating Current240mA (typical)Number of phases4Gear Reduction Ratio64:1Step Angle5.625°/64Frequency100HzIn-traction Torque>34.3mN.m(120Hz)Self-positioning Torque>34.3mN.mFriction torque600-1200 gf.cmPull in torque300 gf.cmFor more information, please refer to the datasheet below.
The ULN2003 Driver Board
Microcontrollers like Arduino are great for controlling electronics, but they have a limitation – they can only output a small amount of current, usually around 20–40 mA per GPIO pin. However, the 28BYJ-48 stepper motor requires much more power – around 240 mA per phase. That’s six times more than what Arduino can safely provide!
To solve this, the motor is typically paired with a ULN2003 driver board. The ULN2003 is a special chip that can handle higher current and voltage levels, acting like a bridge to safely drive the motor’s coils using the low-current signals from the Arduino.
The ULN2003 chip contains seven Darlington transistor pairs. Each pair can handle up to 500 mA and 50 volts, which is more than enough for this motor! The board uses four of these pairs to control the four phases of the stepper motor.
There’s another important reason to use the ULN2003: Stepper motors are inductive loads, which means they can generate high-voltage spikes, known as back EMF (electromotive force), especially when switching. These spikes can damage your microcontroller or other components. Fortunately, the ULN2003 chip has built-in flyback diodes (also called freewheeling diodes), which safely absorb these voltage spikes and help protect your circuit.
The ULN2003 driver board has four control inputs (IN1 to IN4) that receive signals from the Arduino and a power supply connection.
It also includes a JST connector that matches the 5-wire connector of the 28BYJ-48 motor. This lets you easily plug the motor into the board without the need for extra wiring.
In addition, the board features four LEDs that indicate coil activity, providing clear visual feedback as the motor steps.
There is also an ON/OFF jumper on the board, which lets you disable the motor without having to disconnect any wires.
ULN2003 Stepper Driver Board PinoutThe ULN2003 stepper driver board has the following pinout:
IN1 – IN4 are the control inputs used to send the step sequence to the motor. You connect them to the Arduino’s digital output pins.
GND is the ground pin.
VCC supplies power to the motor. Keep in mind that the motor draws a lot of current, so it’s best to use a separate 5V power source rather than drawing power from the Arduino itself.
Motor Connector is where the motor plugs into the board. This connector is “keyed”, meaning it can only be inserted one way, which helps prevent wiring mistakes.
Wiring 28BYJ-48 Stepper Motor and ULN2003 Driver to an Arduino
Now, let’s connect the motor to the Arduino. The wiring is simple and straightforward.
First, connect the driver board’s IN1, IN2, IN3, and IN4 pins to the Arduino’s digital pins 8, 9, 10, and 11. Then, plug the 28BYJ-48 motor into the motor connector on the ULN2003 driver board.
After that, connect the power supply. Remember that the motor uses quite a bit of power and can create those voltage spikes we mentioned. To keep your Arduino safe, connect a separate 5V power supply to the ULN2003 driver board.
Finally, make sure the circuit and Arduino share a common ground.
Here’s a quick reference table for the pin connections:
ULN2003 Driver ArduinoIN1 8IN2 9IN3 10IN4 11GND GNDThis diagram shows you exactly how to connect everything:
Arduino Example Code 1 – Using the Built-in Stepper Library
In this first example, we’re going to use the Stepper Library that comes with the Arduino IDE. This library takes care of all the complicated stepping sequences for you, making it much easier to control both unipolar and bipolar stepper motors.
Here is a simple sketch that will make the motor rotate slowly in one direction, then quickly in the opposite direction.
//Includes the Arduino Stepper Library #include // Defines the number of steps per rotation const int stepsPerRevolution = 2048; // Creates an instance of stepper class // Pins entered in sequence IN1-IN3-IN2-IN4 for proper step sequence Stepper myStepper = Stepper(stepsPerRevolution, 8, 10, 9, 11); void setup() < // Nothing to do (Stepper Library sets pins as outputs) > void loop() < // Rotate CW slowly at 5 RPM myStepper.setSpeed(5); myStepper.step(stepsPerRevolution); delay(1000); // Rotate CCW quickly at 10 RPM myStepper.setSpeed(10); myStepper.step(-stepsPerRevolution); delay(1000); > Code ExplanationLet’s break down how this program works.
The sketch begins by including the Stepper library. This library gives us access to functions that help control stepper motors.
#includeNext, we tell the Arduino how many steps our motor needs to make one complete rotation, which in this case is 2048 steps.
const int stepsPerRevolution = 2048;After that, we create an object from the Stepper class. We give this object two pieces of information: the number of steps per revolution and which Arduino pins connect to the motor driver board.
You might notice something odd about the pin order—IN1, IN3, IN2, IN4—instead of what you might expect (IN1, IN2, IN3, IN4). That’s because the 28BYJ-48 motor follows a special step sequence: IN1-IN3-IN2-IN4. If the pins aren’t specified in the correct order, the motor may move incorrectly or not move at all.
Stepper myStepper = Stepper(stepsPerRevolution, 8, 10, 9, 11);The setup() function is empty in our program. Usually, this function is where we prepare pins for use, but the Stepper Library automatically sets up the pins for us, so we don’t need to add anything here.
void setup()In the loop() function, we first set the motor’s speed using setSpeed() . Then, we use the step() function to tell the motor how many steps to move. If the number passed to step() is positive, the motor spins clockwise. If it’s negative, the motor spins counter-clockwise.
So in our program, the motor first rotates slowly clockwise at 5 RPM for 2048 steps (one full rotation). Then it waits for one second. After that, it spins faster in the opposite direction at 10 RPM for another 2048 steps. This whole process repeats endlessly.
void loop() < // Rotate CW slowly at 5 RPM myStepper.setSpeed(5); myStepper.step(stepsPerRevolution); delay(1000); // Rotate CCW quickly at 10 RPM myStepper.setSpeed(10); myStepper.step(-stepsPerRevolution); delay(1000); >One important thing to understand is that the step() function is blocking. That means when you tell the motor to take steps, the Arduino stops and waits until all those steps are finished before moving on to the next instruction. For example, if you set the speed to 1 RPM and tell the motor to make one full rotation (2048 steps), the Arduino will pause for a whole minute before moving on to the next line of code.
Arduino Example Code 2 – Using the AccelStepper library
The built-in Arduino Stepper library works fine for simple projects with a single motor. However, if you need to control multiple stepper motors simultaneously, or if you want smoother movement with acceleration and deceleration, you’ll need something more advanced.
That’s where the AccelStepper library comes in! This powerful library is specifically designed for more sophisticated stepper motor control. Unlike the basic Stepper library, AccelStepper can:
- Control multiple steppers at once, with independent concurrent stepping on each stepper.
- Support acceleration and deceleration.
- Support half-stepping for smoother and more precise movements
Since this library doesn’t come pre-installed with Arduino IDE, you’ll need to install it before using it.
Library InstallationTo install the library,
- First open your Arduino IDE program. Then click on the Library Manager icon on the left sidebar.
- Type “accelstepper” in the search box to filter your results.
- Look for the AccelStepper library by Mike McCauley.
- Click the Install button to add it to your Arduino IDE.
Here is a simple sketch that makes the motor accelerate in one direction and then decelerate until it stops completely. After completing one full rotation, the motor will reverse direction and repeat the process.
// Include the AccelStepper Library #include // Define step constant #define MotorInterfaceType 4 // Creates an instance // Pins entered in sequence IN1-IN3-IN2-IN4 for proper step sequence AccelStepper myStepper(MotorInterfaceType, 8, 10, 9, 11); void setup() < // set the maximum speed, acceleration factor, // initial speed and the target position myStepper.setMaxSpeed(1000.0); myStepper.setAcceleration(50.0); myStepper.setSpeed(200); myStepper.moveTo(2048); > void loop() < // Change direction once the motor reaches target position if (myStepper.distanceToGo() == 0) myStepper.moveTo(-myStepper.currentPosition()); // Move the motor one step myStepper.run(); > Code Explanation:The sketch begins by including the newly installed AccelStepper library.
#includeWe then define the motor interface type. Since we are using a 4-wire stepper motor in full-step mode, we set this constant to 4. If you wanted smoother motion, you could use half-step mode by changing this value to 8.
#define MotorInterfaceType 4Next, we create an object from the AccelStepper class. When setting this up, we provide two pieces of information: the motor interface type we just defined and the Arduino pins connected to the motor driver board.
Just like in our first example, we use the special pin sequence IN1-IN3-IN2-IN4 (connected to Arduino pins 8, 10, 9, and 11). Remember, this unusual order is necessary because the 28BYJ-48 motor requires this specific sequence to work properly. If the pins are arranged incorrectly, your motor might move incorrectly or not move at all.
AccelStepper myStepper(MotorInterfaceType, 8, 10, 9, 11);In the setup() function, we configure several important settings:
- setMaxSpeed() sets the fastest speed the motor is allowed to reach
- setAcceleration() controls how quickly the motor speeds up or slows down—this helps create smooth motion instead of sudden jerks
- setSpeed() sets the initial speed
- moveTo() tells the motor its first target position, which in this case is 2048 steps (one full revolution for the 28BYJ-48)
Now, in the loop() function, we check whether the motor has reached its target position using distanceToGo() . When this value becomes zero, it means the motor has arrived at its destination. At that point, we reverse the motor’s direction by setting a new target that’s the opposite of its current position. This makes the motor go back and forth endlessly, switching directions after each full turn.
At the end of the loop, we call the run() function. This is extremely important! Unlike the basic Stepper library where the motor moves immediately when you call step() , the AccelStepper library works differently. You must call run() repeatedly to make the motor actually move. If you forget this step, your motor won’t do anything at all, even if you’ve set a speed and target.
void loop() < // Change direction once the motor reaches target position if (myStepper.distanceToGo() == 0) myStepper.moveTo(-myStepper.currentPosition()); // Move the motor one step myStepper.run(); >Arduino Example Code 3 – Controlling Two 28BYJ-48 Stepper Motors Simultaneously
In this third example, we’re going to control two 28BYJ-48 stepper motors simultaneously. This is something that’s difficult to do with the basic Stepper library, but becomes much easier when using the more powerful AccelStepper library.
WiringTo get started, we need to add a second 28BYJ-48 stepper motor to our setup. Each motor requires its own ULN2003 driver board. For the second driver, connect VCC to the 5V pin and GND to the ground pin on a separate power supply. Then connect the four control input pins IN1 to IN4 to Arduino pins 4, 5, 6, and 7. Now you’ll have both motors wired up and ready to go.
The following table lists the pin connections:
ULN2003 Driver 1 ArduinoIN1 8IN2 9IN3 10IN4 11GND GND ULN2003 Driver 2 ArduinoIN1 4IN2 5IN3 6IN4 7GND GNDThe image below shows how to build the circuit.
Arduino CodeHere’s a sketch that runs one motor in half-step mode and the other in full-step mode. Both motors will first accelerate, then decelerate. After completing a full rotation, each motor will reverse direction and repeat the process.
// Include the AccelStepper Library #include // Define step constants #define FULLSTEP 4 #define HALFSTEP 8 // Creates two instances // Pins entered in sequence IN1-IN3-IN2-IN4 for proper step sequence AccelStepper stepper1(HALFSTEP, 8, 10, 9, 11); AccelStepper stepper2(FULLSTEP, 4, 6, 5, 7); void setup() < // set the maximum speed, acceleration factor, // initial speed and the target position for motor 1 stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(50.0); stepper1.setSpeed(200); stepper1.moveTo(2048); // set the same for motor 2 stepper2.setMaxSpeed(1000.0); stepper2.setAcceleration(50.0); stepper2.setSpeed(200); stepper2.moveTo(-2048); > void loop() < // Change direction once the motor reaches target position if (stepper1.distanceToGo() == 0) stepper1.moveTo(-stepper1.currentPosition()); if (stepper2.distanceToGo() == 0) stepper2.moveTo(-stepper2.currentPosition()); // Move the motor one step stepper1.run(); stepper2.run(); > Code Explanation:First, we include the AccelStepper library, just like we did in our previous example.
#includeSince we want to run our motors in different stepping modes, we define two constants: one for half-step mode and one for full-step mode. Half-step mode provides smoother movement but requires twice as many steps for a complete rotation.
#define FULLSTEP 4 #define HALFSTEP 8Next, we create two separate AccelStepper objects, one for each motor. When setting up the first motor, we specify half-step mode and connect it to Arduino pins 8, 10, 9, and 11, using the correct sequence for the 28BYJ-48 motor (IN1-IN3-IN2-IN4). For the second motor, we use full-step mode and connect it to pins 4, 6, 5, and 7, also in the same proper sequence.
AccelStepper stepper1(HALFSTEP, 8, 10, 9, 11); AccelStepper stepper2(FULLSTEP, 4, 6, 5, 7);In the setup() function, we configure several important settings for both motors:
- We set the maximum speed to 1000 steps per second for both motors
- We define acceleration values to control how quickly each motor speeds up or slows down
- We set an initial speed for each motor using setSpeed()
- We give each motor its own target position using moveTo()
Since the 28BYJ-48 motor takes 2048 steps to complete one full rotation, we use this number as our target value. To make the second motor spin in the opposite direction from the first one, we simply use -2048 as its target.
void setup() < // settings for motor 1 stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(50.0); stepper1.setSpeed(200); stepper1.moveTo(2048); // settings for motor 2 stepper2.setMaxSpeed(1000.0); stepper2.setAcceleration(50.0); stepper2.setSpeed(200); stepper2.moveTo(-2048); >In the loop() function, we use two separate if statements—one for each motor—to check whether each motor has reached its destination. We do this by seeing if distanceToGo() equals zero, which means the motor has arrived at its target position. When this happens, we reverse the motor’s direction by setting a new target that’s the opposite of its current position. This way, the motor will keep moving back and forth, spinning one full turn in each direction over and over.
At the end of the loop, we call the run() function for both motors. This step is absolutely essential – without it, neither motor will move at all, even if you’ve set speeds and targets. You must keep calling run() inside the loop for the motors to actually turn.
void loop() < // Change direction once the motor reaches target position if (stepper1.distanceToGo() == 0) stepper1.moveTo(-stepper1.currentPosition()); if (stepper2.distanceToGo() == 0) stepper2.moveTo(-stepper2.currentPosition()); // Move the motor one step stepper1.run(); stepper2.run(); > Amrit PrabhuAmrit is an Electronics Engineer who loves making complex programming and hardware concepts accessible. He has more than 15 years of experience, having worked as a Senior Programmer Analyst at Mindtree Ltd. and Symantec on major projects like Windows 8, Wolters Kluwer CCH and NSE. Since 2018, he has authored hundreds of tutorials and guides for Last Minute Engineers, helping readers master everything from basic circuits to IoT. You can find him on LinkedIn
You might also like
Precise. Tested. Reliable. Because when you're working at the last minute, there's no room for error.
Copyright © 2018 - 2026 Last Minute Engineers. All rights reserved.