Stepper motor not behaving properly

I am using a NEMA 23 4.25:1 Geared stepper motor with a TB6600 Driver and an original Arduino Mega 2560.
I know everything is wired up correctly (including a 16x2 I2C LCD and 12pos Rotary Switch)

I am trying to create a control box that I can select the amount of rotation of a stepper motor with a rotary switch (NOT a Potentiometer or Rotary encoder) and press one button for CW motion and another button for equal CCW motion and a display to tell me the amount of rotation I have selected

When I run a simple stepper motor sketch which only spins the motor constantly it works as it should

/* Example sketch to control a stepper motor with TB6600 stepper motor driver and Arduino without a library: continuous rotation. More info: https://www.makerguides.com */

// Define stepper motor connections:
#define dirPin 8
#define stepPin 9

void setup() {
  // Declare pins as output:
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  // Set the spinning direction CW/CCW:
  digitalWrite(dirPin, HIGH);
}

void loop() {
  // These four lines result in 1 step:
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(60);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(60);
}

When I run my sketch the LCD displays everything as its meant to and the buttons do send either a CW or CCW signal but the motor just ticks off the steps like a second hand on a clock (I hope that makes sense) I have no idea what is wrong.

#include <LiquidCrystal_I2C.h>
#include <Wire.h> // Library for I2C communication


// Wiring: SDA pin is connected to 20 and SCL pin to 21.
// Connect to LCD via I2C, default address 0x27
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

////4-position rotary switch
const int firstRotaryPin = 23;
const int lastRotaryPin = 34;

// Define stepper motor connections:
#define dirPin 8
#define stepPin 9

int button1 = 2; ////button 1 input
int button2 = 3; ////button 2 input
int input1 = LOW;

int distance = 100;
int StepCounter = 0;
int Stepping = false;


void setup() {
  // Initiate the LCD:
  lcd.init();
  lcd.backlight();

  for ( int i = firstRotaryPin; i <= lastRotaryPin; i++) {
    pinMode( i, INPUT);
    digitalWrite( i, HIGH); // turn on internal pullup resistor
  }

  pinMode(button1, INPUT); ////for button 1 input
  pinMode(button2, INPUT); ////for button 2 input

  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  digitalWrite(dirPin, LOW);
  digitalWrite(stepPin, LOW);

  pinMode(2, INPUT);
  pinMode(3, INPUT);
}

// returns the position of the rotary switch, 1-12
// or returns 0 if no rotary switch is hooked up
int getRotaryValue() {
  for ( int i = firstRotaryPin; i <= lastRotaryPin; i++) {
    int val = digitalRead( i ); // look at a rotary switch input
    if ( val == LOW ) { // it's selected
      return (i - firstRotaryPin + 1); // return a value that ranges 1 - 12
    }
  }
  return 0; // error case
}


void loop() {
  int rotaryPos = getRotaryValue();

  lcd.setCursor(1, 0); // Set the cursor on the third column and first row.
  lcd.print("#1=FWD  #2=REV");


  if ( rotaryPos == 1 ) {
    distance = 425;
    lcd.setCursor(2, 1); // Set the cursor on the third column and first row.
    lcd.print("1/4 Rotation  ");
  }
  else if ( rotaryPos == 2 ) {
    distance = 850;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("  1/2 Rotation  ");
  }
  else if ( rotaryPos == 3 ) {
    distance = 1275;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("  3/4 Rotation  ");
  }
  else if ( rotaryPos == 4 ) {
    distance = 1700;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   1 Rotation  ");
  }
  else if ( rotaryPos == 5 ) {
    distance = 2125;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   2 Rotation  ");
  }
  else if ( rotaryPos == 6 ) {
    distance = 2550;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   3 Rotation  ");
  }
  else if ( rotaryPos == 7 ) {
    distance = 2975;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   4 Rotation  ");
  }
  else if ( rotaryPos == 8 ) {
    distance = 3400;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   5 Rotation  ");
  }
  else if ( rotaryPos == 9 ) {
    distance = 3825;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   7 Rotation  ");
  }
  else if ( rotaryPos == 10 ) {
    distance = 4250;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   8 Rotation  ");
  }
  else if ( rotaryPos == 11 ) {
    distance = 4675;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("   9 Rotation  ");
  }
  else if ( rotaryPos == 12 ) {
    distance = 5100;
    lcd.setCursor(0, 1); // Set the cursor on the third column and first row.
    lcd.print("  10 Rotation  ");
  }

  input1 = digitalRead(button1);
  if (input1 == LOW) { //Checking for button push
  }
  {
    input1 = digitalRead(button2);
    if (input1 == LOW) { //Checking for button push
    }
    if (input1 == HIGH) { //Checking for button push
    }
  }

  if (digitalRead(3) == LOW && Stepping == false)
  {
    digitalWrite(dirPin, LOW);
    Stepping = true;
  }
  if (digitalRead(2) == LOW && Stepping == false)
  {
    digitalWrite(dirPin, HIGH);
    Stepping = true;
  }

  if (Stepping == true)
  {
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(500);

    StepCounter = StepCounter + 1;

    if (StepCounter == distance)
    {
      StepCounter = 0;
      Stepping = false;
      delay(1000);
    }
  }
}

I will admit from the start I am very new to this but I just cant wrap my head around what is wrong with my code.

Thanks
Craig

You are trying to step WAY, WAY too fast for full steps.

Start slow and work your way up.

void loop() {
  // These four lines result in 1 step:
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(60);
  digitalWrite(stepPin, LOW);
  delay(10);  //10 ms/step 
}

Your delays in the second code are rather larger than the ones in the one that's working as expected.

Also, you're printing to the LCD on every iteration of loop. That burns some time too.

wildbill:
Your delays in the second code are rather larger than the ones in the one that's working as expected.

Also, you're printing to the LCD on every iteration of loop. That burns some time too.

How do I stop it from printing to the LCD every run of the loop?

craigstaples:
How do I stop it from printing to the LCD every run of the loop?

Look at the blink without delay example sketch that comes with the IDE. First though I'd reduce the delays you're using and see what you get.

jremington:
You are trying to step WAY, WAY too fast for full steps.

Start slow and work your way up.

void loop() {

// These four lines result in 1 step:
 digitalWrite(stepPin, HIGH);
 delayMicroseconds(60);
 digitalWrite(stepPin, LOW);
 delay(10);  //10 ms/step
}

I tried your suggested changes and it had no effect on the motors behavior

I find "no effect on behavior" impossible to believe.

To what value have you set the driver current limit, and are you using microsteps?

What is the motor current rating?

Microsteps won't work properly if the current limit is not set properly, that is, set to or lower than the motor current rating.

Re LCD: you should write to the screen only when the display needs to be changed.

jremington:
I find "no effect on behavior" impossible to believe.

To what value have you set the driver current limit, and are you using microsteps?

What is the motor current rating?

Microsteps won't work properly if the current limit is not set properly, that is, set to or lower than the motor current rating.

Re LCD: you should write to the screen only when the display needs to be changed.

The motor is 2.8A per phase and I have the driver set to 2.5A 2.7A Peak and 800 Steps per revolution

The loop suggested in reply #1 should result in a bit less than 100 steps/second. If that is not the case, something is seriously wrong.

With most drivers, the step is initiated on the rising edge of the pulse on the STEP input, and in most cases you can replace the 60 microsecond delay in that loop with 1 microsecond.

In the second bit of code, the loop that reads the rotary switch needs to be completely rethought.

Right now it appears that every time through the loop, you read the rotary switch and reset the distance to be traveled. Instead, use a state machine to separate encoder reads from step activity -- you don't want to read the encoder again until the motor has finished executing the move.

Remove the nonfunctional button code.

first of all well done to post code as code-sections.

if I understand right the sequence of your program is:

set rotary-switch to a value 1-12
show chosen number on the LC-Display
program waits for a button-press
there are two buttons one for CW-rotation one for CCW-rotation
depending on this value drive stepper-motor a certain amount of steps

So indeed this can be programmed as a statemachine
here is a link to a tutorial about statemachines

read this tutorial and then come back with your questions.
You can ask as many questions as you like. You will get answers if you show some own effort in learning.
About creating stepper-pulses I can suggest a solution how step-creation can be made independent
from the rest of the code.

But I will only post it if you post a first attempt of a statemachine of your program. It doesn't even have to compile
just a first attempt how it might works. By providing this piece of code the forum can see your level of understanding and can adapt explanations to be understandable for you.

So give it a try
best regards Stefan

Here is an outline for a simple state machine to consider:

#define READ_ENCODER 1
#define READ_START_SWITCH 2
#define MOVE 3

int state = READ_ENCODER;  //global variable

void loop() {

if (state == READ_ENCODER) {
   value = get_rotary_switch_value();
   lcd_display_value(value);
   state == READ_START_SWITCH;
}

if (state == READ_START_SWITCH) {
   if (digitalRead(switch_pin) == HIGH) state=MOVE;
}

if (state == MOVE) {
   lcd_display_moving();
   for (int i=0; i<value; i++) step_motor();
   lcd_display_done();
   state = READ_ENCODER;
}
} //end loop()

Well this works....sort of
It runs the stepper surprisingly well and is running at a nice speed for my application and the LCD is displaying everything but the 2 buttons do nothing as the motor constantly moves again and again according to the position I have the rotary switch in.
I realize I didn't achieve the State Functions properly

#include <Stepper.h>

#include <LiquidCrystal_I2C.h>
#include <Wire.h> // Library for I2C communication


// Wiring: SDA pin is connected to 20 and SCL pin to 21.
// Connect to LCD via I2C, default address 0x27
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

//4-position rotary switch connections
const int firstRotaryPin = 23;
const int lastRotaryPin = 34;

// Define stepper motor connections:
#define motorSteps 3200
#define motorPin1 8
#define motorPin2 9

// initialize of the Stepper library:
Stepper myStepper(motorSteps, motorPin1, motorPin2);

// button connections:
int button1 = 2; ////button 1 input
int button2 = 3; ////button 2 input
int input1 = LOW;

int distance = 100;
int StepCounter = 0;
int Stepping = false;

//=================================================

void setup() {
  // set the motor speed at 200 RPMS:
  myStepper.setSpeed(100);

  // Initiate the LCD:
  lcd.init();
  lcd.backlight();

  lcd.setCursor(1, 0); // Set the cursor on the third column and first row.
  lcd.print("#1=FWD  #2=REV");
  lcd.setCursor(6, 1); // Set the cursor on the third column and first row.
  lcd.print("Rotation");
  lcd.setCursor(2, 1);

  for ( int i = firstRotaryPin; i <= lastRotaryPin; i++) {
    pinMode( i, INPUT);
    digitalWrite( i, HIGH); // turn on internal pullup resistor
  }

  pinMode(button1, INPUT); ////for button 1 input
  pinMode(button2, INPUT); ////for button 2 input

  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, LOW);

  pinMode(2, INPUT);
  pinMode(3, INPUT);
}

//===============================================================

void loop() {
  updateRotarySwitchState();
  updateRotarySwitchPos();
  updateLcdState();
  updateMotorMove();
}

//===============================================================

void updateRotarySwitchState() {

  int rotaryPos = getRotaryValue();
}
// returns the position of the rotary switch, 1-12
// or returns 0 if no rotary switch is hooked up
int getRotaryValue() {
  for ( int i = firstRotaryPin; i <= lastRotaryPin; i++) {
    int val = digitalRead( i ); // look at a rotary switch input
    if ( val == LOW ) { // it's selected
      return (i - firstRotaryPin + 1); // return a value that ranges 1 - 12
    }
  }
  return 0; // error case
}

//==============================================================

void updateLcdState() {

  lcd.setCursor(1, 0); // Set the cursor on the third column and first row.
  lcd.print("#1=FWD  #2=REV");
  lcd.setCursor(6, 1); // Set the cursor on the third column and first row.
  lcd.print("Rotation");
  lcd.setCursor(2, 1);
}

//==============================================================

void updateRotarySwitchPos() {

  int rotaryPos = getRotaryValue();

  if ( rotaryPos == 1 ) {
    lcd.print("1/4");
    myStepper.step(850);
  }
  else if ( rotaryPos == 2 ) {
    lcd.print("1/2");
    myStepper.step(1700);
  }
  else if ( rotaryPos == 3 ) {
    lcd.print("3/4");
    myStepper.step(1275);
  }
  else if ( rotaryPos == 4 ) {
    lcd.print(" 1 ");
    myStepper.step(3400);
  }
  else if ( rotaryPos == 5 ) {
    lcd.print(" 2 ");
    myStepper.step(6800);
  }
  else if ( rotaryPos == 6 ) {
    lcd.print(" 3 ");
    myStepper.step(10200);
  }
  else if ( rotaryPos == 7 ) {
    lcd.print(" 4 ");
    myStepper.step(13600);
  }
  else if ( rotaryPos == 8 ) {
    lcd.print(" 5 ");
    myStepper.step(17000);
  }
  else if ( rotaryPos == 9 ) {
    lcd.print(" 6 ");
    myStepper.step(20400);
  }
  else if ( rotaryPos == 10 ) {
    lcd.print(" 7 ");
    myStepper.step(23800);
  }
  else if ( rotaryPos == 11 ) {
    lcd.print(" 8 ");
    myStepper.step(27200);
  }
  else if ( rotaryPos == 12 ) {
    lcd.print(" 9 ");
    myStepper.step(30600);
  }
}

//=====================================

void updateMotorMove() {
  input1 = digitalRead(button1);
  {
    input1 = digitalRead(button2);
    if (input1 == HIGH) { //Checking for button push
    }
  }

  if (digitalRead(3) == LOW && Stepping == false)
  {
    digitalWrite(motorPin1, LOW);
    Stepping = true;
  }
  if (digitalRead(2) == LOW && Stepping == false)
  {
    digitalWrite(motorPin1, HIGH);
    Stepping = true;
  }
  if (Stepping == true)
  {
    // These four lines result in 1 step:
    digitalWrite(motorPin2, HIGH);
    delay(500);
    digitalWrite(motorPin2, LOW);
    delay(500);
  }
  {
    StepCounter = StepCounter + 1;

    if (StepCounter == distance)
    {
      StepCounter = 0;
      Stepping = false;
      delay(1000);
    }
  }
}

what your program does is running down consecutively the function-calls you have programmed.

well .... ----

things to improve:

button1 and button2 do not light up a big LED-number showing a "1" or a big "2"
they do something different. The names should tell straightforward what happens if the button gets pressed.
I guess rotateCW and rotateCCW but I#m not shure

your function "updateRotarySwitchPos() " does not update the rotaryswitch-position

depending on the rotary-switch-position it calls a stepper-function that makes the steppermotor move

what function updatemotormove really does is setting up rotation-direction and moving the stepper-motor
until distance is reached.

I recommend a programming style of

a function that does just one thing and the functions name tells this one thing

  • getRotarySwitchPosition
    -showSwitchPositionAndSteps
  • setupStepDistance
  • waitForStartButtonCW_CCW
  • showMessageMotorMoving
  • moveMotor

these names are selfexplaining clearly stating what each function does.

This enables to test each part of the program that is a functional unit and if tested move on to the next part
You can use the serialmonitor to show what is happening inside the code = which part of the code gets executed
For the step-pulse-creation you should not use serial.print() etc. because
stepper-motors need a very tight and regular timing and the serial-output is disturbing this.

The way I have listed it above could be programmed as functions
similar to what you have written so far but in a much easier to understand way because each function does only one thing.

The other way is to use a statemachine. Therefore you would have to really read a statemachine-tutorial.
best regards Stefan