Stepper dial on the analyzer, on an Arduino Uno
A 28BYJ-48 following a knob, with a logic analyzer on its four coil lines: the half-step sequence walking, and the capture as CSV. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
IntermediateRuns in your browser. Free, and no account needed.
Step by step, with the wiring and the common mistakes: Arduino Stepper Motor: 28BYJ-48 and ULN2003
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Stepper dial: a knob on A0, the shaft follows it.
//
// A0 the potentiometer's wiper; its ends to 5 V and GND
// pins 8..11 IN1 to IN4 on the ULN2003 board
//
// A stepper does not go at a speed, it goes to a *position*, and this is the
// shortest circuit that makes that obvious: turn the knob and the shaft goes
// there and stays there.
//
// # What a driver actually is
//
// A table and a delay. The motor is four coils; switching them in order pulls
// the rotor round one point of a magnetic circle at a time. Nothing in
// firmware/lib/mokxi_stepper.h is cleverer than that, and nothing in the part
// is either; Mokxi's model works out where the field is pointing and moves
// the rotor there, so half-step, wave drive and two-phase-on all fall out of
// one table rather than being three special cases.
//
// # 4096
//
// The rotor turns 5.625 degrees per half-step and the gearbox divides by 64, so
// the output shaft moves 5.625/64 of a degree at a time and a whole turn is
// 4096 half-steps. That number is in every 28BYJ-48 sketch ever written and
// this is where it comes from. Half a turn (2048) is the sweep here, which
// is about as far as a dial wants to go.
//
// # The rate ceiling is real
//
// The datasheet's idle out-traction frequency is over 1000 Hz, so steps closer
// together than a millisecond are steps the rotor cannot follow. Mokxi's part
// misses them exactly as a real motor does. Turn the knob quickly and the
// shaft follows at its own top speed rather than teleporting, which is what a
// real one does too.
//
// # It does not know where it is
//
// Nothing here reads the shaft. The sketch counts the steps it has sent and
// assumes the motor made them, which is true until it is not (a stall, a
// jam, or a rate past the ceiling), and is why real positioning systems have
// a limit switch to home against. Press nothing and watch: if the count and
// the shaft ever disagree, the sketch will never find out.
#include "mokxi_stepper.h"
const int KNOB = A0;
const int IN1 = 8;
const int IN2 = 9;
const int IN3 = 10;
const int IN4 = 11;
// Half a turn of the output shaft across the knob's whole sweep.
const long SWEEP_STEPS = STEPS_PER_TURN_HALF / 2;
// The knob has to move this far before the motor is asked to, so the last bit
// of the ADC does not make it hum.
const int DEADBAND = 8;
Stepper28byj motor(IN1, IN2, IN3, IN4);
long at = 0; // where the sketch believes the shaft is, in half-steps
int lastKnob = -1000;
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Uno: 28BYJ-48 following a knob on A0");
motor.begin();
}
void loop() {
int knob = analogRead(KNOB);
if (knob > lastKnob - DEADBAND && knob < lastKnob + DEADBAND) {
return;
}
lastKnob = knob;
// Where the knob says the shaft should be, in half-steps from the start.
long want = ((long)knob * SWEEP_STEPS) / 1023;
long move = want - at;
if (move == 0) {
return;
}
motor.stepHalf(move, 1500);
at = want;
// Tenths of a degree, so there is no floating point anywhere:
// degrees = half-steps * 5.625 / 64, and ten times that in tenths.
long tenths = (at * 5625L) / 6400L;
Serial.print("knob ");
Serial.print(knob);
Serial.print(" -> ");
Serial.print(at);
Serial.print(" half-steps, ");
Serial.print(tenths / 10);
Serial.print(".");
Serial.print(tenths % 10);
Serial.println(" degrees");
// Let go once it is there: nothing holds the shaft, nothing draws current,
// and a real one stops getting warm.
motor.release();
}
Parts list
5 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
7 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 11; Stepper motor, 28BYJ-48 with ULN2003 pin IN4; Logic analyzer, four channels pin D3
- Arduino Uno R3 pin 10; Stepper motor, 28BYJ-48 with ULN2003 pin IN3; Logic analyzer, four channels pin D2
- Arduino Uno R3 pin 9; Stepper motor, 28BYJ-48 with ULN2003 pin IN2; Logic analyzer, four channels pin D1
- Arduino Uno R3 pin 8; Stepper motor, 28BYJ-48 with ULN2003 pin IN1; Logic analyzer, four channels pin D0
- Arduino Uno R3 pin 5V; Stepper motor, 28BYJ-48 with ULN2003 pin VCC; Potentiometer pin 3
- Ground: Arduino Uno R3 pin GND; Stepper motor, 28BYJ-48 with ULN2003 pin GND; Potentiometer pin 1; Logic analyzer, four channels pin GND
- Arduino Uno R3 pin A0; Potentiometer pin 2
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.