Knob stepper on an Arduino Uno
A rotary encoder on pins 2, 3 and 4 steers a 28BYJ-48 click for click; press the knob and the shaft goes home. The whole build, an Arduino Uno and 2 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.
Teaching? Assign it to your class in one click.
Intermediate Runs in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Knob stepper: a rotary encoder steers a stepper motor, click for click.
//
// encoder CLK -> pin 2, DT -> pin 3, SW -> pin 4, + -> 5V, GND -> GND
// ULN2003 IN1 -> pin 8, IN2 -> pin 9, IN3 -> pin 10, IN4 -> pin 11
// + -> 5V, - -> GND
//
// The encoder has 20 clicks a turn and the 28BYJ-48 takes 2048 steps a turn,
// so each click moves the shaft about 102 steps, 18 degrees: turn the knob
// once and the shaft turns once. Press the knob to send the shaft home.
//
// # Reading the encoder
//
// CLK and DT are two switches that open and close a quarter of a click apart.
// Every change is looked up in a table of all sixteen moves: a real quarter
// step counts +1 or -1, and a jump of two states, which is what a bouncing
// contact looks like, counts nothing. Four quarter steps make a click.
//
// Pins 2 and 3 are the Uno's two interrupt pins, so the reading happens in an
// interrupt on every change. That matters here: while the motor is stepping,
// loop() is busy for a fifth of a second a click, and a knob turned in that
// time would be missed if loop() had to notice it.
#include <Stepper.h>
const int CLK = 2;
const int DT = 3;
const int SW = 4;
const int STEPS_PER_TURN = 2048;
const int CLICKS_PER_TURN = 20;
Stepper motor(STEPS_PER_TURN, 8, 10, 9, 11);
const int8_t QUARTER[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
volatile uint8_t state = 3;
volatile int8_t quarters = 0;
volatile long clicks = 0;
long position = 0; // where the shaft is, in steps from home
long shownClicks = 0;
bool lastSw = HIGH;
uint8_t pair() {
return (uint8_t)((digitalRead(CLK) << 1) | digitalRead(DT));
}
void onTurn() {
uint8_t now = pair();
if (now == state) return;
quarters += QUARTER[(state << 2) | now];
state = now;
if (now == 3) {
if (quarters >= 4) clicks++;
if (quarters <= -4) clicks--;
quarters = 0;
}
}
void moveTo(long target) {
if (target != position) {
motor.step((int)(target - position));
position = target;
}
for (int pin = 8; pin <= 11; pin++) digitalWrite(pin, LOW);
}
void setup() {
pinMode(CLK, INPUT_PULLUP);
pinMode(DT, INPUT_PULLUP);
pinMode(SW, INPUT_PULLUP);
motor.setSpeed(15);
Serial.begin(9600);
Serial.println("Mokxi Uno: knob stepper");
state = pair();
attachInterrupt(digitalPinToInterrupt(CLK), onTurn, CHANGE);
attachInterrupt(digitalPinToInterrupt(DT), onTurn, CHANGE);
}
void loop() {
noInterrupts();
long now = clicks;
interrupts();
if (now != shownClicks) {
shownClicks = now;
Serial.print("click ");
Serial.println(now);
moveTo(now * STEPS_PER_TURN / CLICKS_PER_TURN);
}
bool sw = digitalRead(SW);
if (sw == LOW && lastSw == HIGH) {
noInterrupts();
clicks = 0;
interrupts();
shownClicks = 0;
Serial.println("home");
moveTo(0);
}
lastSw = sw;
}
Parts list
4 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
9 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
- Arduino Uno R3 pin 10; Stepper motor, 28BYJ-48 with ULN2003 pin IN3
- Arduino Uno R3 pin 9; Stepper motor, 28BYJ-48 with ULN2003 pin IN2
- Arduino Uno R3 pin 8; Stepper motor, 28BYJ-48 with ULN2003 pin IN1
- Arduino Uno R3 pin 4; Rotary encoder, KY-040 pin SW
- Arduino Uno R3 pin 3; Rotary encoder, KY-040 pin DT
- Arduino Uno R3 pin 2; Rotary encoder, KY-040 pin CLK
- Arduino Uno R3 pin 5V; Stepper motor, 28BYJ-48 with ULN2003 pin VCC; Rotary encoder, KY-040 pin VCC
- Ground: Arduino Uno R3 pin GND; Stepper motor, 28BYJ-48 with ULN2003 pin GND; Rotary encoder, KY-040 pin GND
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.