thoughts on stallguard:
it requires the right combination of speed, velocity and sensitivity to work properly. Too slow and it doesn’t work at all.
I used several libraries to get it all working, and pay attention to the values it sets for the motor speed etc.
I used the following sketch:
#include <Arduino.h>
#include <FastAccelStepper.h>
#include <HardwareSerial.h>
#include <TMCStepper.h>
#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Motor Pins
#define STEP_PIN 8
#define DIR_PIN 7
#define ENABLE_PIN 9
#define STALLGUARD 3
#define RXD2 17
#define TXD2 16
// Switch Pins
#define UP_SWITCH 18
#define DOWN_SWITCH 19
#define E_STOP_SWITCH 12
// LED Pins
#define RED_LED 10
#define GREEN_LED 14
#define R_SENSE 0.11f
#define DRIVER_ADDRESS 0b00
const long set_velocity = 30000;
const int set_acceleration = 8000;
const int set_current = 650;
const int set_stall = 2;
const long set_tcools = 150;
const int motor_microsteps = 64;
const int full_steps_per_revolution = 200;
// Leadscrew pitch
const float LEADSCREW_PITCH_MM = 4.0; // 4mm per revolution
const float MM_TO_INCH = 0.0393701;
const float DISTANCE_PER_MICROSTEP_MM = LEADSCREW_PITCH_MM / (full_steps_per_revolution * motor_microsteps); // 0.0003125mm/microstep
// LCD Setup - 4x20 LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);
volatile bool stalled_motor = false;
static bool moving_up = false;
static bool moving_down = false;
static long current_position = 0;
static float position_inches = 0.0;
enum LastSwitch { NONE, UP, DOWN, ESTOP };
static LastSwitch last_switch_pressed = NONE;
enum State { INIT_TOP, MOVE_BOTTOM, MOVE_TOP, IDLE };
static State current_state = INIT_TOP;
#define SERIAL_PORT_2 Serial2
FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;
TMC2209Stepper driver(&SERIAL_PORT_2, R_SENSE, DRIVER_ADDRESS);
void stalled_position() {
stalled_motor = true;
Serial.println("STALL DETECTED!");
}
void setup() {
Serial.begin(115200);
SERIAL_PORT_2.begin(115200);
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
digitalWrite(RED_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
Wire.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Starting...");
delay(1000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
pinMode(UP_SWITCH, INPUT_PULLUP);
pinMode(DOWN_SWITCH, INPUT_PULLUP);
pinMode(E_STOP_SWITCH, INPUT_PULLUP);
pinMode(ENABLE_PIN, OUTPUT);
pinMode(STALLGUARD, INPUT);
attachInterrupt(digitalPinToInterrupt(STALLGUARD), stalled_position, RISING);
driver.begin();
driver.toff(4);
driver.blank_time(24);
driver.I_scale_analog(false);
driver.internal_Rsense(false);
driver.mstep_reg_select(true);
driver.microsteps(motor_microsteps);
driver.TPWMTHRS(0);
driver.semin(0);
driver.shaft(true);
driver.en_spreadCycle(false);
driver.pdn_disable(true);
driver.VACTUAL(0);
driver.rms_current(set_current);
driver.SGTHRS(set_stall);
driver.TCOOLTHRS(set_tcools);
engine.init();
stepper = engine.stepperConnectToPin(STEP_PIN);
stepper->setDirectionPin(DIR_PIN);
stepper->setEnablePin(ENABLE_PIN);
stepper->setAutoEnable(true);
stepper->setSpeedInHz(set_velocity);
stepper->setAcceleration(set_acceleration);
digitalWrite(RED_LED, LOW);
Serial.println("Initialization complete. Moving to top...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Moving to Top");
stepper->runBackward(); // Move up
moving_up = true;
}
void loop() {
static bool firstLoop = true;
if (firstLoop) {
digitalWrite(GREEN_LED, HIGH);
firstLoop = false;
}
bool up_state = digitalRead(UP_SWITCH);
bool down_state = digitalRead(DOWN_SWITCH);
bool estop_state = digitalRead(E_STOP_SWITCH);
// Update position
current_position = stepper->getCurrentPosition();
float distance_mm = current_position * DISTANCE_PER_MICROSTEP_MM;
position_inches = distance_mm * MM_TO_INCH;
if (position_inches < 0) position_inches = -position_inches; // Absolute value
// Debug position during motion
if (moving_up || moving_down) {
Serial.print("Steps: ");
Serial.print(current_position);
Serial.print(", Distance (in): ");
Serial.println(position_inches, 4);
}
// State machine
switch (current_state) {
case INIT_TOP:
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in ");
if (stalled_motor) {
stepper->forceStop();
stepper->setCurrentPosition(0);
current_position = 0;
moving_up = false;
stalled_motor = false;
Serial.println("Top reached. Zero set. Moving to bottom...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Moving to Bottom");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
stepper->runForward(); // Move down
moving_down = true;
current_state = MOVE_BOTTOM;
}
break;
case MOVE_BOTTOM:
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in ");
if (stalled_motor) {
stepper->forceStop();
long bottom_position = stepper->getCurrentPosition();
float distance_traveled_mm = bottom_position * DISTANCE_PER_MICROSTEP_MM;
float distance_traveled_in = distance_traveled_mm * MM_TO_INCH;
if (distance_traveled_in < 0) distance_traveled_in = -distance_traveled_in;
Serial.print("Bottom reached. Distance traveled: ");
Serial.print(distance_traveled_in, 4);
Serial.println(" inches");
Serial.print("Steps traveled: ");
Serial.println(bottom_position);
stepper->setCurrentPosition(0);
current_position = 0;
moving_down = false;
stalled_motor = false;
Serial.println("Moving back to top...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Moving to Top");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
stepper->runBackward(); // Move up
moving_up = true;
Serial.println("Transitioning to MOVE_TOP state");
current_state = MOVE_TOP;
}
break;
case MOVE_TOP:
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in ");
if (stalled_motor) {
stepper->forceStop();
moving_up = false;
stalled_motor = false;
Serial.println("Top reached again. Awaiting instructions...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
current_state = IDLE;
}
break;
case IDLE:
lcd.setCursor(0, 0);
lcd.print("Ready "); // Extra spaces to clear previous text
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in ");
static bool last_estop_state = HIGH;
if (estop_state == LOW && last_estop_state == HIGH && !stalled_motor && last_switch_pressed != ESTOP) {
stepper->forceStop();
moving_up = false;
moving_down = false;
last_switch_pressed = ESTOP;
Serial.println("Emergency Stop Activated!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ESTOP");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
while (digitalRead(E_STOP_SWITCH) == LOW) {
delay(100);
}
Serial.println("Emergency Stop Released. Awaiting instructions...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
} else if (estop_state == LOW && last_estop_state == HIGH && last_switch_pressed == ESTOP) {
Serial.println("Repeated E-Stop press ignored...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ESTOP Ignored");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
while (digitalRead(E_STOP_SWITCH) == LOW) {
delay(100);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
}
last_estop_state = estop_state;
if (estop_state == HIGH) {
if (up_state == LOW && !moving_up && !moving_down) {
Serial.println("UP switch pressed. Starting upward motion...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Going Up");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
stalled_motor = false;
moving_up = true;
last_switch_pressed = UP;
stepper->runBackward();
} else if (down_state == LOW && !moving_down && !moving_up) {
Serial.println("DOWN switch pressed. Starting downward motion...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Going Down");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
stalled_motor = false;
moving_down = true;
last_switch_pressed = DOWN;
stepper->runForward();
} else if (moving_up) {
if (down_state == LOW || stalled_motor) {
stepper->forceStop();
moving_up = false;
last_switch_pressed = (down_state == LOW) ? DOWN : NONE;
Serial.println(down_state == LOW ? "DOWN switch pressed. Stopping..." : "Stalled. Stopping...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
}
} else if (moving_down) {
if (up_state == LOW || stalled_motor) {
stepper->forceStop();
moving_down = false;
last_switch_pressed = (up_state == LOW) ? UP : NONE;
Serial.println(up_state == LOW ? "UP switch pressed. Stopping..." : "Stalled. Stopping...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ready");
lcd.setCursor(0, 2);
lcd.print("Pos: ");
lcd.print(position_inches, 2);
lcd.print(" in");
}
}
}
break;
}
if (moving_up || moving_down) {
Serial.print("SG_RESULT: "); Serial.println(driver.SG_RESULT());
Serial.print("TSTEP: "); Serial.println(driver.TSTEP());
}
delay(10);
}
void moveUp() {}
void moveDown() {}