Creating a sketch to control a stepper motor with limit switches to drive a prototype SeeSaw Piston Vessel

Hello everyone, I could do with some help.

I have designed a SeeSaw mechanism to tilt a piston driven injection vessel (basically a metre long syringe) from side to side. Inside the vessel is a ball bearing to mix the slurry inside it (a mixture of sand/xanthan gum/water) - this is a proven concept.

I am using a stepper motor to drive the SeeSaw with a limit switch on either side (see jpg). I am also using a potentiometer to change the speed of the stepper motor.



What I want to achieve:

  1. Stepper motor starts up slowly and moves CCW until the vessel activates the left limit switch and then moves CW until the switch deactivates. this will be home -> zero

  2. Stepper motor moves slowly CW until the vessel activates the right limit switch and then moves CCW until the switch deactivates. this will be the maximum position

  3. Stepper motor cycles CCW/CW between home +a couple of degrees/steps and maximum -a couple of degrees/steps . It should not activate the limit switches but SeeSaw the vessel between them just missing.

Once I have this working I would like to make the limit switches act as safety end stops, i.e. if they are activated the program/stepper motor stops and resets.

I would also like to add an on/off toggle switch to allow me to pause the program if possible without depowering the arduino.

Where I am at:

I have made a sketch using the AccelStepper library, but my coding skills are not up to scratch the result is:

The motor turns juddering, not smooth and slowly, the limit switches intermittently work, but not work as intended.

I have uploaded a diagram of the setup and a schematic of the wiring. Below is the code:

*Motor Control Sketch for SeeSaw Vessel Cradle Control using AccelStepper Library

Created by James Goddings

*/

#include "AccelStepper.h";
//Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper
//Used for programming stepper motors using Arduino microprocessors

//Setup Control Variables


AccelStepper stepperX(1,6,7);//AccelStepper Setup
//1=Easy Driver Interface
//Arduino Pin 6 Connected to PUL- Pin => Pulse width controls speed of motor
//Arduino Pin 7 Connected to DIR- Pin => Controls motor direction

//Define Limit Switch Pins
const int limit_switch_l = 2;//Left Limit Switch Connected to Pin 2
const int limit_switch_r = 3;//Left Limit Switch Connected to Pin 3

//Define Variables
//long TravelX;//Used to Store the Value X Entered in the Serial Monitor
//int move_finished;//Used to CHeck if Move is Completed
long initial_homing=-1;//Used to Home Stepper at Startup
long find_max=1;//Used to Find Max Limit at Startup
long max_position;//Used to Set the Maximum Position of the SeeSaw 
int variable_speed;//Used to Vary Speed of SeeSaw Using Potentiometer


void setup() {
  //Start the Serial Monitor at 9600 Baud Rate
  Serial.begin(9600);

  //Pull Up Resistor Debouncing on Each Switch
  pinMode(limit_switch_l,INPUT_PULLUP);
  pinMode(limit_switch_r,INPUT_PULLUP);

  //Wait for Easy Driver to Wake
  delay(5);

  //Start the Homing Procedure ofthe Stepper Motor on Startup
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Homing
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Homing

  Serial.print("Vessel is finding home position");

  while(digitalRead(limit_switch_l)){//Make the Stepper Move CCW Until the Left Limit Switch is Activated
  stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
  stepperX.run();//Start Moving the Stepper Motor
  initial_homing--;//Decrease by 1 Step if Needed
  delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);//When the Switch is Activated Set This Position as Zero for Now
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Home Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Home Position Seeking
  initial_homing=1;

  while(!digitalRead(limit_switch_l)){//Make the Stepper Move CW Until the Left Limit Switch is Deactivated
  stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
  stepperX.run();//Start Moving the Stepper Motor
  initial_homing++;//Increase by 1 Step if Needed
  delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);
  Serial.println("Homing Complete");
  Serial.println("");
  Serial.println("Vessel now Seeking Max Position");

  //Start the Max Seeking Procedure ofthe Stepper Motor
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Max Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Max Seeking

  while(digitalRead(limit_switch_r)){//Make the Stepper Move CW Until the Right Limit Switch is Activated
  stepperX.moveTo(find_max);//Set the Stepper Motor to Move CW
  stepperX.run();//Start Moving the Stepper Motor
  find_max++;//Increase by 1 Step if Needed
  delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(max_position);//When the Switch is Activated Set This Position as Max Position for Now
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Max Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Max Position Seeking
  find_max=-1;

  while(!digitalRead(limit_switch_r)){//Make the Stepper Move CCW Until the Right Limit Switch is Deactivated
  stepperX.moveTo(find_max);//Set the Stepper Motor to Move CCW
  stepperX.run();//Start Moving the Stepper Motor
  find_max--;//Decrease by 1 Step if Needed
  delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(max_position);
  Serial.println("Max Seeking Complete");
  Serial.println("");
  Serial.println("Vessel Will Now Start Cycling");

}

void loop() {
  variable_speed=map(analogRead(A1),0,1023,300,3000);//Set Potentiometer Controlled Speed Variable to Map on to Potentiomater Position
  long left_position=2;//Add a new left position variable just before left limit switch
  long right_position=max_position-2;//Add a new right position variable just before left limit switch

  stepperX.setMaxSpeed(3000.0);//Set Max Speed High for SeeSaw
  stepperX.setAcceleration(300.0);//Set Acceleration High for SeeSaw
  stepperX.setSpeed(variable_speed);
  stepperX.runToNewPosition(left_position);
  delay(50);
  stepperX.runToNewPosition(right_position);

  Serial.println("Running Test");

}

How long have you been working on this project? What progress have you made? Your skill is based on trial and error and learning. Did you just copy someone eases sketch, or did you design it your self and debug as you went?
What help do you want, as only you have the test setup?

There are certain things to clean up in loop() ...

If you look at these lines what do you expect to happen?

  stepperX.runToNewPosition(left_position);
  delay(50);
  stepperX.runToNewPosition(right_position);

Edit: You are commanding the stepper to go left and 50 ms later to go to the right ... See my post #5 for the reason that the stepper is quickly moving forward/backwards. It's because max_position is not set correctly ...

1 Like

I'd think you shouldn't reset the current position after you've established the relation to limit_switch_l.

You merely need a measure of the number of steps between limit_switch_l to limit_switch_r.

Juddering sounds like a power or misspecification issue. Is your stepper up to the task and is the driver current properly tuned? Does your belt have slop/backlash?

1 Like

There is also a glitch with the use of "max_position" ...

 stepperX.setCurrentPosition(max_position);//When the Switch is Activated Set This Position as Max Position for Now

The function setCurrentPosition() does not change the variable value of its parameter ...

I made a few changes to the code and uploaded it to Wokwi:

Sketch
/*
  Forum: https://forum.arduino.cc/t/creating-a-sketch-to-control-a-stepper-motor-with-limit-switches-to-drive-a-prototype-seesaw-piston-vessel/1227569/4
  Wokwi: https://wokwi.com/projects/390534148140979201


  Motor Control Sketch for SeeSaw Vessel Cradle Control using AccelStepper Library

  Created by James Goddings

  Changes done 2024/02/23

  ec2021

*/

#include "AccelStepper.h";
//Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper
//Used for programming stepper motors using Arduino microprocessors

//Setup Control Variables


AccelStepper stepperX(1, 6, 7); //AccelStepper Setup
//1=Easy Driver Interface
//Arduino Pin 6 Connected to PUL- Pin => Pulse width controls speed of motor
//Arduino Pin 7 Connected to DIR- Pin => Controls motor direction

//Define Limit Switch Pins
const int limit_switch_l = 2;//Left Limit Switch Connected to Pin 2
const int limit_switch_r = 3;//Left Limit Switch Connected to Pin 3

//Define Variables
//long TravelX;//Used to Store the Value X Entered in the Serial Monitor
//int move_finished;//Used to CHeck if Move is Completed
long initial_homing = -1; //Used to Home Stepper at Startup
long find_max = 1; //Used to Find Max Limit at Startup
long max_position;//Used to Set the Maximum Position of the SeeSaw
int variable_speed;//Used to Vary Speed of SeeSaw Using Potentiometer
int newspeed;
long left_position;
long right_position;

void setup() {
  //Start the Serial Monitor at 9600 Baud Rate
  Serial.begin(9600);

  //Pull Up Resistor Debouncing on Each Switch
  pinMode(limit_switch_l, INPUT_PULLUP);
  pinMode(limit_switch_r, INPUT_PULLUP);

  //Wait for Easy Driver to Wake
  delay(5);

  //Start the Homing Procedure ofthe Stepper Motor on Startup
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Homing
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Homing

  Serial.print("Vessel is finding home position");

  while (digitalRead(limit_switch_l)) { //Make the Stepper Move CCW Until the Left Limit Switch is Activated
    stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    initial_homing--;//Decrease by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);//When the Switch is Activated Set This Position as Zero for Now
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Home Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Home Position Seeking
  initial_homing = 1;

  while (!digitalRead(limit_switch_l)) { //Make the Stepper Move CW Until the Left Limit Switch is Deactivated
    stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    initial_homing++;//Increase by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);
  Serial.println("Homing Complete");
  Serial.println("");
  Serial.println("Vessel now Seeking Max Position");

  //Start the Max Seeking Procedure ofthe Stepper Motor
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Max Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Max Seeking

  while (digitalRead(limit_switch_r)) { //Make the Stepper Move CW Until the Right Limit Switch is Activated
    stepperX.moveTo(find_max);//Set the Stepper Motor to Move CW
    stepperX.run();//Start Moving the Stepper Motor
    find_max++;//Increase by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  //stepperX.setCurrentPosition(max_position);//When the Switch is Activated Set This Position as Max Position for Now
  max_position = stepperX.currentPosition();
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Max Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Max Position Seeking
  find_max = -1;

  while (!digitalRead(limit_switch_r)) { //Make the Stepper Move CCW Until the Right Limit Switch is Deactivated
    stepperX.moveTo(find_max);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    find_max--;//Decrease by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  max_position = stepperX.currentPosition();
  stepperX.setCurrentPosition(max_position);
  Serial.println("Max Seeking Complete");
  Serial.println("");
  Serial.println("Vessel Will Now Start Cycling");

  left_position = 2; //Add a new left position variable just before left limit switch
  right_position = max_position - 2; //Add a new right position variable just before left limit switch

  stepperX.setMaxSpeed(3000.0);//Set Max Speed High for SeeSaw
  stepperX.setAcceleration(300.0);//Set Acceleration High for SeeSaw
  Serial.println(left_position);
  Serial.println(right_position);
  delay(2000);
  Serial.println("Running Test");
}


boolean goLeft = true;


void loop() {
  newspeed = map(analogRead(A1), 0, 1023, 300, 3000); //Set Potentiometer Controlled Speed Variable to Map on to Potentiomater Position
  if (variable_speed != newspeed) {
    variable_speed = newspeed;
    stepperX.setSpeed(variable_speed);
  }
  if (goLeft) {
    stepperX.runToNewPosition(left_position);
    Serial.println(stepperX.currentPosition());
    if (stepperX.currentPosition() == left_position) {
      goLeft = false;
    }
  } else {
    stepperX.runToNewPosition(right_position);
    Serial.println(stepperX.currentPosition());
    if (stepperX.currentPosition() == right_position) {
      goLeft = true;
    }
  }
}

It works but has still room for improvement (e.g. stepperX.runToNewPosition() is a blocking function ...).

See here https://wokwi.com/projects/390534148140979201

1 Like

runToNewPosition is a blocking call, so the 50ms delay happens only after the stepper has reached left_position.

But because of the blocking call, the limit switches cannot be used as a safety end stop. A completely blocking free design is needed. Maybe this is easier to achieve with the MobaTools library.

BTW, 'real' safety endstops should never rely on software, but stop the movement by HW.

Yes I found that already. The reason for the problem was mainly a wrong usage of setCurrentPosition() see my post #5 ...

Hi Paul,
I did do an Arduino module at College, but it was pretty basic and nearly 10 years ago. Didn't cover Accelstepper.

I have watched Youtube videos and tried to compile code from their examples (Drone Bot Workshop/Bored Robot/Brainy-Bits/Curious Scientist). Iam now working through each section trying to debug.

I am probably using the wrong loop command using while, because i really want the processes to work through consecutively not concurrently and I think they may be interfering with eachother

James

Thats true, but there are definitely more :wink:

1 Like

This is a suggestion to make your sketch work:

1 Like

And here a sketch that does not use any blocking function and checks the limit switches to stop movement immediately when a limit switch has been triggered:

Sketch;

/*
  Forum: https://forum.arduino.cc/t/creating-a-sketch-to-control-a-stepper-motor-with-limit-switches-to-drive-a-prototype-seesaw-piston-vessel/1227569/4
  Wokwi: https://wokwi.com/projects/390537630494628865


  Motor Control Sketch for SeeSaw Vessel Cradle Control using AccelStepper Library

  Created by James Goddings

  Changes done 2024/02/23

  ec2021

*/

#include "AccelStepper.h";
//Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper
//Used for programming stepper motors using Arduino microprocessors

//Setup Control Variables


AccelStepper stepperX(1, 6, 7); //AccelStepper Setup
//1=Easy Driver Interface
//Arduino Pin 6 Connected to PUL- Pin => Pulse width controls speed of motor
//Arduino Pin 7 Connected to DIR- Pin => Controls motor direction

//Define Limit Switch Pins
const int limit_switch_l = 2;//Left Limit Switch Connected to Pin 2
const int limit_switch_r = 3;//Left Limit Switch Connected to Pin 3

//Define Variables
//long TravelX;//Used to Store the Value X Entered in the Serial Monitor
//int move_finished;//Used to CHeck if Move is Completed
long initial_homing = -1; //Used to Home Stepper at Startup
long find_max = 1; //Used to Find Max Limit at Startup
long max_position;//Used to Set the Maximum Position of the SeeSaw
int variable_speed;//Used to Vary Speed of SeeSaw Using Potentiometer
int newspeed;
long left_position;
long right_position;

void setup() {
  //Start the Serial Monitor at 9600 Baud Rate
  Serial.begin(9600);

  //Pull Up Resistor Debouncing on Each Switch
  pinMode(limit_switch_l, INPUT_PULLUP);
  pinMode(limit_switch_r, INPUT_PULLUP);

  //Wait for Easy Driver to Wake
  delay(5);

  //Start the Homing Procedure ofthe Stepper Motor on Startup
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Homing
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Homing

  Serial.print("Vessel is finding home position");

  while (digitalRead(limit_switch_l)) { //Make the Stepper Move CCW Until the Left Limit Switch is Activated
    stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    initial_homing--;//Decrease by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);//When the Switch is Activated Set This Position as Zero for Now
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Home Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Home Position Seeking
  initial_homing = 1;

  while (!digitalRead(limit_switch_l)) { //Make the Stepper Move CW Until the Left Limit Switch is Deactivated
    stepperX.moveTo(initial_homing);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    initial_homing++;//Increase by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  stepperX.setCurrentPosition(0);
  Serial.println("Homing Complete");
  Serial.println("");
  Serial.println("Vessel now Seeking Max Position");

  //Start the Max Seeking Procedure ofthe Stepper Motor
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Max Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Max Seeking

  while (digitalRead(limit_switch_r)) { //Make the Stepper Move CW Until the Right Limit Switch is Activated
    stepperX.moveTo(find_max);//Set the Stepper Motor to Move CW
    stepperX.run();//Start Moving the Stepper Motor
    find_max++;//Increase by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  //stepperX.setCurrentPosition(max_position);//When the Switch is Activated Set This Position as Max Position for Now
  max_position = stepperX.currentPosition();
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Final Max Position Seeking
  stepperX.setAcceleration(100.0);//Set Acceleration of Stepper Low for Final Max Position Seeking
  find_max = -1;

  while (!digitalRead(limit_switch_r)) { //Make the Stepper Move CCW Until the Right Limit Switch is Deactivated
    stepperX.moveTo(find_max);//Set the Stepper Motor to Move CCW
    stepperX.run();//Start Moving the Stepper Motor
    find_max--;//Decrease by 1 Step if Needed
    delay(5);//5 millisecond delay before looping this
  }

  max_position = stepperX.currentPosition();
  stepperX.setCurrentPosition(max_position);
  Serial.println("Max Seeking Complete");
  Serial.println("");
  Serial.println("Vessel Will Now Start Cycling");

  left_position = 2; //Add a new left position variable just before left limit switch
  right_position = max_position - 2; //Add a new right position variable just before left limit switch

  stepperX.setMaxSpeed(3000.0);//Set Max Speed High for SeeSaw
  stepperX.setAcceleration(300.0);//Set Acceleration High for SeeSaw
  Serial.println(left_position);
  Serial.println(right_position);
  delay(2000);
  Serial.println("Running Test");
  newspeed = -1;
}


boolean goLeft = true;
boolean run    = true;


void loop() {
  checkSpeed();
  if (goLeft) {
    stepperX.moveTo(left_position);
    stepperX.setSpeed(-variable_speed);
  } else {
    stepperX.moveTo(right_position);
    stepperX.setSpeed(variable_speed);
  }
  if (!digitalRead(limit_switch_l) || !digitalRead(limit_switch_r)) {
    if (run) {
      Serial.println("Limit Switch triggered!");
      run = false;
    }
  }
  if (run) {
    stepperX.run();
    if (stepperX.currentPosition() <= left_position) {
      goLeft = false;
    }
    if (stepperX.currentPosition() >= right_position) {
      goLeft = true;
    }
  }
}

void checkSpeed() {
  newspeed = map(analogRead(A1), 0, 1023, 20, 3000); //Set Potentiometer Controlled Speed Variable to Map on to Potentiomater Position
  if (variable_speed != newspeed) {
    variable_speed = newspeed;
  }
}

See on Wokwi: https://wokwi.com/projects/390537630494628865

There might still be some issues, e.g. if the tolerance to the limit switches is not sufficient.

Anyway, you might have a look at @MicroBahner 's MobaTools as they provide very helpful functions for servos, steppers, leds, timer functions and buttons! It's really worth it, Very well documented and lots of examples!

Good luck!

2 Likes

Many thanks to Edison and MicroBahner you have been an amazing help on this. I was not expecting such rapid and excellent responses, I have been looking for an excuse to get into microcontrollers more for a while, having only really dabbled before. It is great to find such dedicated and helpful contributors to the community. I am certainly going to start doing more projects like this so look forward to more.

To clear a few things up:

Very helpful comment, lead me to check the wiring of the stepper motor... The supplier sent the wrong data sheet and I had the wiring from the stepper driver to the motor wired incorrectly, double checked online and found correct data sheet with correct wiring (huh, the different colours do make a difference)!

Once I had the correct wiring it was a different ballgame and I may have got there eventually.

All of your comments were helpful and correct concerning blocking commands which I had gathered were an issue from videos on AccelStepper, but I was struggling to find a way around it.

MicroBahner your suggestion was a solution to the problem, but Edison has taken it one step further. I will definitely be using wokwi from now on and will have a look through your MobaTools to see if there is an even more elegant solution and to get inspiration for further projects.

Many thanks to you all!

Thanks for your appreciation!

Honestly @MicroBahner is the very expert for controlling stepper motors. If you look at his suggestion on Wokwi I suggest to at least combine his approach to setup() and probably my part for loop() if you want to stay with the lib as is.

I also second Microbahner's concern about the limit switches. For safety reasons it is better to have one pair of switches as limit indicators (what they are now) and mount two further switches more to the left and right that cut the motor power in case they are activated. Software is not always reliable...

BTW: My nickname here is ec2021, Edison is just a kind of a title one gets depending on contribution to the forum ... no worries... :wink:

And here a final version based on MicroBahner 's sketch and my last one plus some improvements e.g. debouncing of the limit switches, removed unnecessary code and added comments ...

/*
  Forum: https://forum.arduino.cc/t/creating-a-sketch-to-control-a-stepper-motor-with-limit-switches-to-drive-a-prototype-seesaw-piston-vessel/1227569/4
  Wokwi: https://wokwi.com/projects/390626969872951297

  Motor Control Sketch for SeeSaw Vessel Cradle Control using AccelStepper Library
  Created by James Goddings

  Combining the improvements of MicroBahner
  from https://wokwi.com/projects/390537977584839681

  and ec2021
  from https://wokwi.com/projects/390537630494628865

  In addition
    Removed unused variables
    Removed unnecessary function
    Added stepperX.stop() in case of limit switch activated in loop()
    Debounced the limit switches using a simple class
    Added printing whether the left of right limit switch was activated
    Added lots of comments ...

  Changes done 2024/02/24
  ec2021

*/

#include "AccelStepper.h";
//Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper
//Used for programming stepper motors using Arduino microprocessors

//Setup Control Variables
AccelStepper stepperX(1, 6, 7); //AccelStepper Setup
//1=Easy Driver Interface
//Arduino Pin 6 Connected to PUL- Pin => Pulse width controls speed of motor
//Arduino Pin 7 Connected to DIR- Pin => Controls motor direction

//Define Limit Switch Pins
const int limit_switch_l = 2;//Left Limit Switch Connected to Pin 2
const int limit_switch_r = 3;//Left Limit Switch Connected to Pin 3

//Define Variables
long max_position;   // Used to set the Maximum Position of the SeeSaw
long left_position;  // Used to set the left most position where the SeeSaw
// may be moved to before touching the left limit switch
long right_position; // Used to set the right most position where the SeeSaw
// may be moved to before touching the right limit switch
int variable_speed;  // Used to store the variable speed of SeeSaw using a potentiometer
boolean goLeft = true;  // Used to define the direction the vessel shall be moved to
// Starts with going to the left position
boolean run    = true;  //
// Set to true here so that loop() starts "running"

// Simple class to handle switches/buttons and read their debounced status
class switchType {                      // Class name
  private:                              // private variables/functions can only be used inside the class
    byte pin;                           // Holds the pin number
    byte state = HIGH;                  // Holds the "official" state of digital pin "pin"
    byte lastState = HIGH;              // Holds the last state of pin read in class function "activated()"
    unsigned long lastChange = 0;       // Holds the time in ms when the last status change of pin took place
  public:                               // public variables/functions are available from outside the class
    void init(byte pinNo) {             // Usually called once in setup()) to initialize "pin" 
      pin = pinNo;                      // Sets internal "pin" to the value of "pinNo"
      pinMode(pin, INPUT_PULLUP);       // Initializes the digital pin as INPUT_PULLUP
    }
    boolean activated() {               // Returns true if the pin state is LOW else returns false
      byte actState = digitalRead(pin); // Reads the actual digital state of "pin"
      if (actState != lastState) {      // If this state is different from lastState 
        lastChange = millis();          // we store the time of this change as "lastChange" in ms
        lastState = actState;           // and set lastState to actState 
      }
      if (actState != state && millis() - lastChange > 20) { // if "state" is different from actState
                                        // and the last change has been performed more than 20 ms before
                                        // we accept the state change as stable 
        state = actState;               // and set our official "state" to actState
      }
      return (state == LOW);           // if state is LOW the function returns true else  false
    }
};

switchType limitSwitchLeft;            // We declare the left switch based on the class switchType
switchType limitSwitchRight;           // We declare the right switch based on the class switchType

void setup() {
  //Start the Serial Monitor at 115200 Baud Rate
  Serial.begin(115200);

  //Initialize the limit switches
  limitSwitchLeft.init(limit_switch_l);    // Left switch initialized with the pin limit_switch_l
  limitSwitchRight.init(limit_switch_r);   // Right switch initialized with the pin limit_switch_r

  //Wait for Easy Driver to Wake
  delay(5);

  //Start the Homing Procedure of the Stepper Motor on Startup
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Homing
  stepperX.setAcceleration(500.0);//Set Acceleration of Stepper Low for Homing

  Serial.print("Vessel is finding home position");

  stepperX.moveTo(-10000);//Set the Stepper Motor to Move CCW
  while (!limitSwitchLeft.activated()) { //Make the Stepper Move CCW Until the Left Limit Switch is Activated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.move(200);
  while (limitSwitchLeft.activated()) { //Make the Stepper Move CW Until the Left Limit Switch is Deactivated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.setCurrentPosition(0); // this is the new reference point
  Serial.println("Homing Complete");
  Serial.println("");
  Serial.println("Vessel now Seeking Max Position");

  //Start the Max Seeking Procedure of the Stepper Motor

  stepperX.moveTo(10000);//more than possible max_position

  while (!limitSwitchRight.activated()) { //Make the Stepper Move CW Until the Right Limit Switch is Activated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.move(-200);//Set the Stepper Motor to Move CCW
  while (limitSwitchRight.activated()) { //Make the Stepper Move CCW Until the Right Limit Switch is Deactivated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  max_position = stepperX.currentPosition();
  Serial.print("Max Seeking Complete, "); Serial.println(max_position);
  Serial.println("");
  Serial.println("Vessel Will Now Start Cycling");
  left_position = 2; //Add a new left position variable just before left limit switch
  right_position = max_position - 2; //Add a new right position variable just before left limit switch
  stepperX.setMaxSpeed(3000);//Set Max Speed High for SeeSaw
  stepperX.setAcceleration(300.0);//Set Acceleration High for SeeSaw
  delay(2000);
}


void loop() {
  variable_speed = map(analogRead(A1), 0, 1023, 300, 3000); // Speed Variable mapped to Potentiometer Position
  if (goLeft) {   // If "go to the left" then
    stepperX.moveTo(left_position); // command stepper to go to the left position
    stepperX.setSpeed(-variable_speed); // set speed (negative for turning left)
  } else {         // else -> "go to the right"
    stepperX.moveTo(right_position);  // command stepper to go to the right position
    stepperX.setSpeed(variable_speed); // set speed (positive for turning right)
  }
  if (limitSwitchLeft.activated() ||
      limitSwitchRight.activated()) {  // Check whether one of the limit switches is activated
    if (run) {                         // If yes and run is true ...
      Serial.print(limitSwitchLeft.activated() ? "Left " : "Right ");  // Print either Left or Right
      // assuming that it's unlikely to have both activated at the same time
      Serial.println("Limit Switch triggered!");
      stepperX.stop();                // Stop the stepper
      run = false;                    // Set run to false so that stepperx.Run() is not executed in loop()
      // and to avoid printing the message over and over again ;-)
    }
  }
  if (run) {                          // While run is true
    stepperX.run();                   // execute this line to step forward/backwards
    if (stepperX.currentPosition() <= left_position) {  // if the left position has been reached
      goLeft = false;                                   // switch the direction
    }
    if (stepperX.currentPosition() >= right_position) { // if the right position has been reached
      goLeft = true;                                    // switch the direction
    }
  }
}

To be tested on Wokwi: https://wokwi.com/projects/390626969872951297

Thanks to @MicroBahner for the efforts regarding the homing part!

Good luck!
ec2021

1 Like

ec2021,

Thank you for this I have hardwired the system and I am going to test today, having run it over the past few days on the breadboard.

I have been playing with 2 things:

1 Adding a toggle switch to pause the program once it is in the loop phase for safety and convenience. The simplest way is to add a switch on the pulse output from pin six, which stops the motor, however the program still runs in the background so if you switch back on the position is incorrect. I have been trying to add another true/false : on/off variable to pin 8 and pause the program with if/else statement, but it seems to interupt the other if/else commands. Is there a simple way to do this.

The other thing I have been trying to improve is adding an acceleration/dece;eration to the loop program, however adding setAcceleration commands in before the setSpeed commands only effects one direction of travel, is it a blocking function? The system will work as is, I am worried about longevity with the abrupt change of direction.

I will keep tinkering, but some further guidance would be hugely appreciated.

jg

If you are using the #14 code, one solution would be to use change detection on your pin8 switch, to set/unset the "run" variable.

Acceleration will work better if you don't override the speeds. Setting the target positions with stepperX.moveTo() is enough to apply the acceleration setting and dynamically set appropriate new speeds. If you override the current speed with stepperX.setSpeed() it instantly changes the speed, ignoring acceleration.

Hi @DaveX ,

based on your post I think we have to change from setSpeed() to setMaxSpeed(). That should keep the acceleration/deacceleration while applying changes to the maximum speed for the movement.

Here is a new sketch (on Wokwi https://wokwi.com/projects/391166823708089345) :

/*
  Forum: https://forum.arduino.cc/t/creating-a-sketch-to-control-a-stepper-motor-with-limit-switches-to-drive-a-prototype-seesaw-piston-vessel/1227569/4
  Wokwi: https://wokwi.com/projects/391166823708089345

  Motor Control Sketch for SeeSaw Vessel Cradle Control using AccelStepper Library
  Created by James Goddings

  Combining the improvements of MicroBahner
  from https://wokwi.com/projects/390537977584839681

  and ec2021
  from https://wokwi.com/projects/390537630494628865

  In addition
    Removed unused variables
    Removed unnecessary function
    Added stepperX.stop() in case of limit switch activated in loop()
    Debounced the limit switches using a simple class
    Added printing whether the left of right limit switch was activated
    Added lots of comments ...

  Changes done 2024/02/24
  ec2021

  In addition
     Added the function pressed() to class SwitchType
     Added a toggle switch procedure to stop/start the movement
     Changed from setSpeed to setMaxSpeed (not to influence acceleration/deacceleration)
     Added a function to change maxSpeed only if variable_speed has changed
     Corrected some comments

  Changes done 2024/03/01
  ec2021     

*/

#include "AccelStepper.h";
//Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper
//Used for programming stepper motors using Arduino microprocessors

//Setup Control Variables
AccelStepper stepperX(1, 6, 7); //AccelStepper Setup
//1=Easy Driver Interface
//Arduino Pin 6 Connected to PUL- Pin => Pulse width controls speed of motor
//Arduino Pin 7 Connected to DIR- Pin => Controls motor direction

//Define Limit Switch Pins
const byte limit_switch_l = 2;//Left Limit Switch Connected to Pin 2
const byte limit_switch_r = 3;//Right Limit Switch Connected to Pin 3
const byte startStopSwitchPin = 8; // Switch to stop and restart movement during movement

//Define Variables
long max_position;   // Used to set the Maximum Position of the SeeSaw
long left_position;  // Used to set the left most position where the SeeSaw
// may be moved to before touching the left limit switch
long right_position; // Used to set the right most position where the SeeSaw
// may be moved to before touching the right limit switch
int variable_speed;  // Used to store the variable speed of SeeSaw using a potentiometer
int prev_variable_speed;  // Used to store the variable speed of SeeSaw to detect changes
boolean goLeft = true;  // Used to define the direction the vessel shall be moved to
// Starts with going to the left position
boolean run    = true;  //

// Simple class to handle switches/buttons and read their debounced status
class switchType {                      // Class name
  private:                              // private variables/functions can only be used inside the class
    byte pin;                           // Holds the pin number
    byte state = HIGH;                  // Holds the "official" state of digital pin "pin"
    byte lastState = HIGH;              // Holds the last state of pin read in class function "activated()"
    unsigned long lastChange = 0;       // Holds the time in ms when the last status change of pin took place
  public:                               // public variables/functions are available from outside the class
    void init(byte pinNo) {             // Usually called once in setup()) to initialize "pin"
      pin = pinNo;                      // Sets internal "pin" to the value of "pinNo"
      pinMode(pin, INPUT_PULLUP);       // Initializes the digital pin as INPUT_PULLUP
    }
    boolean activated() {               // Returns true if the pin state is LOW else returns false
      byte actState = digitalRead(pin); // Reads the actual digital state of "pin"
      if (actState != lastState) {      // If this state is different from lastState
        lastChange = millis();          // we store the time of this change as "lastChange" in ms
        lastState = actState;           // and set lastState to actState
      }
      if (actState != state && millis() - lastChange > 20) { // if "state" is different from actState
        // and the last change has been performed more than 20 ms before
        // we accept the state change as stable
        state = actState;               // and set our official "state" to actState
      }
      return (state == LOW);           // if state is LOW the function returns true else  false
    }
    boolean pressed() {               // Returns true if the pin state is LOW else returns false
      byte actState = digitalRead(pin); // Reads the actual digital state of "pin"
      if (actState != lastState) {      // If this state is different from lastState
        lastChange = millis();          // we store the time of this change as "lastChange" in ms
        lastState = actState;           // and set lastState to actState
      }
      if (actState != state && millis() - lastChange > 20) { // if "state" is different from actState
        // and the last change has been performed more than 20 ms before
        // we accept the state change as stable
        state = actState;               // and set our official "state" to actState
        return !state;                  // returns true only once if going from HIGH to LOW else false
      }
      return false;                     // returns false if nothing is done
    }
};

switchType limitSwitchLeft;            // We declare the left switch based on the class switchType
switchType limitSwitchRight;           // We declare the right switch based on the class switchType
switchType startStopSwitch;

void setup() {
  //Start the Serial Monitor at 115200 Baud Rate
  Serial.begin(115200);

  //Initialize the limit switches
  limitSwitchLeft.init(limit_switch_l);     // Left switch initialized with the pin limit_switch_l
  limitSwitchRight.init(limit_switch_r);    // Right switch initialized with the pin limit_switch_r
  startStopSwitch.init(startStopSwitchPin); // Switch to stop or restart the movement

  //Wait for Easy Driver to Wake
  delay(5);

  //Start the Homing Procedure of the Stepper Motor on Startup
  stepperX.setMaxSpeed(100.0);//Set MaxSpeed of Stepper Low for Homing
  stepperX.setAcceleration(500.0);//Set Acceleration of Stepper Low for Homing

  Serial.print("Vessel is finding home position");

  stepperX.moveTo(-10000);//Set the Stepper Motor to Move CCW
  while (!limitSwitchLeft.activated()) { //Make the Stepper Move CCW Until the Left Limit Switch is Activated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.move(200);
  while (limitSwitchLeft.activated()) { //Make the Stepper Move CW Until the Left Limit Switch is Deactivated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.setCurrentPosition(0); // this is the new reference point
  Serial.println("Homing Complete");
  Serial.println("");
  Serial.println("Vessel now Seeking Max Position");

  //Start the Max Seeking Procedure of the Stepper Motor

  stepperX.moveTo(10000);//more than possible max_position

  while (!limitSwitchRight.activated()) { //Make the Stepper Move CW Until the Right Limit Switch is Activated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  stepperX.move(-200);//Set the Stepper Motor to Move CCW
  while (limitSwitchRight.activated()) { //Make the Stepper Move CCW Until the Right Limit Switch is Deactivated
    stepperX.run();//Start Moving the Stepper Motor
  }
  stepperX.stop();
  max_position = stepperX.currentPosition();
  Serial.print("Max Seeking Complete, "); Serial.println(max_position);
  Serial.println("");
  Serial.println("Vessel Will Now Start Cycling");
  left_position = 2; //Add a new left position variable just before left limit switch
  right_position = max_position - 2; //Add a new right position variable just before left limit switch
  stepperX.setMaxSpeed(3000);//Set Max Speed High for SeeSaw
  stepperX.setAcceleration(300.0);//Set Acceleration High for SeeSaw
  delay(2000);
  prev_variable_speed = 0;
}


void loop() {
  if (goLeft) {   // If "go to the left" then
    stepperX.moveTo(left_position); // command stepper to go to the left position
  } else {         // else -> "go to the right"
    stepperX.moveTo(right_position);  // command stepper to go to the right position
  }
  variable_speed = map(analogRead(A1), 0, 1023, 300, 3000); // Speed Variable mapped to Potentiometer Position
  setMaxSpeedIfRequired();
  if (limitSwitchLeft.activated() ||
      limitSwitchRight.activated()) {  // Check whether one of the limit switches is activated
    if (run) {                         // If yes and run is true ...
      Serial.print(limitSwitchLeft.activated() ? "Left " : "Right ");  // Print either Left or Right
      // assuming that it's unlikely to have both activated at the same time
      Serial.println("Limit Switch triggered!");
      stepperX.stop();                // Stop the stepper
      run = false;                    // Set run to false so that stepperx.Run() is not executed in loop()
      // and to avoid printing the message over and over again ;-)
    }
  }
  if (run) {                          // While run is true
    stepperX.run();                   // execute this line to step forward/backwards
    if (stepperX.currentPosition() <= left_position) {  // if the left position has been reached
      goLeft = false;                                   // switch the direction
    }
    if (stepperX.currentPosition() >= right_position) { // if the right position has been reached
      goLeft = true;                                    // switch the direction
    }
  }
  checkStartStop();  // Check the startStopSwitch
}

void setMaxSpeedIfRequired() {
  if (variable_speed != prev_variable_speed) { // Only set a new speed if variable_speed has changed
    prev_variable_speed = variable_speed;
    Serial.print("New max speed: ");
    Serial.println(variable_speed);
    stepperX.setMaxSpeed(variable_speed); // set speed
  }
}

void checkStartStop(){
  if (startStopSwitch.pressed()){  // After a first press 
    while (!startStopSwitch.pressed()){}; // wait for a second press
  }
}
  • Function to recognize a button press for the startStop switch
  • Function to stop/restart during loop() when startStop switch pressed
  • Function to set max speed (only if changed)

The max speed change can only be recognized if there the range between left/right limit is sufficient enough.

Hope it works with the "real thing" as well ...

And always a pleasure to get your inputs!
Regards
ec2021

1 Like

I recently had a similar experience with motor to controller hookup, causing confusion. It appears the OP quickly solved it, but on behalf future visitors to this thread, this is how I arrived at a working solution. I did not like the trial and error process, fearing damage to the driver or motor: Motors suppliers typically offer a pin to coil diagram (left in image). Driver chip datasheets often have a driver chip pin to motor coil diagram (middle), and driver "stick" makers usually publish diagrams showing the chip pin to the stick pin diagram (and possibly to motor coil diagram if you are lucky). So in my image below I made an assumption that all diagrams showing two coils positioned at a 90 degree angle to each other are the same (probably a bad idea), and will feature four labels, two of which are adjacent (see "C" and "B" in the two coil diagram below) positioned next to each other. That provided me the needed information to plot the connections from the motor diagram (left) to the Chip datasheet diagram (middle) to the "stick" pinout diagram (right) Note I added the motor to that image, as the stick manufacturer only supplied labels, and as you can see the Labels of the stick correspond to the TMC2209 chip, and not the motor ABCD. There has got to be a better way for me to sort out these connections - but this worked for me at my IQ. So here's the image, I hope it helps someone.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.