Tryinng to make an on/off switch

83 views
Skip to first unread message

Kings

unread,
May 16, 2024, 5:13:57 PMMay 16
to accelstepper
Hi, I haven't used arduino in a while but I'm working a project that requires the motor to accelerate to a certain speed hold the speed for an amount of time and then decelerate to zero. I think I've gotten that figured out. 
What I'm now working on is an on/off switch where when turned on the code will run, and if its turned off the motor will immediately decelerate and stop. I'm currently unsure how to make this.
I'm also having an issue with the motor stuttering during acceleration and deceleration.

Here's my current code.

#include <AccelStepper.h>

#define dirPin 5
#define stepPin 2

AccelStepper stepper(1, stepPin, dirPin);

void setup() {
  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(200);
  stepper.moveTo(8000);

}

void loop() {
  if (!stepper.run()) {  
    stepper.moveTo(-stepper.currentPosition());
  }
 
}

Jim Larson

unread,
May 16, 2024, 8:21:59 PMMay 16
to accelstepper
Hi Kings-

First, I tried your code unmodified and verified that it accelerates and decelerates smoothly. I'm using an a4988 driver and a NEMA 17 motor. The usual suspect for stuttering operation is improperly adjusted current on the driver  The easiest way to adjust the current is to use a non-metallic screwdriver and turn the adjustment screw while the motor is running. Either too much or too little current will cause the motor to misbehave. turn the screw until you hit the "sweet spot".

Second, to get on/off behavior, you probably want to use a state machine to control motor operation. Read your button every 1/10 to 1 second and modify the state variable as needed. I will try to put together an example in the next couple of days.

One question: you mention using a switch to stop the motor. Do you want another press of the switch to start it again? If so, what position should the motor now seek?

HTH
                -jim

Kings

unread,
May 16, 2024, 8:29:39 PMMay 16
to accelstepper
Hi, thank you so much for the quick response and being willing to help me!

I tried working on the potentiometer, I'm also using the a4988, but I imagine I may have gotten the value wrong, I'll definitely check that.
To answer your question I basically want to press the switch when the motor is off to just start up the code again, where the motor will accelerate run at a speed decelerate and once complete stop. With the button also being a manual stop to stop the cycle at any point.

gjgsm...@gmail.com

unread,
May 16, 2024, 9:48:24 PMMay 16
to accelstepper

Try attaching a button onto a spare pin with a pullup, and use an interrupt to detect a button press. The interrupt will change a variable to either TRUE/FALSE to trigger the Accelstepper 'stop()' function. You can toggle another variable to keep track of whether you are stopping or starting. You will need to poll 'accelstepper' for the current postion when stopping so that you can calculate the 'remaining steps to go' to reach your target when restarting with another button press.

I put my above answer and your code into ChatGPT4o and asked it to amend the code, and this is what it came up with. I didn't test it but it could be a good starting point. FWIW, this whole response took about 5 mins.

#include <AccelStepper.h>

#define dirPin 5
#define stepPin 2
#define buttonPin 3

volatile bool buttonPressed = false;
bool isRunning = true;

AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);  // Configure the button pin with an internal pullup
  attachInterrupt(digitalPinToInterrupt(buttonPin), handleButtonPress, FALLING);


  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(200);
  stepper.moveTo(8000);
}

void loop() {
  if (buttonPressed) {
    buttonPressed = false;
    if (isRunning) {
      stepper.stop();
      isRunning = false;
    } else {
      long remainingSteps = stepper.targetPosition() - stepper.currentPosition();
      stepper.moveTo(stepper.currentPosition() + remainingSteps);
      isRunning = true;
    }
  }

  if (isRunning) {
    stepper.run();
  }
}

void handleButtonPress() {
  buttonPressed = true;
}

Geoff

Jim Larson

unread,
May 17, 2024, 12:21:58 AMMay 17
to accelstepper
Geoff -
The ChatGPT code is cute, but probably won't work as written. Recall that stepper.stop() *does not* stop the motor. stepper.run() must continue to be called - hence the need for a state machine approach.

It's also worth noting that not every pin can be an interrupt pin. On the UNO, for example, only pins 2 and 3 can be interrupts (without additional libraries).

Finally, the button should have some debounce or strange behavior may result.

I don't think ChatGPT really solves the problem, but it is quick! Reminds me of the old sailor's saying: It's better to go slow in the right direction than to go fast in the wrong direction.

                -jim

gjgsm...@gmail.com

unread,
May 17, 2024, 4:15:06 AMMay 17
to accelstepper
Thanks Jim, the code was only intended as a guide, there are many ways to make this work, this is just one commonly used option. The revised code below should fix that issue regarding the stop() function, again, it's not tested. Further development is up to the user. Debouncing buttons is common practice and again up to the user how they do that along with their choice of MCU, but the UNO should work well with this example. Maybe we could have a beer one day and discuss the philosophies of problem solving. Geoff

#include <AccelStepper.h>

#define dirPin 5
#define stepPin 2
#define buttonPin 3

volatile bool buttonPressed = false;
bool isRunning = true;
bool stopping = false;


AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);  // Configure the button pin with an internal pullup
  attachInterrupt(digitalPinToInterrupt(buttonPin), handleButtonPress, FALLING);

  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(200);
  stepper.moveTo(8000);
}

void loop() {
  if (buttonPressed) {
    buttonPressed = false;
    if (isRunning) {
      stepper.stop();  // Request the motor to stop
      stopping = true; // Set the stopping flag
    } else {
      long remainingSteps = stepper.distanceToGo();  // Calculate remaining steps

      stepper.moveTo(stepper.currentPosition() + remainingSteps);
      isRunning = true;
    }
  }

  if (isRunning) {
    stepper.run();
  } else if (stopping) {
    stepper.run();  // Continue calling run() to decelerate to a stop
    if (stepper.isRunning() == false) {  // Check if the motor has stopped
      stopping = false; // Clear the stopping flag
      isRunning = false;

    }
  }
}

void handleButtonPress() {
  buttonPressed = true;
}

Ralph Hulslander

unread,
May 17, 2024, 11:38:14 AMMay 17
to accels...@googlegroups.com
ChatGPT and I have had arguments over it's code. It finally admitted I was correct and it's code would not work.

Ralph

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/accelstepper/a07e867c-eef3-455c-b386-b86def6fa327n%40googlegroups.com.

A. Craig West

unread,
May 17, 2024, 12:54:26 PMMay 17
to accels...@googlegroups.com
The problem with using ChatGPT for code, is that it is essentially a souped up version if Eliza. It mostly treats everything based on conversational structure. If you tell it that it is wrong, it will generally proceed as if that is true, whether or not that is so, and then make up a plausible sounding reason why it is wrong. It reminds me of Douglas Adam's fictional software Reason, which comes up with a convincingly plausible justification for a given conclusion....

Jim Larson

unread,
May 18, 2024, 12:43:01 AMMay 18
to accelstepper
Replying to Geoff:
Yes, I think we could have a rich discussion about problem solving! And beer always helps.
Someday.

Replying to the OP:
Here's some code that I've actually tested. Seems to work fine, but the debounce delays I mention in the comments may be necessary. I think I saw bad behavior one time that seemed to be due to bouncing.
BTW, I copied the idea of the interrupt on the button. My original inclination of reading buttons every second or less was a method I used with multiple buttons. Not needed here.

#include <AccelStepper.h>

#define dirPin 5
#define stepPin 2
#define buttonPin 3

//AccelStepper stepper(1, stepPin, dirPin);
AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);           // works for a4988 (Bipolar, constant current, step/direction driver)

// State definitions
#define RUN 01
#define RESTART 02
#define STOP_NOW 03
#define STOPPED 04
// State variable
volatile int state;   // must survive interrupts

volatile int enabFlag;  // controls the button interrupt

int dir;
#define FWD 1
#define REV -1

// define movement limits here
long int fwdLimit = 1000; // OP had 8000
long int revLimit = -1000;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);   // use pullup on interrupt
  // fix this to use digital pin number, not absolute
  attachInterrupt(digitalPinToInterrupt(buttonPin), buttonInt, FALLING);
  enabFlag = 1; // enable the sensor interrupt
  dir = FWD;    // tracks the direction of movement
  state = RUN;   // initial state is to run
  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(200);
  stepper.moveTo(fwdLimit);
}

void loop() {
  switch (state) {
    case RUN:
      // Check if the Stepper has reached desired position
      if ((stepper.distanceToGo() != 0)) {
        stepper.run();
      } else {  // reverse direction
        if (dir > 0) {
          stepper.moveTo(revLimit);
          dir = REV;
        } else {
          stepper.moveTo(fwdLimit);
          dir = FWD;
        }
      }
      break;
    case RESTART:  // come here if button is pressed to restart.
      if (dir >= FWD) {
        stepper.moveTo(fwdLimit);  // keep going in same direction
      } else {
        stepper.moveTo(revLimit);
      }
      enabFlag = 1; // rearm the interrupt  -- may need a delay here to debounce?
      state = RUN;
      break;
    case STOP_NOW:
      stepper.stop();
      enabFlag = 1; // rearm the interrupt  -- may need a delay here to debounce?
      state = STOPPED;
      break;
    case STOPPED:
      // Check if the Stepper has reached desired position
      if ((stepper.distanceToGo() != 0)) {
        stepper.run();
      }
      break;
   // should have a default!!
  }
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++ Interrupt service routine +++++++++++++++++++++++++++++
//  Come here if falling edge on D2.
//  If enable flag is true, change state
void buttonInt() {
  if (enabFlag == 1) {
    enabFlag = 0;
    if (state == RUN) {
      state = STOP_NOW;
    }
    else if (state == STOPPED) {
      state = RESTART;
    }
  }
}

Kings

unread,
May 18, 2024, 6:53:28 PMMay 18
to accelstepper
Hi I tried the code and the motor moves smoothly and perfectly. One issue I'm having is I have the button wired properly to pin 3 on the arduino, however I can't seem to get the button being pressed to do anything. I'm wondering if I need to fix how I've wired the button?
I'm using a Ulincos U19D1.

Jim Larson

unread,
May 18, 2024, 8:44:21 PMMay 18
to accelstepper
To test the functioning of your circuit, remove your button completely. Then connect a wire to D3 on the Arduino. The motor should run normally. Touch the other end of the wire briefly to ground. The motor should stop. Touch the wire briefly to ground again and the motor should start again.

If that works (or even partly works) it means your button is likely wired wrong. Check your circuit carefully and try again.

HTH.

                 -jim

Kings

unread,
May 18, 2024, 11:06:07 PMMay 18
to accelstepper
Hi,
I checked the wiring and figured out what was wrong, I've gotten the button working and tweaked the code to fulfil a couple wants. Everything is working perfectly and exactly as I needed.
Thanks to everyone especially Jim for the code!

Reply all
Reply to author
Forward
0 new messages