Using a millis delay in a millis

I knew this day would come and I'd have to find a way to do it. I can't nest a delay inside as delaying is affecting other tasks (it's hiccuping reading current). I proved this by disabling the delays and sure enough my current readings resumed as normal.

What I need help with is how to cause a delay like function without halting the loop.

The way I see it, if I try and nest another millis() style delay / timer, once the second if statement runs once, it resets the timer on the first.

I need to keep a temporary change to analogWrite on a pin for 200mS, multiple times in fact while the main millis() timer stops the cycle from running for 40 seconds.

Here's a snippet of what I'm trying to modify the delay()s out of:

if (millis() - timerD >= 40000) {
      if (analogueMeterPWM <= 127) {
        analogueMeterPWM = analogueMeterPWM + 127;
        analogWrite(analogueMeter, analogueMeterPWM);
        delay(200);
        analogueMeterPWM - 127;
      } else if (analogueMeterPWM > 127) {
        analogueMeterPWM = analogueMeterPWM - 127;
        analogWrite(analogueMeter, analogueMeterPWM);
        delay(200);
        analogueMeterPWM + 127;
      }
      timerD = millis();
    }

Thanks guys!

Here's my full sketch, as the sticky suggested I should post it. (the section I'm editing the delay()s on is for twitching the needle on an analogue volt meter, to show me the current reading being displayed (volts/amps/temperature scale).
Note one, two and three twitches respectively.
Also note I made the twitch go upwards if the needle is below halfway and downwards if above halfway.

had to attach it

Yep, plenty to tidy up and I noticed some conflicting terms I should change in my notes (I referenced the analogue meter display as a screen earlier in the program as I didn't think I should differentiate from LCD screens.) Plus notes missing of course.

_2020-06-24_Arduino_core.ino (11.4 KB)

Just as you have timerD, have another variable that keeps track of the time for the other event(s) that you are waiting for. Then when that is expired set TimerD to the new millis() so you can wait for the other 40 seconds.

Alternatively, instead of hard coding 40000 milliseconds, set that value to be variable and you can set the target based on what you want to achieve.

Are you trying to delay the code in the if by 40s, or by 200ms? Which is it?

As you’ve only present a code snippet, your delays inside your delay don’t actually do anything other than delay when you reset your variable. So you could just remove them and have no effect on your code as presented. There must be some other code we are unaware of....

You also don’t seem to know that you can do arithmetic on parameters when passing them in, such that you do not effect the source variable...

if (millis() - timerD >= 40000) {
      if (analogueMeterPWM <= 127) {
        analogWrite(analogueMeter, analogueMeterPWM + 127);
      } else if (analogueMeterPWM > 127) {
        analogWrite(analogueMeter, analogueMeterPWM - 127);
     }
      timerD = millis();
    }

Or is it that you require a 40s delay for the FIRST time, and a 200ms delay thereafter?

In which case...

  // somewhere else in your code, e.g. setup, where you want the initial delay to be 4000ms... 
  timerD_delay = 4000;
  .....
  if (millis() - timerD >= timerD_delay) {
      if (analogueMeterPWM <= 127) {
        analogWrite(analogueMeter, analogueMeterPWM + 127);
      } else if (analogueMeterPWM > 127) {
        analogWrite(analogueMeter, analogueMeterPWM - 127);
      }
      timerD = millis();
      timerD_delay = 200;
    }

Also your presented “else” clause...

} else if (analogueMeterPWM > 127) {

Is pointless after...

if (analogueMeterPWM <= 127) {

You might as well just have written...

} else {

...and not bothered checking the condition (if the variable is not less than or equal to 127 it must be greater than 127).

Edit: PS I’d look at your full sketch to find out more details, but you have attached it and not in-line posted it. Because I’m on a Apple mobile device that means I can download it, but not actually open it because Apple don’t recognise INO as a text file format. Now I admit that’s rather deficient of them, but it is an impediment to me correctly diagnosing your problem and why the posting guidelines requires you do not attach sketches.

Edit again: From your description it seems you want to do the different delay multiple times and then go back to the 40s delay. You probably want to read up on the “state machines” tutorials in the sticky.

You need to keep track of which “step” of the “multiple times” you are on, from which you can work out the delay for the next step. e.g. ...

if (step < 3) {
  step++;
  timerD_delay = 200;
} else {
  timerD_delay = 40000;
}

marco_c:
Just as you have timerD, have another variable that keeps track of the time for the other event(s) that you are waiting for. Then when that is expired set TimerD to the new millis() so you can wait for the other 40 seconds.

If I get what you mean, I'm already doing so with the other timers in the series (A to I)

marco_c:
Alternatively, instead of hard coding 40000 milliseconds, set that value to be variable and you can set the target based on what you want to achieve.

I don't really see how that helps, but could you give an example, pseudo or otherwise?

Thanks for your help non-the-less!

pcbbc:
Are you trying to delay the code in the if by 40s, or by 200ms? Which is it?

I want to delay everything under the first if by 40s, but once 40 seconds has passed I want the analogWrite to the pin modified and held for 200mS, then revert back. Then the program will wait another 40s to do the 200ms task again.

pcbbc:
As you’ve only present a code snippet

yeah sorry apparently there's a character limit on posts, so I tried to break it in two but I still had to attach the .ino to the second reply/post anyway.
[/quote]

pcbbc:
your delays inside your delay don’t actually do anything other than delay when you reset your variable. So you could just remove them and have no effect on your code as presented. There must be some other code we are unaware of....

I couldn't tell from how I read it if there was enough in that snippet to show the analogWrite changing for 200mS then reverting back or not. But it does work and my meter needle does twitch after 40s of waiting.

pcbbc:
You also don’t seem to know that you can do arithmetic on parameters when passing them in, such that you do not effect the source variable...

if (millis() - timerD >= 40000) {

if (analogueMeterPWM <= 127) {
        analogWrite(analogueMeter, analogueMeterPWM + 127);
      } else if (analogueMeterPWM > 127) {
        analogWrite(analogueMeter, analogueMeterPWM - 127);
    }
      timerD = millis();
    }

Cool! I haven't seen this before, thanks, I'll definitely use that again!

pcbbc:
Or is it that you require a 40s delay for the FIRST time, and a 200ms delay thereafter?

Do nothing for 40s, then charge the analogWrite by 127 and hold that change for 200mS to make the needle twitch then revert back to the original analogWrite value.

pcbbc:
In which case...

  // somewhere else in your code, e.g. setup, where you want the initial delay to be 4000ms... 

timerD_delay = 4000;
  .....
  if (millis() - timerD >= timerD_delay) {
      if (analogueMeterPWM <= 127) {
        analogWrite(analogueMeter, analogueMeterPWM + 127);
      } else if (analogueMeterPWM > 127) {
        analogWrite(analogueMeter, analogueMeterPWM - 127);
      }
      timerD = millis();
      timerD_delay = 200;
    }

That definitely looked promising, but it doesn't seem to do what my analogue voltmeter needs. The needle did nothing this time. It looks like reverting change doesn't happen and so the analogueMeterPWM value re-enters the _if_s with a different value. Interestingly the meter just sat still at the correct mapped analogue source.

Thanks for taking the time to answer, I appreciate it! I know I'm a bit sloppy, but I'm making progress. Just FYI I'm not a programmer, so I shouldn't be irritating any professional programmers :wink:

pcbbc:
Also your presented “else” clause...

} else if (analogueMeterPWM > 127) {

Is pointless after...

if (analogueMeterPWM <= 127) {

You might as well just have written...

} else {

...and not bothered checking the condition (if the variable is not less than or equal to 127 it must be greater than 127).

Thanks. I was being sloppy there leaving it as a quick reminder this is a split. I should do it properly and remember it properly the first time. It won't be the last crappy but working piece in my program heh.

pcbbc:
..why the posting guidelines requires you do not attach sketches.

Sorry about that, I kept getting knocked back on too many characters for the post. So I tried splitting my posts but even that was still over limit.

pcbbc:
Edit again: From your description it seems you want to do the different delay multiple times and then go back to the 40s delay. You probably want to read up on the “state machines” tutorials in the sticky.

You need to keep track of which “step” of the “multiple times” you are on, from which you can work out the delay for the next step. e.g. ...

if (step < 3) {

step++;
  timerD_delay = 200;
} else {
  timerD_delay = 40000;
}

That sounds exactly like what I need. Hopefully I can adapt it to do multiple 200ms twitches to represent which display readout I'm looking at, which the delay()s current do for me.

Many thanks again!

HI,

first of all I try to describe what your code-version posted in the first post tries to achieve:

every 40 seconds there should happen

if (analogueMeterPWM <= 127)

add 127 to the variable analogueMeterPWM

keep value of variable analogueMeterPWM for 200 milliseconds
after 200 milliseconds
reduce value of analogueMeterPWM to value before

if (analogueMeterPWM >= 127)

do the opposite: substract 127 from value of variable analogueMeterPWM
keep new value for 200 milliseconds
after 200 milliseconds increase value of analogueMeterPWM to value before

example with numbers:

39,999 seconds: analogueMeterPWM has value 20

40,000 seconds: analogueMeterPWM is set to 147

40,001 seconds: analogueMeterPWM is set to 147
40,002 seconds: analogueMeterPWM is set to 147
...
40,199 seconds: analogueMeterPWM is set to 147
40,200 second: analogueMeterPWM is set to 20
...
80,200 second: analogueMeterPWM is set to 20
80,200 seconds: analogueMeterPWM is set to 147

80,201 seconds: analogueMeterPWM is set to 147
80,202 seconds: analogueMeterPWM is set to 147
...
80,399 seconds: analogueMeterPWM is set to 147
80,400 seconds: analogueMeterPWM is set to 20

that's what you seem to try to code

Now if your code should not be blocked from running but instead should run through its loop
even with the value of analogueMeterPWM beeing increased/decreased temporarily for 200 milliseconds

this can be coded by additional timers and boolean-flag variables.
instead of calling a blocking delay() you set a boolean flag
I will call this variable "Change_analogueMeterPWM"

// define as global variables
boolean Change_analogueMeterPWM = false;
unsigned long analogueMeterPWM_Timer;


  if (millis() - timerD >= 40000) {  // execute the code below every 40 seconds
                                    // "!" is the logical not-operator
    if (  (analogueMeterPWM <= 127) && (!Change_analogueMeterPWM) { // if increase-pulse is not yet started
      Change_analogueMeterPWM = true;                               // set flag that indicates increase-pulse HAS start
      analogueMeterPWM_Timer = millis();                            // setup timer-variable for 200msec-"pulse"
      analogueMeterPWM = analogueMeterPWM + 127;                    // increase value
      analogWrite(analogueMeter, analogueMeterPWM);  
    }
  
    if (Change_analogueMeterPWM) {  // if increase-pulse HAS started
      if ( analogueMeterPWM_Timer - millis() >= 200) { // keep track of time passed by and if 200 milliseconds are over
        Change_analogueMeterPWM = false;               // set flag back to false
        analogueMeterPWM = analogueMeterPWM - 127;     // decrease value again 
        analogWrite(analogueMeter, analogueMeterPWM);        
        timerD = millis();                             // update TimerD to start new 40-seconds cycle
      }
    }
  }

by the way you should rename your timers TimerA, TimerB, TimerC etc. with selfexplaining names.
Each timer-variable has a certain purpose and the name of the variable should bring that purpose to the point.

Through the appendix "_timer" you still can indicate it is a variable for timing.

like my example "analogueMeterPWM_Timer"

I developed the habit of thinking 2 to 5 minutes about a meaningful and selfexplaining name for each variable.
You might think you don't want to invest that time. I made the experience that it saved me a lot of time later
as I don't have to jump in the code up and down "what was TimerD? used for???
If the name is selfexplaining you just read the code right where you are and your done.

best regards Stefan

For multiples 200msec-twitches you would have to add a timer for TwitchPause which does the timing for another 200 milliseconds to make the needle swing-back

and
add a counter and an if-condition

if (TwitchCounter >= NumberOfTwitchesToShow) {
TwitchCounter = 0;
TimerD = millis(); // moved from the other place shown in the code above
}

at this point a statemachine becomes more convenient than adding more and more boolean variables.

Again: If you use a statemachine give the values that are assigned to the state-variable meaningful and selfexplaining names.

Like "StartTwitching", "TwitchNeedleON" , "TwitchNeedleOFF" or if you would like to distinguish between
Twitching means increase/decrease value

"TwitchNeedleUP", "TwitchPause" , "TwitchNeedleDown"

For multiple twitches you need a pause of at least 100 milliseconds to give the needle time to swing back so you can twitch UO/Down again

I don't know what your complete application is. Maybe there is no other way than twitching the needle.
If you have the opportunity to add a small dotmatrix-display or a 7-segment-display I would prefer to use that over counting small twitches of a analog-meter-needle.

best regards Stefan