Stepper Motor Control
28BYJ-48 + ULN2003
Control a 28BYJ-48 stepper motor with an Arduino UNO and ULN2003 driver — including forward/reverse rotation, variable speed, button controls, and serial commands. Fully simulated in Wokwi, no hardware needed.
Components Required (Wokwi)
All components are simulated — use the free Wokwi Arduino Simulator at wokwi.com. No physical hardware required.
Project Features
Pin Connections
⚠️ Note: The Stepper() constructor uses the pin order IN1, IN3, IN2, IN4 — this is correct for the 28BYJ-48's coil sequence.
Button Controls
All buttons use hardware debouncing (50ms) in software. They are wired with INPUT_PULLUP — no external resistors needed.
diagram.json
Paste this into the diagram.json tab in Wokwi to auto-wire all components instantly.
{
"version": 1,
"author": "28BYJ-48 Stepper Motor Control",
"editor": "wokwi",
"parts": [
{
"type": "wokwi-arduino-uno", "id": "uno",
"top": 0, "left": 0, "attrs": {}
},
{
"type": "wokwi-stepper-motor", "id": "stepper1",
"top": -100, "left": 300,
"attrs": { "size": "28BYJ-48" }
},
{
"type": "wokwi-pushbutton", "id": "btn1",
"top": 120, "left": -200,
"attrs": { "color": "green", "label": "Forward" }
},
{
"type": "wokwi-pushbutton", "id": "btn2",
"top": 120, "left": -100,
"attrs": { "color": "red", "label": "Reverse" }
},
{
"type": "wokwi-pushbutton", "id": "btn3",
"top": 180, "left": -200,
"attrs": { "color": "blue", "label": "Speed+" }
},
{
"type": "wokwi-pushbutton", "id": "btn4",
"top": 180, "left": -100,
"attrs": { "color": "blue", "label": "Speed-" }
},
{
"type": "wokwi-pushbutton", "id": "btn5",
"top": 240, "left": -150,
"attrs": { "color": "yellow", "label": "STOP" }
},
{
"type": "wokwi-led", "id": "led1",
"top": -80, "left": 500,
"attrs": { "color": "green", "label": "Forward" }
},
{
"type": "wokwi-led", "id": "led2",
"top": -20, "left": 500,
"attrs": { "color": "red", "label": "Reverse" }
},
{
"type": "wokwi-resistor", "id": "r1",
"top": -60, "left": 480,
"attrs": { "value": "220" }
},
{
"type": "wokwi-resistor", "id": "r2",
"top": 0, "left": 480,
"attrs": { "value": "220" }
}
],
"connections": [
[ "uno:GND.1", "stepper1:GND", "black", [ "v0" ] ],
[ "uno:5V", "stepper1:V+", "red", [ "v0" ] ],
[ "uno:8", "stepper1:A-", "orange", [ "v0" ] ],
[ "uno:9", "stepper1:A+", "yellow", [ "v0" ] ],
[ "uno:10", "stepper1:B-", "green", [ "v0" ] ],
[ "uno:11", "stepper1:B+", "blue", [ "v0" ] ],
[ "btn1:1.l", "uno:2", "green", [ "v0" ] ],
[ "btn1:2.l", "uno:GND.2", "black", [ "v0" ] ],
[ "btn2:1.l", "uno:3", "red", [ "v0" ] ],
[ "btn2:2.l", "uno:GND.2", "black", [ "v0" ] ],
[ "btn3:1.l", "uno:4", "blue", [ "v0" ] ],
[ "btn3:2.l", "uno:GND.2", "black", [ "v0" ] ],
[ "btn4:1.l", "uno:5", "blue", [ "v0" ] ],
[ "btn4:2.l", "uno:GND.2", "black", [ "v0" ] ],
[ "btn5:1.l", "uno:6", "yellow", [ "v0" ] ],
[ "btn5:2.l", "uno:GND.2", "black", [ "v0" ] ],
[ "uno:12", "r1:1", "green", [ "v0" ] ],
[ "r1:2", "led1:A", "green", [ "v0" ] ],
[ "led1:C", "uno:GND.3", "black", [ "v0" ] ],
[ "uno:13", "r2:1", "red", [ "v0" ] ],
[ "r2:2", "led2:A", "red", [ "v0" ] ],
[ "led2:C", "uno:GND.3", "black", [ "v0" ] ]
],
"dependencies": {}
}
Arduino Code
/* 28BYJ-48 Stepper Motor Control with ULN2003 Driver ===================================================== Forward/Reverse, variable speed, 5 buttons, serial commands, step tracking Author: Arduino Project | Wokwi Simulator */ #include <Stepper.h> // Motor: 28BYJ-48 has 64:1 gear ratio → 32 steps × 64 = 2048 #define STEPS_PER_REV 2048 // ULN2003 driver pins (IN1–IN4) #define IN1 8 #define IN2 9 #define IN3 10 #define IN4 11 // Button pins #define BTN_FORWARD 2 #define BTN_REVERSE 3 #define BTN_SPEED_UP 4 #define BTN_SPEED_DOWN 5 #define BTN_STOP 6 // LED indicators #define LED_FORWARD 12 #define LED_REVERSE 13 // Note: Stepper(steps, IN1, IN3, IN2, IN4) — coil order for 28BYJ-48 Stepper myStepper(STEPS_PER_REV, IN1, IN3, IN2, IN4); // State variables int motorSpeed = 10; // RPM (1–15 recommended) int currentPosition = 0; bool motorRunning = false; int direction = 1; // 1 = fwd, -1 = rev unsigned long stepCount = 0; // Debounce state bool lastBtnForward = HIGH; bool lastBtnReverse = HIGH; bool lastBtnSpeedUp = HIGH; bool lastBtnSpeedDown = HIGH; bool lastBtnStop = HIGH; void setup() { Serial.begin(9600); Serial.println("========================================"); Serial.println(" 28BYJ-48 Stepper Motor Controller"); Serial.println("========================================"); pinMode(BTN_FORWARD, INPUT_PULLUP); pinMode(BTN_REVERSE, INPUT_PULLUP); pinMode(BTN_SPEED_UP, INPUT_PULLUP); pinMode(BTN_SPEED_DOWN, INPUT_PULLUP); pinMode(BTN_STOP, INPUT_PULLUP); pinMode(LED_FORWARD, OUTPUT); pinMode(LED_REVERSE, OUTPUT); myStepper.setSpeed(motorSpeed); digitalWrite(LED_FORWARD, LOW); digitalWrite(LED_REVERSE, LOW); printInstructions(); } void loop() { checkButtons(); checkSerialCommands(); if (motorRunning) { myStepper.step(direction); stepCount++; currentPosition += direction; if (stepCount % 100 == 0) displayStatus(); } delay(1); } void checkButtons() { bool b; b = digitalRead(BTN_FORWARD); if (b == LOW && lastBtnForward == HIGH) { delay(50); if (digitalRead(BTN_FORWARD) == LOW) rotateForward(); } lastBtnForward = b; b = digitalRead(BTN_REVERSE); if (b == LOW && lastBtnReverse == HIGH) { delay(50); if (digitalRead(BTN_REVERSE) == LOW) rotateReverse(); } lastBtnReverse = b; b = digitalRead(BTN_SPEED_UP); if (b == LOW && lastBtnSpeedUp == HIGH) { delay(50); if (digitalRead(BTN_SPEED_UP) == LOW) increaseSpeed(); } lastBtnSpeedUp = b; b = digitalRead(BTN_SPEED_DOWN); if (b == LOW && lastBtnSpeedDown == HIGH) { delay(50); if (digitalRead(BTN_SPEED_DOWN) == LOW) decreaseSpeed(); } lastBtnSpeedDown = b; b = digitalRead(BTN_STOP); if (b == LOW && lastBtnStop == HIGH) { delay(50); if (digitalRead(BTN_STOP) == LOW) stopMotor(); } lastBtnStop = b; } void checkSerialCommands() { if (Serial.available() > 0) { char cmd = Serial.read(); switch (cmd) { case 'F': case 'f': rotateForward(); break; case 'R': case 'r': rotateReverse(); break; case 'S': case 's': stopMotor(); break; case '+': increaseSpeed(); break; case '-': decreaseSpeed(); break; case '1': rotate90Degrees(); break; case '2': rotate180Degrees(); break; case '3': rotate360Degrees(); break; case 'I': case 'i': displayStatus(); break; case 'Z': case 'z': resetPosition(); break; case 'H': case 'h': printInstructions(); break; } } } void rotateForward() { motorRunning = true; direction = 1; digitalWrite(LED_FORWARD, HIGH); digitalWrite(LED_REVERSE, LOW); Serial.println(">>> FORWARD ROTATION <<<"); } void rotateReverse() { motorRunning = true; direction = -1; digitalWrite(LED_FORWARD, LOW); digitalWrite(LED_REVERSE, HIGH); Serial.println(">>> REVERSE ROTATION <<<"); } void stopMotor() { motorRunning = false; digitalWrite(LED_FORWARD, LOW); digitalWrite(LED_REVERSE, LOW); Serial.print(">>> STOPPED — Total steps: "); Serial.println(stepCount); } void increaseSpeed() { if (motorSpeed < 15) { motorSpeed++; myStepper.setSpeed(motorSpeed); } Serial.print("Speed: "); Serial.print(motorSpeed); Serial.println(" RPM"); } void decreaseSpeed() { if (motorSpeed > 1) { motorSpeed--; myStepper.setSpeed(motorSpeed); } Serial.print("Speed: "); Serial.print(motorSpeed); Serial.println(" RPM"); } void rotate90Degrees() { int steps = STEPS_PER_REV / 4; Serial.println("Rotating 90°..."); myStepper.step(steps); currentPosition += steps; stepCount += steps; Serial.println("Done!"); } void rotate180Degrees() { int steps = STEPS_PER_REV / 2; Serial.println("Rotating 180°..."); myStepper.step(steps); currentPosition += steps; stepCount += steps; Serial.println("Done!"); } void rotate360Degrees() { Serial.println("Full rotation 360°..."); myStepper.step(STEPS_PER_REV); currentPosition += STEPS_PER_REV; stepCount += STEPS_PER_REV; Serial.println("Done!"); } void resetPosition() { currentPosition = 0; stepCount = 0; Serial.println("Position counter reset to zero"); } void displayStatus() { Serial.println("=== Motor Status ==="); Serial.print("Running: "); Serial.println(motorRunning ? "YES" : "NO"); Serial.print("Direction: "); Serial.println(direction == 1 ? "FORWARD" : "REVERSE"); Serial.print("Speed: "); Serial.print(motorSpeed); Serial.println(" RPM"); Serial.print("Position: "); Serial.print(currentPosition); Serial.println(" steps"); Serial.print("Rotations: "); Serial.println((float)currentPosition / STEPS_PER_REV, 2); Serial.println("==================="); } void printInstructions() { Serial.println("BUTTON: Btn1=Fwd Btn2=Rev Btn3=Spd+ Btn4=Spd- Btn5=Stop"); Serial.println("SERIAL: F=Fwd R=Rev S=Stop +=Spd+ -=Spd- 1=90° 2=180° 3=360° I=Info Z=Reset H=Help"); Serial.println("Ready!"); }
Serial Command Reference
Open the Serial Monitor at 9600 baud and send single characters to control the motor in real time.
Run the Simulation
Copy the diagram.json into Wokwi's diagram tab to auto-wire everything.
Copy the sketch into the code editor tab.
Press the green start button to run the simulation.
Click the virtual push buttons to control direction and speed.
Open Serial Monitor, type F, R, 1–3 for precise control.
Comments
Post a Comment