Trying to implement millis concept to replace a delay function

Hi all,

I am writing a simple code to monitor analog pin A3 and write digital pin D8 accordingly. Below is a summary of my code in a table form.

Condition Action
if A3 >1000 Write D8 HIGH
else if A3 < 1000 & D8 = HIGH Delay for 10 seconds & read A3 again
if A3 >1000 Keep D8 HIGH
else if A3 <1000 Write D8 LOW
else Write D8 LOW

I read about the millis() concept and tried to implement it for my application. However, I am not able to capture(lock) the millis() value at which CONDITION:A3 < 1000 & D8 = HIGH is true. Below is the code using millis(), if I am able to capture the millis()='triggerTime' each time the above condition is true, that would solve my issue.

Am I missing something in my code? Does anyone have an idea how to achieve this?

Thank you.

int PSON = A3; // 5V PSON to analog pin 3
int SSR = 8; // 5V from digital pin 8
int psState = 0; // PSON status
int ssState = 0; // SSR status

const unsigned long delayTime = 10000; // delay

void setup() {
  pinMode(PSON, INPUT); // analog pin 3 as input
  pinMode(SSR, OUTPUT); // digital pin 8 as output
}

void loop() {
  psState = analogRead(PSON); // read analog input status
  ssState = digitalRead(SSR); // read digital output status

  if (psState > 1000) {
    digitalWrite(SSR, HIGH); // sets digital pin 8 HIGH
  }

  else if (psState < 1000 && ssState == HIGH) {

    unsigned long triggerTime = millis();

    if (triggerTime != 0) {

      unsigned long currentTime = millis();

      if (currentTime - triggerTime >= delayTime) {

        psState = analogRead(PSON); // read analog input status

        if (psState > 1000) {
          digitalWrite(SSR, HIGH); // sets digital output HIGH
        }

        else if (psState < 1000) {
          digitalWrite(SSR, LOW); // sets digital output LOW
        }
      }
    }
  }

  else {
    digitalWrite(SSR, LOW); // sets digital output LOW
  }

}

It is not clear what you really want to do. If you want a delay, just use the delay() function, keeping in mind that delay() is blocking.

The reason to schedule events using millis() is to avoid a blocking delay. The processor can do other things until the interval expires.

triggerTime will never be zero. Except maybe once at reset.

currentTime will rarely be different to triggerTime, and if it different, only by 1.

So your logic is… illogical. :expressionless:

a7

@jremington I am aware that delay() is blocking. Hence, why I am trying to replace the delay() by the millis() concept to avoid a blocking code.

Like you have highlighted, in the future I might want to have other functions going on while the interval is ticking.

Do you have any idea of how I could implement millis() for my application?

Thank you.

@alto777 what you have said, is the exact behaviour of the code.

The part that you have quoted was just a trial to capture the triggerTime. I thought "if (triggerTime != 0)" would make the code move forward and lock triggerTime to X value. Hence, currentTime would keep running.

This is how I was looking at it, do you have any idea how I could work around this?

Thank you.

But you go on to use that temporary variable

I'm not at the big rig, so for now I see that that if condition will never be true. Since currentTime and currentTime will never differ by more than 1.

Typically in this spot, we exploit a variable declared at the beginning of the fuction (so it has scope for the entire body of the function) like

  static unsigned long previuosTime;

which we set when we do the thing we want to happen periodically, then next time around compare now (the freshly called millis(), that is to say currentTime) to that previuosTime we stored last time we did the thing. To see if delayTime has yet elapsed.

It's idiomatic and can be seen in virtually all uses of millis() that are intended to eliminate delay() calls.

You are close. I think the "blink without delay" uses that model, and "doing two things at once" also.

Sry I don't have the URLs at my fingertips. But google

Blink without delay Arduino

And/or

Doing two things at once Arduino

And/or they may be in the examples in the IDE.

I'll drop by later. I have no life, but there is eating, Netflix and sleeping...

a7

No, because you have not described the logic in a way that using millis() makes sense.

This is probably the best explanation of how to schedule events using millis(): https://www.baldengineer.com/blink-without-delay-explained.html

@alto777 I have read through both "blink without delay" and "doing two things at once", these examples are for repetitive timed events. The moment the board is powered on, it starts ticking.

In my situation it is a timed event nested in an if statement, and the if statement can be true at any given time. One thing I am sure, the board will not be powered long enough for millis() to overflow.

Thank you for the two references.

@jremington Thank you for the link, I will read through it and try other avenues.

I will try an explain where the delay() fits, if A3 <1000 and D8 is HIGH it should delay for 10 seconds and then read A3 again for any change.

How I understood the millis() concept, capture the millis() at which 'A3 <1000 and D8 is HIGH' and store it as "triggerTime".
From there have a new variable that keeps track of millis(), naming it as "currentTime".
Then if (currentTime - triggerTime >= delayTime), read A3 and write D8 accordingly.

I hope this gives you an idea of what I am trying to achieve. Please let me know if anything is not clear.

Thank you

How does that differ from using delay()?

What will the processor do while the interval has not yet expired?

You will be stuck until you come up with some alternative to nested if statements. Either formulate the problem in a completely different way, or just use delay() and get on with your life.

It sounds like what you want is to capture millis() each time the condition becomes true.  This is a critical distinction.  Read -> IDE/file/examples/digital/statechangedetection

Sry, I did not absorb and have in mind your requirements as you stated in the first place. Although perhaps a waste of your time just now, I can practically guarantee you that soona later anything you discovered or learned will be useful. I hope. Again I apologize for diving into the code so close I didn’t even see what you were trying to do, just how you were doing what I thought incorrect. ly.

Your description is a bit hard to understand, maybe more scrutiny of your attempt will clear it up.

But.

I think you should draw a flowchart of the logic. Where there are not multiple tests. I’ll try later.

Flowcharts are old fashioned but still have their place.

a7

OK I tried to make a flowchart from your multiple line table if/else description and also from examining your code.

And failed.

Could you just describe in plain English what you are trying to accomplish? I have a funny feeling you are making this way too complicated.

Like:

If the value is over 1000, set D8.

If the value is less than 1000, and ten seconds have gone by on a high D8, clear D8.

Try this demo. The pushbutton stands for an analog read over 1000.

I set the time to 2 seconds as life is too short to wait 10 seconds… as long as you keep stabbing the button, the LED stays on. Ans stays on for two additional seconds after the condition clears (you stop stabbing the button).

magicLED is called very very frequently, it uses millis() to only do anything every 100 ms.

a7

Your making this too complicated. All you need is to know when to turn it on and when to turn it off.

Condition Action
if A3 >1000 Write D8 HIGH & Start Timer
else if A3 < 1000 & D8 == HIGH Check Timer- if 10 sec. has passed then Write D8 LOW

The timer will continuously restart as long as A3 is >1000 and if D8 is LOW there is no need to do anything until the next time A3 is >1000.

EDIT: And as mentioned above triggerTime needs to be global. Also currentTime needs to be updated in loop() outside of any conditional.

Is this Timig-chart a correct example of the timely relations between A3 and D8?

This is the way how I understand the behaviour that you want.

It took me 3 minutes to draw this. It is rough, freehand and sketchy but it show all relevant information and the timepoint where D8 changes in time is easy to identify. Even that my drawing has a tolerance of 1 mm = 20%

Please confirm or correct if this is the behaviour that you want to have

best regards Stefan

@StefanL38 using your chart as an example, I have drawn another set of timing chart for my application.



I have no issue achieving this with a simple code using delay() function. Please find below the current code I am using.

int PSON = A3; // 5V PSON to analog pin 3
int SSR = 8; // 5V from digital pin 8
int psState = 0; // PSON status
int ssState = 0; // SSR status

void setup() {
  pinMode(PSON, INPUT); // analog pin 3 as input
  pinMode(SSR, OUTPUT); // digital pin 8 as output
}

void loop() {
  psState = analogRead(PSON); // read analog input status
  ssState = digitalRead(SSR); // read digital output status

  if (psState > 1000) {
    digitalWrite(SSR, HIGH); // sets digital pin 8 HIGH
  }

  else if (psState < 1000 && ssState == HIGH) {

    delay(10000); // wait for 10 seconds
    psState = analogRead(PSON); // read analog input status

    if (psState > 1000) {
      digitalWrite(SSR, HIGH); // sets digital output HIGH
    }

    else if (psState < 1000) {
      digitalWrite(SSR, LOW); // sets digital output LOW
    }
  }

  else {
    digitalWrite(SSR, LOW); // sets digital output LOW
  }

}

Below is the delay() function I am trying to replace with millis() concept.

else if (psState < 1000 && ssState == HIGH) {

delay(10000); // wait for 10 seconds
psState = analogRead(PSON); // read analog input status

I hope this gives you a clearer visual explanation of my code.
Thank you.

@alto777 Please take a look at the above post, I included a timing chart for A3 vs D8.

Thank you.

@jremington Thank you again for this link Blink without delay() explained line-by-line . While reading through I came across Delayed actions with millis(), I will try this implementation.

Thank you.

Condition 2.2 is not necessary because if A3 goes above 1000 again while the "delayed" time is still active the timer resets as per condition 1 and will produce identical output.

In the old 7400 TTL hardware days - before microprocessors.., that would have been called a retriggerable one-shot.

Retriggerable, because ongoing pulses within the time constant would restart the output pulse timing without interrupting the output.

74LS123 retriggerable one shot.
(See item 7 on page 4)