Multiple switches with single stepper motor

Hi, I'm a relatively novice coder (I teach kids so I can do lots of block programming, and I've dabbled a bit in arduino before for other various projects), but I am stumped by this problem. I'm trying to build an earthquake table that is run by a stepper motor. I've got all the hardware set up and have made the motor move, change speeds, bounce, and other simple things. However, what I actually want it to do is a bit more complicated than that. I have 3 arcade buttons, which correspond to the three speeds we want the motor to run. When button one is pushed, I want the motor to turn on at a slow speed for ten seconds, ignore all other input, and then turn off. With button two, do the same at a medium speed, and button three, do it at a fast speed.

The issues I am running into are:

  • delay does a great job blocking code, but the stepper needs to update every step so that doesn't work

  • If I use millis(), like in multiple things at once or Blink Without Delay, then the new input overrides the old input and it switches speeds if another button is pressed.

How do I insert blocking code for inputs while still allowing the motor to update, and the timer to count down?

I've included my code in case there is anything suggestions anyone has. Thanks in advance!
-Evan

#include <MultiStepper.h>
#include <AccelStepper.h>
// Define a stepper and the pins it will use
AccelStepper stepper(1,7,6); //stepper in mode 1, PUL- on pin 7, DIR- on pin 6
const int button1 = 2; //slow button
const int button2 = 3; //medium button
const int led1 = 13; //indicator for slow
const int led2 = 12; //indicator for medium
int button1State = 0;
int button2State = 0;
unsigned long previousButton1Millis = 0;
unsigned long previousButton2Millis = 0;
const long interval = 10000;


void setup() {
  // put your setup code here, to run once:
pinMode(button1, INPUT_PULLUP);
pinMode(button2, INPUT_PULLUP);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
stepper.setMaxSpeed(1000);
stepper.setAcceleration(500);


}

void loop() {
  // put your main code here, to run repeatedly:
unsigned long currentMillis = millis(); //
button1State = digitalRead(button1);
button2State = digitalRead(button2);
if ((button1State == LOW) && (button2State == HIGH)) {
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);
    stepper.setSpeed(200);
}   stepper.run();
  if (currentMillis - previousButton1Millis >= interval){
    previousButton1Millis = currentMillis;
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    stepper.stop();
  }
if ((button1State == HIGH) && (button2State == LOW)) {
    digitalWrite(led1, LOW);
    digitalWrite(led2, HIGH); 
    stepper.setSpeed(400);
    stepper.run(); 
}
  if (currentMillis - previousButton2Millis >= interval){
    previousButton2Millis = currentMillis;
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    stepper.stop();
  }
}

That is because you are not using it right. Have a variable that is set according to what motor is on. Only pay attention to buttons when this variable is zero. When a button is pressed and the value is zero set it to the button number and start the motor going.

When the time is up reset the variable back to zero.

Hi Evan,
do you want to learn how to use millis and AccelStepper, or do you want a simple solution for your project?
You could use my MobaTools Library, which is a toolbox to make many things ( e.g. steppers, buttons, timings... ) easier.
This could be a solution with MobaTools ( for three modes: slow, medium and fast ):

#include <MobaTools.h>

// Define a stepper and the pins it will use
const byte pulPin = 7;
const byte dirPin = 6;
MoToStepper stepper(200, STEPDIR);  // stepper with, PUL and DIR pin
MoToTimer stepTimer;                // timer for stepper runtime
const int runTime = 10000; // 10 sec runtime

const byte buttonPins[] = {2, 3, 4}; // Pins for slow/medium/fast
const byte nbrOfModes = sizeof( buttonPins ); // buttonPins must be declared as byte
MoToButtons buttons( buttonPins, nbrOfModes , 20, 1000 );    // manage 3 buttons with debouncing

const byte ledPins[nbrOfModes] = {13, 12, 11}; //indicator for slow,medium and fast
const int stepSpeeds[nbrOfModes] = { 200, 400, 600 };
byte speedMode = 0; // 0= slow ... 2 = fast

void setup() {
    stepper.attach( pulPin, dirPin );
    stepper.setSpeedSteps( 200 * 10, 50 ); // ramp up within 50 steps at speed 200 steps/sec (
    for ( byte  i=0; i< nbrOfModes; i++ ) pinMode( ledPins[i], OUTPUT );
}

void loop() {
    buttons.processButtons();

    if ( !stepper.moving() ) {
        // check buttons only if stepper is not moving
        for ( speedMode = 0; speedMode < nbrOfModes; speedMode++ ) {
            // check which button is pressed
            if ( buttons.pressed(speedMode) ) {
                stepper.setSpeedSteps( stepSpeeds[speedMode] * 10 ); // set stepper speed ( in steps/10sec )
                digitalWrite( ledPins[speedMode], HIGH );       // switch on led
                stepTimer.setTime( runTime );                   // set timer for stepper runTime
                stepper.rotate(1);                              // start turning
                break;                                          // finish for loop
            }
        }
    }

    if ( stepTimer.expired() ) {
        // end of runtime
        stepper.rotate(0);        // stop stepper
        digitalWrite( ledPins[speedMode], LOW );       // switch off led

    }

}

Nothing of this code is blocking, the stepper steps are created in the background ( with interrupts, but you don't have to bother with this ).
Franz-Peter

In such cases i have used tone() and noTone() functions, after calling them you can call delay. But if you need frequency below 32 Hz, then should do a cycle and manually update digital ports output with delays.

An even more simple variant with delay and based on @evanrock's first approach in #1 is possible too:

#include <MobaTools.h>
// Define a stepper and the pins it will use
const byte pulPin = 7;
const byte dirPin = 6;
MoToStepper stepper(200, STEPDIR);  // stepper with, PUL and DIR pin
const int button1 = 2; //slow button
const int button2 = 3; //medium button
const int led1 = 13; //indicator for slow
const int led2 = 12; //indicator for medium
int button1State = 0;
int button2State = 0;
const long interval = 10000;


void setup() {
    // put your setup code here, to run once:
    pinMode(button1, INPUT_PULLUP);
    pinMode(button2, INPUT_PULLUP);
    pinMode(led1, OUTPUT);
    pinMode(led2, OUTPUT);
    stepper.attach( pulPin, dirPin );
    stepper.setSpeedSteps( 200 * 10, 50 ); // ramp up within 50 steps at speed 200 steps/sec (


}

void runStepper( int speed, int time ) {
    // Run stepper with speed and time ( blocking );
    stepper.setSpeedSteps(speed * 10);
    stepper.rotate(1);    // start movement
    delay( time );
    stepper.rotate(0);    // stop movement

}
void loop() {
    // put your main code here, to run repeatedly:
    button1State = digitalRead(button1);
    button2State = digitalRead(button2);
    if ((button1State == LOW) && (button2State == HIGH)) {
        digitalWrite(led1, HIGH);
        digitalWrite(led2, LOW);
        runStepper(200, interval);
    }
    if ((button1State == HIGH) && (button2State == LOW)) {
        digitalWrite(led1, LOW);
        digitalWrite(led2, HIGH);
        runStepper( 400, interval );
    }
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
}

Thanks Franz-Peter! That's super helpful. I'll look over the documentation and give it a shot.

Following up on this, the code works beautifully with an Uno, but I switched everything over to a ProMini (which I think is 5v but I'm not 100% sure), and now it just runs the slow speed and light once for 10 seconds, then medium, then fast, and then ignores all input for some reason. I'm going to try it with a Nano to see if that makes a difference, but if anyone has any suggestions in the meantime, I'd love to hear them. TIA!

There are several proMini boards - 5V/16MHz and 3.3V 8Mhz. Depends on which you have.
The 5V board should behave just like an UNO. It's the same processor. The main difference to an ONO/Nano is only that there is no serial - USB converter on board.

Well. two anyway. :grin:

It is as well to mention that the 8 MHz board will work on 3.3 V as it uses the lesser clock crystal/ resonator but that in no way suggests it can only work on 3.3 V; it will of course work perfectly well on 5 V and you would generally use 5 V when programming it with a USB adapter module.

There is also the different regulator - the 3.3 V version has a 3.3 V regulator but since the regulator is basically quite useless. that is of no concern - you generally want to remove the regulator (and pilot LED) if you are going to operate it on batteries and use sleep mode.

Good nickname for a UNO. :rofl:

And you will get both of them with 328P or 168 processors ... :wink:

That's true, but in any way you have to select the 3,3V, 8MHz Version in the IDE. Otherwise the timing will not be correct.

So I will not correct the typo :joy:

Oh yes, I forgot those obsolete ones still existed! :astonished:

I mean does Atmel/ Microchip - or the fake factories - still produce ATmega168s? If so, just why? :confused:

You can still buy them with even less flash/RAM: ATmega88, ATmega48 ...

Yes but those processors have the advantage of a much smaller form factor than the 168.
Also the slight difference in cost is not important in the hobby world, but is vitally important for the commercial market.

I fact the hobby world is not at all significant to the semiconductor companies in terms of sales.

1 Like

Yes, that's the answer:

OK, so I would have to presume the lesser chips use significantly less silicon in order to make it actually viable to have separate production lines. :thinking:

No, it costs the same for all chips with the same size silicon area. What you might get with chips containing less memory is a better yield coming off the line but that would be marginal.

You have to remember that what a chip is sold for has little to with how much it costs to make, but is the price the manufacture thinks the market can sustain. This includes factors like the availability of replacement chips and how old the design is. However a OEM is generally reluctant to change the chip on a design because the device they are making will need to be requalified .

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