Built-in project · Arduino Uno R3

Stepper speed control with a TMC2209 and a knob on an Arduino Uno

A knob on A0 sets the speed and direction of a NEMA 17 on a quiet TMC2209 with AccelStepper: stop in the middle, up to 120 rpm either way, eased. 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.

Teaching? Assign it to your class in one click.

Intermediate Runs in your browser. Free, and no account needed.

The parts retail for about $46. On Mokxi they're free.

Stepper speed control with a TMC2209 and a knob · Arduino Uno R3live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Stepper motor speed control with a knob, on a TMC2209: the middle of the
// knob is stop, either side turns the NEMA 17 that way, faster further out.
//
//   A0         the potentiometer's wiper (its ends on 5 V and GND)
//   TMC2209    STEP -> pin 2, DIR -> pin 3, EN -> pin 4
//              VIO -> 5V, GND -> GND, VM -> 12 V
//              M1A, M1B -> one coil, M2A, M2B -> the other
//
// The TMC2209 is the quiet driver in most 3D printers. Wired like this, with
// MS1, MS2 and SPREAD left open, it runs StealthChop at 1/8 step: 200 full
// steps a turn times 8 is 1600 pulses a turn, and it interpolates each one to
// 1/256 inside. AccelStepper only has to pulse STEP and set DIR.
//
// EN is active low, so setPinsInverted(false, false, true) tells AccelStepper
// to pull it low to enable. With the knob in the middle for two seconds the
// sketch disables the outputs, so the motor and driver stop drawing holding
// current and run cool; the shaft turns freely then.
//
// A stepper cannot jump from stopped to full speed: past a few hundred steps a
// second it misses steps and stalls with a buzz. So the speed follows the knob
// at ACCEL steps a second every second, and a reversal slows through zero.
//
// Press Run, then turn the knob either side of the middle.

#include <AccelStepper.h>

const int KNOB = A0;
const int STEP_PIN = 2;
const int DIR_PIN = 3;
const int EN_PIN = 4;

const float MAX_SPEED = 3200;  // steps a second: 2 turns a second at 1/8 step
const float ACCEL = 4000;      // steps a second, per second
const int DEAD = 40;           // counts either side of 512

AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

float speed = 0;
unsigned long lastRead = 0;
unsigned long stoppedAt = 0;
bool enabled = true;
int printed = 9999;

float target() {
  int offset = analogRead(KNOB) - 512;
  if (abs(offset) <= DEAD) return 0;
  float s = (abs(offset) - DEAD) * MAX_SPEED / (511 - DEAD);
  if (s > MAX_SPEED) s = MAX_SPEED;
  return offset > 0 ? s : -s;
}

void setup() {
  Serial.begin(9600);
  stepper.setEnablePin(EN_PIN);
  stepper.setPinsInverted(false, false, true);  // EN is active low
  stepper.setMaxSpeed(MAX_SPEED);
  stepper.enableOutputs();
  Serial.println("TMC2209 speed knob: middle is stop, turn either way");
}

void loop() {
  // Step as often as possible; read the knob 20 times a second.
  stepper.runSpeed();
  unsigned long now = millis();
  if (now - lastRead < 50) return;
  lastRead = now;

  float want = target();
  float most = ACCEL * 0.05;  // the change allowed in 50 ms
  if (want > speed) speed = min(speed + most, want);
  if (want < speed) speed = max(speed - most, want);
  stepper.setSpeed(speed);

  if (speed == 0 && want == 0) {
    if (enabled && now - stoppedAt > 2000) {
      stepper.disableOutputs();
      enabled = false;
      Serial.println("stopped: outputs off");
    }
  } else {
    stoppedAt = now;
    if (!enabled) {
      stepper.enableOutputs();
      enabled = true;
    }
  }

  int rpm = (int)(speed * 60 / 1600);
  if (rpm != printed && enabled) {
    printed = rpm;
    Serial.print(rpm);
    Serial.println(" rpm");
  }
}

Parts list

5 parts, plus the jumper wires. They retail for about $46; on Mokxi they're free.

How it is wired

12 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.

  • Ground: Arduino Uno R3 pin GND; TMC2209 stepper driver pin GND
  • Arduino Uno R3 pin 4; TMC2209 stepper driver pin EN
  • Arduino Uno R3 pin 3; TMC2209 stepper driver pin DIR
  • Arduino Uno R3 pin 2; TMC2209 stepper driver pin STEP
  • Arduino Uno R3 pin 5V; Potentiometer pin 3; TMC2209 stepper driver pin VIO
  • Ground: Arduino Uno R3 pin GND; Potentiometer pin 1
  • Arduino Uno R3 pin A0; Potentiometer pin 2
  • 12 V: TMC2209 stepper driver pin VM
  • TMC2209 stepper driver pin M2B; Stepper motor, NEMA 17 pin B2
  • TMC2209 stepper driver pin M2A; Stepper motor, NEMA 17 pin B1
  • TMC2209 stepper driver pin M1A; Stepper motor, NEMA 17 pin A1
  • TMC2209 stepper driver pin M1B; Stepper motor, NEMA 17 pin A2

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.