Hello all. I am a beginner in Arduino IDE and this is my first post here. I have written codes using AccelStepper to move a linear stage, and also written a code using ezButton to print serial messages when microswitches are triggered. I am now trying to combine them together to have a linear stage run its full stroke length, where it will hit a microswitch and stop. Then, run the full stroke length the opposite way until it hits a different microswitch, at which point it will center itself between the two. I had a code that counted steps, but have stripped it down to try and find the issue. When I run the below code the stage will move absurdly slow. It seems to be about 1 step per second (the full stroke length is 40,000 steps). It is not a speed issue as the speed used below is also used in the code I have that works to move the stage. I have not changed any wiring for the stage or the switches. Any guidance on why I cant get the stepper motor to move in this code would be greatly appreciated! Thanks in advance.
#include <ezButton.h>
#include <AccelStepper.h>
ezButton limitSwitch1(7); // create ezButton object that attach to pin 7;
ezButton limitSwitch2(8); // create ezButton object that attach to pin 8;
AccelStepper stepperX(1,3,5); // Defaults to AccelStepper::FULL4WIRE (4 pins) on 1,3,5
void setup()
{
Serial.begin(9600);
int state1 = limitSwitch1.getState();
int state2 = limitSwitch2.getState();
while(state1 == HIGH)
{
stepperX.setSpeed(600);
stepperX.runSpeed();
}
while(state2 == HIGH)
{
stepperX.setSpeed(-600);
stepperX.runSpeed();
millis();
}
// stepperX.moveTo(10000); // positive step is toward stepper motor
}
void loop()
{
limitSwitch1.loop(); // MUST call the loop() function first
limitSwitch2.loop();
if (limitSwitch1.isPressed()){
Serial.println("The limit switch ONE: UNTOUCHED -> TOUCHED");
}
if (limitSwitch1.isReleased()){
Serial.println("The limit switch ONE: TOUCHED -> UNTOUCHED");
}
if(limitSwitch2.isPressed()){
Serial.println("The limit switch TWO: UNTOUCHED -> TOUCHED");
}
if(limitSwitch2.isReleased()){
Serial.println("The limit switch TWO: TOUCHED -> UNTOUCHED");
}
delay(100);
}
The code does not make sense to me -- state1 and state2 will never change after they are first set. If you want them to change, you have to re-check them within their while() {....} loops.
In setup, change the 2 whiles to an if else. If I recall correctly you set speed once then execute runspeed once. If that does not work, check the samples in AccelStepper
You should start with one of the example-codes that you can find in the example-folder.
There is an example bounce
// Bounce.pde
// -*- mode: C++ -*-
//
// Make a single stepper bounce from one limit to another
//
// Copyright (C) 2012 Mike McCauley
// $Id: Random.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $
#include <AccelStepper.h>
// Define a stepper and the pins it will use
AccelStepper stepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
void setup()
{
// Change these to suit your stepper if you want
stepper.setMaxSpeed(100);
stepper.setAcceleration(20);
stepper.moveTo(500);
}
void loop()
{
// If at the end of travel go to the other end
if (stepper.distanceToGo() == 0)
stepper.moveTo(-stepper.currentPosition());
stepper.run();
}
This demo-code includes the function-call
.setMaxSpeed(100);
which is the one that makes all the difference
#include <ezButton.h>
#include <AccelStepper.h>
int button1 = 7;
int button2 = 8;
int state1, state2;
ezButton limitSwitch1(button1); // create ezButton object that attach to pin 7;
ezButton limitSwitch2(button2); // create ezButton object that attach to pin 8;
AccelStepper stepperX(1, 3, 5); // Defaults to AccelStepper::FULL4WIRE (4 pins) on 1,3,5
int stepperSpeed = 1000;
void setup() {
Serial.begin(9600);
stepperX.setMaxSpeed(stepperSpeed); // <<<<=== THIS makes all the difference
stepperX.setSpeed(0);
}
void loop() {
limitSwitch1.loop(); // MUST call the loop() function first
limitSwitch2.loop();
if (state2) {
stepperX.setSpeed(-stepperSpeed);
}
if (state1) {
stepperX.setSpeed(stepperSpeed);
}
if (limitSwitch1.isReleased()) {
Serial.println("The limit switch ONE: TOUCHED -> UNTOUCHED");
state1 = 1;
state2 = 0;
}
if (limitSwitch2.isReleased()) {
Serial.println("The limit switch TWO: TOUCHED -> UNTOUCHED");
state2 = 1;
state1 = 0;
}
//unconditional call that makes the stepper run all the time
stepperX.runSpeed();
//delay(10); no delay needed. AccelStepper does set the speed with .setSpeed()
}```
https://wokwi.com/projects/420782545561486337
Thank you all for the help. Stefan, when you initialize the variables "state1" and "state2", do they default as 0? then when switch 1 is touched, that would prompt the 2nd and statement
if (state1) {
stepperX.setSpeed(stepperSpeed);
}
correct? I am trying to understand fully. The linear stage is changing directions as I press the switches, but my next objective is to get is to center and "home" between the two switches. Would you recommend using a variable++ with each iteration to count steps, or use the built in commands in Accelstepper library (such as setCurrentPosition() and runToNewPosition()) to try and set a zero at one switch, end at the other switch, and go to the middle? I have tried both methods without success in either.
As a very general rule:
always and if I write always I mean always post your most actualcomplete sketch. Each and every time.
How should somebody help you if your code does not yet work if you don't provide the code?
AccelStepper offers internal counting the position and positioning functions.
But I want to recommend a different library for driving steppers
The MobaTools.
The big advantage of the mobatools-library is that the step-pulses are created in the background. This means your code can do other things in parallel. As soon as you want to do something in parallel with accelstepper you have to keep code-execution very very fast to make sure that stepper.run() is called often enough to create the step-pulse right in time.
Not nescessary with the mobatools. If your stepper shall move set and forget target position. The timer-interrupt will do in the background.
Hello again! I have tried to implement MobaTools, but have not fully figured it out. I started with ChatGPT (which did not work) and I built off of it from there using the MobaTools master pdf. Code is attached. Stepper motor runs, pauses and changes direction when switch 1 is hit, pauses and changes when switch 2 is hit, and then runs back indefinitely toward switch 1 rather than stopping between the two. Based on serial window (attached) it appears that the code is moving the motor toward maybe a total step value based on current position rather than moving the amount of steps that the variable specifies? Any guidance on correctly using MobaTools to figure this last part out would be greatly appreciated. Thanks in advance.
#include <MobaTools.h> // Include the MobaTools library
#include <ezButton.h> // Library for limit switch debouncing
// Pin definitions
#define STEP_PIN 4 // Stepper motor step pin
#define DIR_PIN 6 // Stepper motor direction pin
#define LIMIT_SWITCH1 7 // Limit switch at one end
#define LIMIT_SWITCH2 8 // Limit switch at the other end
// Stepper configuration (MobaTools stepper object)
MoToStepper stepper(200, STEPDIR); // 200 steps per revolution, Step/Dir mode
// Limit switches
ezButton limitSwitch1(LIMIT_SWITCH1);
ezButton limitSwitch2(LIMIT_SWITCH2);
// Variables
long totalStepsBetween = 0; // Stores steps between limit switches
bool movingForward = true; // Direction flag
bool moveToMidpoint = false; // Flag for midpoint movement
void setup() {
Serial.begin(9600); // Initialize serial communication for debugging
// Configure stepper motor
stepper.attach(STEP_PIN, DIR_PIN); // Attach step/dir pins
stepper.setSpeed(500); // Set speed (steps per second)
stepper.setRampLen(20); // Smooth acceleration & deceleration
// Set up limit switches
limitSwitch1.setDebounceTime(50);
limitSwitch2.setDebounceTime(50);
// Initiate movement
// homeStepper(); // Move toward limit switch 1 at startup
// Serial.println("Starting movement.");
}
void loop() {
// Update limit switches
limitSwitch1.loop();
limitSwitch2.loop();
// If the first limit switch is pressed
if (limitSwitch1.isPressed()) {
stepper.stop();
movingForward = false;
Serial.println("Reached Limit Switch 1.");
totalStepsBetween = 0;
delay(500);
}
// If the second limit switch is pressed
if (limitSwitch2.isPressed()) {
stepper.stop();
movingForward = true;
Serial.print("Reached Limit Switch 2. Total Steps: ");
Serial.println(totalStepsBetween);
moveToMidpoint = true;
delay(500);
}
// Move to midpoint
if (moveToMidpoint) {
Serial.print("Moving to midpoint: ");
Serial.println(totalStepsBetween / 2);
stepper.move(totalStepsBetween / 2); // Move back halfway
totalStepsBetween--;
totalStepsBetween--;
while (!stepper.moving()) {
stepper.stop();
Serial.println("Reached midpoint. Stopping.");
}
// moveToMidpoint = false;
}
// Normal motion between switches
if (!moveToMidpoint) {
if (movingForward) {
stepper.move(2000); // Move in forward direction
} else {
stepper.move(-2000); // Move in reverse direction
totalStepsBetween++;
}
}
}
// Homing function to move to limit switch 1
void homeStepper() {
stepper.move(-20000); // Move towards limit switch 1
stepper.stop(); // Stop at home position
stepper.setZero(); // Reset step counter
}
in programming you have to specify very precise what behaviour you want.
Even if you type a prompt for an AI.
Beeing able to write such a prompt requires a precise description. The rules for such a description are
avoid general terms like "this", "that", "them", it"
avoid unprecise descriptions like "switch" specifiy precisely the FIRST switch
repeat the precise specifying in EACH and EVERY case NEVER use generalised terms like "this", "this", "that", "them", it"
describe EACH and EVERY detail what relevant parts of your hardware are doing
describe PRECISE how your hardware is connected.
Whatever you leave UNprecise is an invitation for the AI to guess.
This precision requires knowledge and experience.
Which you get by learning YOURSELF
the following prompt is not yet optimised for that.
prompt for chatGPT
write a code for arduino-IDE that does the following: use the MobaTools-library and the ezButton-library.
There is an axle driven by a steppermotor. On both ends of the axle there are limit-switches.
Make the stepper-motor drive towards first limitswitch. If limit-switch is switched stop steppermotor.
set this point as zero. drive towards the second limit switch until second limit-switch switches.
calculate the coordinate in the middle between the two limit-switches. drive to this middle.
answer from chatGPT
#include <MobaTools.h>
#include <ezButton.h>
// Define stepper motor
const int stepPin = 9;
const int dirPin = 8;
MoToStepper stepper(200, STEPDIR); // 200 steps per revolution, Step/Dir mode
// Define limit switches
const int limitSwitch1Pin = 2;
const int limitSwitch2Pin = 3;
ezButton limitSwitch1(limitSwitch1Pin);
ezButton limitSwitch2(limitSwitch2Pin);
long zeroPos = 0;
long endPos = 0;
long midPos = 0;
void setup() {
Serial.begin(115200);
stepper.attach(stepPin, dirPin);
stepper.setSpeed(200); // Set speed to 200 steps/sec
stepper.setRampLen(20); // Set ramp length for smooth acceleration
limitSwitch1.setDebounceTime(50);
limitSwitch2.setDebounceTime(50);
// Homing to first limit switch
stepper.setSpeed(100);
stepper.rotate(-1); // Move towards first limit switch
while (!limitSwitch1.getState()) {
limitSwitch1.loop();
}
stepper.stop();
zeroPos = stepper.getPos(); // Set this position as zero
Serial.println("Homed to Zero Position");
delay(500);
// Move towards second limit switch
stepper.rotate(1);
while (!limitSwitch2.getState()) {
limitSwitch2.loop();
}
stepper.stop();
endPos = stepper.getPos();
Serial.print("End Position: ");
Serial.println(endPos);
delay(500);
// Calculate middle position
midPos = (zeroPos + endPos) / 2;
Serial.print("Middle Position: ");
Serial.println(midPos);
// Move to middle position
stepper.moveTo(midPos);
while (stepper.moving()) {
// Wait for stepper to reach target
}
Serial.println("Reached Middle Position");
}
void loop() {
// Nothing in loop, process runs once in setup
}
Your attempt to compute the totalStepsBetween is completely wrong. You are not counting steps but loop cycles. So your totalStepsBetween is far too high ( what can also be seen in your serial output).
Let MoToStepper count the steps - it can do much better
N.B. that's not the only problem in your sketch, but at first the most important.
Apologies if these questions seem trivial, or if it seems like I am not trying to figure it out between posts. Again, I am pretty new to Arduino, and the coding setup (especially with a powerful library like MobaTools) is not intuitive to me. The majority of my prior coding experience is MATLAB.
Below is my current iteration of the code. It works, hooray, although Im sure it could be optimized. I decided to use the 'rotate' commands since they run perpetually until the next 'stop'. They are pretty slow, so I will likely have to switch to loops with a variable denoting forward or backward motion, correct? Seems like it would be much simpler for me to communicate I want the rotate function to rotate faster. Is there a way to do that? It would keep my code from adding more nested logic and initialized variables.
#include <MobaTools.h> // Include the MobaTools library
#include <ezButton.h> // Library for limit switch debouncing
// Pin definitions
#define STEP_PIN 4 // Stepper motor step pin
#define DIR_PIN 6 // Stepper motor direction pin
#define LIMIT_SWITCH1 7 // Limit switch at one end
#define LIMIT_SWITCH2 8 // Limit switch at the other end
// initialize switches with ezbutton
ezButton limitSwitch1(LIMIT_SWITCH1);
ezButton limitSwitch2(LIMIT_SWITCH2);
// initialize stepper motor with Moba
MoToStepper myStepper(100, STEPDIR);
// counter variable
long totalSteps = 0;
long posCheck = 4;
long distToGo = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
myStepper.attach(STEP_PIN , DIR_PIN);
Serial.print("INITIATE HOMING CODE ");
// steps per 10 seconds
myStepper.rotate(1);
// Set up limit switches
limitSwitch1.setDebounceTime(50);
limitSwitch2.setDebounceTime(50);
}
void loop() {
limitSwitch1.loop();
limitSwitch2.loop();
// If the first limit switch is pressed
if (limitSwitch1.isPressed()) {
myStepper.stop();
Serial.println("Reached Limit Switch 1. Position:");
posCheck = myStepper.readSteps();
Serial.println(posCheck);
delay(1000);
myStepper.setZero();
myStepper.rotate(-1);
}
// If the second limit switch is pressed
if (limitSwitch2.isPressed()) {
myStepper.stop();
Serial.print("Reached Limit Switch 2. Position: ");
posCheck = myStepper.readSteps();
Serial.println(posCheck);
delay(1000);
myStepper.rotate(10000);
}
if (limitSwitch2.isReleased()) {
myStepper.stop();
delay(1000);
myStepper.moveTo(posCheck/2);
while (myStepper.moving()) {
Serial.print("Distance To Go: ");
Serial.println(myStepper.distanceToGo());
delay(500);
}
Serial.print("HOMED AND CENTERED");
}
}
If you want to communicate to Mobatools that myStepper should move faster, 120RPM for example, you use myStepper.setSpeed(1200) with the target speed as 10x the RPM.
If you want to communicate to the program using serial input or button-presses that you want different speeds, then it might be good to write your code differently in order to handle doing several things at the same time (e.g.: managing speed changes, sequencing motions, and delaying between different motions?)
One of the ways to control the sequencing of motions is to use a Finite State Machine, with tests to shift between states. Several of the Mobatools examples use state machines to handle sequencing. A relatively simple example of using a FSM for stepper motion is the Mobatools stepper Example 2 that uses distinct states for rotating CW, pausing, rotating CCW, pausing, and then repeating.
For setSpeed() to work correctly, it is important to tell MobaTools the actual steps/revolution when instantiating the object. But I doubt that tis is correct;
I never saw a stepper with 100 steps per revolution. ( but maybe a gear or something ... )
An alternative could be to use setSpeedSteps() to set the speed in 10x steps/sec.
This setting is independet of the steps/revolution. But anyway the steps/rev should be set correctly.
Hello all, posting this here for the greater good of Arduino IDE beginners in the future who have similar problems to me. My project was getting a linear stage controlled by a stepper motor to center between two limit switches and then move if the user presses a button. Thanks to the people above who were helpful with suggestions.
No wiring diagram essentially there is 2 switches (one at each end of the stage stroke) and 3 buttons (left, right, re-home). All 5 of these are controlled with ezButton and MobaTools does the stage positioning and step counting. Hope this is a good building place for others doing similar things.
// P. Sullivan - 02.25.25
#include <MobaTools.h> // Include the MobaTools library
#include <ezButton.h> // Library for limit switch debouncing
// Pin definitions
#define STEP_PIN 4 // Stepper motor step pin
#define DIR_PIN 6 // Stepper motor direction pin
#define LIMIT_SWITCH1 A5 // Limit switch at one end
#define LIMIT_SWITCH2 A4 // Limit switch at the other end
#define HOME_SWITCH 3 //homing switch
#define TWD_MOTR A1 //Move stage after homed toward motor
#define TWD_END A0 //Move stage after homed away from motor
// initialize switches with ezbutton
ezButton limitSwitch1(LIMIT_SWITCH1);
ezButton limitSwitch2(LIMIT_SWITCH2);
ezButton homeSwitch(HOME_SWITCH);
ezButton twdMotr(TWD_MOTR);
ezButton twdEnd(TWD_END);
// initialize stepper motor with Moba - 200 since NEMA11
MoToStepper myStepper(200, STEPDIR);
// counter variable
long totalSteps = 0;
long posCheck = 4;
long distToGo = 0;
int needToHome = 1;
long backToZero = 0;
// setup Serial output and stepper rotate speed
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
myStepper.attach(STEP_PIN, DIR_PIN);
Serial.print(" INITIATE HOMING CODE ");
// Set a standard Spped for .rotate to execute at
myStepper.setSpeed(3000);
// rotate in pos direction (toward stepper motor)
myStepper.rotate(1);
// Set up limit switches
limitSwitch1.setDebounceTime(50);
limitSwitch2.setDebounceTime(50);
homeSwitch.setDebounceTime(50);
}
void loop() {
limitSwitch1.loop();
limitSwitch2.loop();
homeSwitch.loop();
twdMotr.loop();
twdEnd.loop();
// initiate homing
if (needToHome == 1) {
// If the first limit switch is pressed
if (limitSwitch1.isPressed()) {
myStepper.stop();
Serial.println("Reached Limit Switch 1. Position:");
posCheck = myStepper.readSteps();
Serial.println(posCheck);
delay(1000);
myStepper.setZero();
myStepper.rotate(-1);
}
// If the second limit switch is pressed
if (limitSwitch2.isPressed()) {
myStepper.stop();
Serial.print("Reached Limit Switch 2. Position: ");
posCheck = myStepper.readSteps();
Serial.println(posCheck);
delay(1000);
myStepper.rotate(1);
}
if (limitSwitch2.isReleased()) {
myStepper.stop();
delay(1000);
myStepper.moveTo(posCheck / 2);
// serial display to show steps until centered
while (myStepper.moving()) {
Serial.print("Distance To Go: ");
Serial.println(myStepper.distanceToGo());
delay(500);
}
myStepper.setZero();
needToHome = 0;
Serial.println("HOMED AND CENTERED");
Serial.println(needToHome);
}
}
if (needToHome == 0) {
// homing Button
if (homeSwitch.isPressed()) {
needToHome = 1;
Serial.println("Homing Switch Initiated. Homing...");
myStepper.rotate(1);
}
// move toward motor button
if (twdMotr.isPressed()) {
myStepper.doSteps(5000); // roughly half inch
Serial.println("Move twd motr");
// make sure switches not triggered
}
// move toward end button
if (twdEnd.isPressed()) {
myStepper.doSteps(-5000); // roughly half inch
Serial.println("Move twd end");
}
if (limitSwitch1.isPressed()) {
myStepper.stop();
delay(500);
myStepper.doSteps(0);
Serial.println("Switch 1 accidentally triggered");
backToZero = myStepper.currentPosition();
Serial.println(backToZero);
myStepper.doSteps(-backToZero);
}
if (limitSwitch2.isPressed()) {
myStepper.stop();
delay(500);
myStepper.doSteps(0);
Serial.println("Switch 2 accidentally triggered");
backToZero = myStepper.currentPosition();
Serial.println(backToZero);
myStepper.doSteps(-backToZero);
}
}
}