Using Millis to delay an event start issues.

Hello,
I’m having difficulty with the millis function to delay the start of an event.
In this case I want to drive an output LOW after an input has been pulled HIGH but the input will remain HIGH the entire cycle.

I have managed to somewhat make it work but I think the “button check” portion of the code is causing my trouble. The code only executes after the button has been held down for a period of time and then released.

Any suggestions?

Thanks in advance!

const byte SW1 = 3; //cycle start switch
int SW1State = 0;         // current state of the button SW1
int lastSW1State = 0;     // previous state of the button SW1
unsigned long SW1PushedMillis; // when button (SW1) was switched
unsigned long stepperTurnedOnAt; // when STEPPER was turned on
unsigned long turnOnDelay = 3000; // turn on STEPPER after this time
bool stepperReady = false; // flag for when button SW1 is let go
bool stepperState = false; // for STEPPER is on or not.
#include <AccelStepper.h>

AccelStepper stepper(AccelStepper::DRIVER, 9, 8);

void setup() {
  pinMode(8, OUTPUT);//stepper driver direction control
  digitalWrite(8, LOW);//output to control direction of motor
  pinMode(9, OUTPUT);//driver output to stepper driver
  digitalWrite(9, LOW);//output to drive stepper controller
  pinMode(SW1, INPUT);//cycle start switch
  pinMode(11, OUTPUT); // enable pin on driver
  digitalWrite(11, HIGH); // disables driver output when high

  stepper.setMaxSpeed(1000);
  stepper.setSpeed(200);

}
void loop() {

  stepper.runSpeed();

  // get the time at the start of this loop()
  unsigned long currentMillis = millis();

  // read the pushbutton input pin:
  SW1State = digitalRead(SW1);

  // compare the buttonState to its previous state
  if (SW1State != lastSW1State) {
    // if the state has changed
    if (SW1State == HIGH) {
      // update the time when button was pushed
      SW1PushedMillis = currentMillis;
      stepperReady = true;
    }
    if (stepperReady) {


      if ((unsigned long)(currentMillis - SW1PushedMillis) >= turnOnDelay) {
        digitalWrite(11, LOW);
        // setup our next "state"
        stepperState = true;
        // save when the STEPPER turned on
        stepperTurnedOnAt = currentMillis;
        // wait for next button press
        stepperReady = false;
      }


      // save the current state as the last state, for next time through the loop
      lastSW1State = SW1State;
    }
  }
}

Hi,

I don't think you want this:

    if (stepperReady) {

inside of this:

 if (SW1State != lastSW1State) {

Yours,
TonyWilk

LesPaul:
In this case I want to drive an output LOW after an input has been pulled HIGH but the input will remain HIGH the entire cycle.

I can't relate that to your code and I can't figure what is the purpose of your code.

To implement what I have quoted above, all you need is (pseudo code)

if (input == HIGH) {
    output = LOW;
}

My guess is that you have not identified your requirement sufficiently clearly. When I get stuck I write things down (on a piece of paper or on a text editor) so I can see the whole requirement clearly.

...R

Robin2:
I can't relate that to your code and I can't figure what is the purpose of your code.

To implement what I have quoted above, all you need is (pseudo code)

if (input == HIGH) {

output = LOW;
}




My guess is that you have not identified your requirement sufficiently clearly. When I get stuck I write things down (on a piece of paper or on a text editor) so I can see the whole requirement clearly.


...R

Robin, the purpose of my code is to drive pin 11 low 3 seconds after an input has been made, the input will remain on the entire cycle. Sorry if I didn't explain it well enough to begin with.

the purpose of my code is to drive pin 11 low 3 seconds after an input has been made, the input will remain on the entire cycle. Sorry if I didn't explain it well enough to begin with.

You still haven't. What will cause pin 11 to go (back to) HIGH?

Detecting when pin goes HIGH (or LOW) ("the input") is trivial. That is the purpose of the state change detection example.

Recording when that happened is trivial. That's what the millis() function is for.

Independently, determining that now (millis() again) minus then (when the input was made) exceeds, or does not exceed, some interval is trivial. Taking the appropriate action - setting pin 11 LOW - is easy. Setting pin 11 LOW when it is already LOW doesn't hurt a thing. On the other hand, keeping track of its state is easy, so setting it LOW when it is already LOW isn't necessary.

But, when should it go HIGH again? 3 seconds after the input ends? A week from next Tuesday? Never?

I really can’t see what you are arguing about.

It seems perfectly simple. The problem with the code is what I hinted at in post #1

Currently, this:

  if (stepperReady) {
      if ((unsigned long)(currentMillis - SW1PushedMillis) >= turnOnDelay) {
        digitalWrite(11, LOW);
        // setup our next "state"
        stepperState = true;
        // save when the STEPPER turned on
        stepperTurnedOnAt = currentMillis;
        // wait for next button press
        stepperReady = false;
      }
   }

is only executed when the switch changes state… you want it outside of the if (SW1State != lastSW1State) { … }
so it executes when the ‘stepperReady’ flag has been set.

Yours,
TonyWilk

LesPaul:
Robin, the purpose of my code is to drive pin 11 low 3 seconds after an input has been made, the input will remain on the entire cycle. Sorry if I didn't explain it well enough to begin with.

Ahhh - that is much clearer - why didn't you say so in your Original Post

Is it necessary for the input to stay HIGH throughout the 3 seconds or will a momentary HIGH be sufficient.

...R

First, I want to thank everyone for their feedback.
Please understand that I have only been tinkering with programming for a couple of months.

Tony, Yes you are correct the code only executes after the state changes from LOW to HIGH and back to LOW and the SW1 must be held HIGH for at least the duration of the turnONdelay for that to work. SW1 will be held HIGH the entire event.

Paul, I'm trying to learn one thing at the time. At some point in the future I will want OUTPUT 11 to be driven HIGH once again with another input from a IR photo eye. IT does not have to be a timed event meaning, it may take an hour for the cycle to complete. I just want to be able to drive it high when the IR sensor is met.

Robin, Yes due to the nature of this test the input will have no choice but to be HIGH the entire event.
This input will be handled through an Omron roller switch on a feed chute, when the chute is closed for loading of the hopper will the input change from HIGH to LOW.

Thank you once again for being patient with an old man.
KE4CVD

LesPaul:
Robin, Yes due to the nature of this test the input will have no choice but to be HIGH the entire event.

That does not actually answer my question. The code will need to be different if there must be a test for a continuous HIGH compared to the situation where a momentary high is sufficient.

Thank you once again for being patient with an old man.

You are lot younger than I am, so less of the "old", thank you. :slight_smile:

...R

Robin2:
That does not actually answer my question.

There will be no further "tests".
It will be high the entire time. There is no option for it to be a momentary input.

LesPaul:
There will be no further "tests".
It will be high the entire time. There is no option for it to be a momentary input.

In that case the code can be written as if it were a momentary test - which makes things simpler. Something like this pseudo code

if (timerStarted == false) {
   if (input == HIGH) {
      timerStarted = true;
      timerStartMillis = millis();
   }
}
else {
    if (millis() - timerStartMillis >= 3000) {
       output = HIGH;
    }
}

...R