Table of contents
Introduction How does the stepper motor work? Important stepper motor characteristics Driving the motor Controlling the motor with joystick - projectIntroduction
Today, stepper motors are found in a variety of devices where precise control and rotation is required. They are used inside 3D printers, CNC machines, scanners and can even be used to control automatic curtain systems in your home. What makes them so popular is their ability to move in tiny, controlled steps which allows engineers and hobbyists to position something very accurately.
In this article, we’ll walk through what stepper motors are, how they work and why they are such a good choice for projects which require precise motion. The stepper motor with the driver module used in this article makes it very easy to start experimenting right away, the driver enables full control of the motor allowing us to focus on creating smooth and precise movement.
How does the stepper motor work?
A stepper motor is a brushless electric motor that moves in discrete steps rather than continuous rotation like in DC motors, allowing for controlled positioning without a feedback sensor. This is achieved by sending electrical pulses that energize the motor’s coils in a specific sequence. There are two main types of stepper motors:
- Bipolar stepper motors (type used in this article) have two coils and require H-bridge control to reverse the direction of the current flow in each coil. Four NPN transistors can be used to provide that control.
- Unipolar stepper motors have a center tap on each coil, allowing them to be driven more simply, but they are less efficient.
Bipolar stepper motors offer higher torque, efficiency and speed, but they require a more complex driver because they need reverse current. They are generally preferred for applications requiring higher torque and efficiency, mainly in precise motion control systems, robotics and precision systems. On the other hand, unipolar motors are simpler and cheaper due to their straightforward driving circuit, but have lower torque and efficiency because they only use half of their windings at a time. They are used in cost-effective environments and less demanding applications, like in basic automation and printers.
Below is a visualization of how different coils are activated in sequence to rotate the shaft:

Each coil in the motor is controlled by an NPN transistor, with each transistor connected to one of the pins (IN1 - IN4) on the board. In this setup the transistors work like switches: when a pin receives an electrical pulse, the transistor then allows the current to flow through the corresponding coil. By turning the transistors on and off in sequence, the coils are energized one after another, which in turn causes the stepper motor to move step by step.

Important stepper motor characteristics
One of the more important characteristics of a stepper motor is its rated current, this tells you how much current each coil can safely handle which in turn directly affects the motor’s torque. Another key characteristic you’ll see is rated voltage listed on stepper motors. Drivers typically use a much higher supply voltage and regulate current electronically, allowing the motor to perform better at different speeds. The last characteristic often looked at is the step angle of the motor, which indicates how far the motor rotates with each full step. 28BYJ-48 stepper motor used in this example has a built-in gearbox with a 64:1 reduction ratio and 5.625° step angle, meaning it takes 2048 steps for the output shaft to make one full rotation.
Driving the motor
Now when you know how stepper motors work, it’s time to start using one. Together with the stepper motor + driver module, we also have a dedicated Arduino library for easy setup and control. Let’s get started!
Example: Simple forward/backward movement
For starters, we’ll simply rotate the motor forward a certain number of steps, and when it completes the turn, then rotate backwards the same number of steps.
#include "Basic-Stepper-Driver-SOLDERED.h"
BasicStepper stepper;
void setup()
{
// Change these to suit your stepper if you want
stepper.setMaxSpeed(500);
stepper.setAcceleration(100);
stepper.moveTo(700);
}
void loop()
{
// If at the end of travel go to the other end
if (stepper.distanceToGo() == 0)
stepper.moveTo(-stepper.currentPosition());
stepper.run();
}
Controlling the motor with joystick - project
To add a little more control to our project, we’ll include a Joystick module to control the motors speed and direction. Also we included a custom 3D printed motor halter piece available on this link: https://www.thingiverse.com/thing:3593641
This project lets you control a stepper motor using a joystick. By moving the joystick left or right on X axis, you can control both the speed and direction of the motor. The further you push the joystick knob in either direction, the faster the motor spins. The system also includes smooth acceleration/deceleration. A button on joystick allows you to toggle the motor on and off, making it more flexible.
Components used:
- ESP32
- Joystick module
- Stepper motor + driver
Here is the main part of the code that sets direction and speed of the motor based on the joystick position.
void loop()
{
// Toggle Run/Stop
if (btn.isPressed)
{
btn.isPressed = false;
runMotor = !runMotor;
}
// Read joystick value -> target_speed
bool inDeadband = false;
if (runMotor)
{
int x = constrain(analogRead(POSX), X_MIN, X_MAX);
if (x > X_CENTER + CENTER_OFFSET)
{
long mag = map(x, X_CENTER + CENTER_OFFSET, X_MAX, 0, (long)MAX_SPEED);
if (mag < 0) mag = 0;
target_speed = (float)mag;
}
else if (x < X_CENTER - CENTER_OFFSET)
{
long mag = map(x, X_CENTER - CENTER_OFFSET, X_MIN, 0, (long)MAX_SPEED);
if (mag < 0) mag = 0;
target_speed = -(float)mag;
}
else
{
// stick centered -> gentle coast to stop
inDeadband = true;
target_speed = 0;
}
}
else
{
target_speed = 0; // disabled -> gentle coast to stop
}
// Time-based smooth stop (deceleration) when not using joystick
unsigned long now = millis();
float dt = (now - last_update_ms) / 1000;
if (dt <= 0) { dt = 0.001; }
last_update_ms = now;
float diff = target_speed - current_speed; // Gap between target and current speed
bool reversing = (current_speed * target_speed) < 0; // Switch from '+' to '-' or '-' to '+'
bool speedingUp = ((diff > 0 && current_speed >= 0) || (diff < 0 && current_speed <= 0)); // If true - accelerate
float accel;
if (!runMotor || inDeadband) { accel = SMOOTH_DECEL; } // If stick centered or motor disabled - SMOOTH DECELERATE
else if (reversing || speedingUp) { accel = ACCEL_UP; } // If accelerating or reversing - ACCEL_UP
else { accel = ACCEL_DOWN; } // ACCEL_DOWN
float maxDelta = accel * dt; // Maximum allowed speed
if (diff > maxDelta) { current_speed += maxDelta; } // Increase speed
else if (diff < -maxDelta) { current_speed -= maxDelta; } // Decrease speed
else { current_speed = target_speed; }
// Avoid "drift", set near zero values to zero
if (current_speed > -0.5f && current_speed < 0.5f && target_speed > -0.5f && target_speed < 0.5f)
{
current_speed = 0;
}
// Drive motor at current speed
stepper.setSpeed(current_speed);
stepper.runSpeed();
}

Full code available on GitHub.