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.
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
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
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?
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 ...
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?
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 ...).
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.
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
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;
}
}
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!
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.
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...
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
}
}
}
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.
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.
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.
/*
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
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.