Go Down

Topic: Change request example code "Blink Without Delay" (Read 1 time) previous topic - next topic

DRose

The example code for "Blink Without Delay" will show a glitch in time over the blink itereations. This happens becaus there is some calculation time between the two subsequent millis()-calls. That means between
  <code> if (millis() - previousMillis > interval) </code>
and
  <code> previousMillis = millis();  </code>

I suggest to change example from
  <code> previousMillis = millis();  </code>
to
  <code> previousMillis = previousMillis+interval;  </code>

That gives us a constant time distance for each comparison and uses only one millis() call. That may be important if some wants to play with the interval values or needs a somewhat synchronised blink frequency.

tigoe

I'll try it when I've got a board in front of me next. Seems like a reasonable change, as long as the time between checks is never significantly larger than the interval, for any reason.

PaulS

Another solution is to store the current time, and use that value throughout the block:

Code: [Select]
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval)


Code: [Select]
previousMillis = currentMillis;

This way, it doesn't matter how long the action part of the loop is. The recorded time is the start of the block, if that's what you want.

tigoe

I like that last solution even better, since it is consistent with what we do in the state change detection example. I'll put that in the SVn for the next version, thanks.

ShawnT

I just got my Arduino and loaded up the sketch for the first time, so it's possible I may be missing something, but the comments about  interval needing to be a long variable are wrong aren't they?
Quote

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

[glow]// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)[/glow]


the comment would apply to the previous variable (previousMillis)
but as writen interval might as well be a constant no?

DRose

@PaulS : No that will not do it. Becaus your sugesstion has a kind of a jitter of reading millis() depending on loop execution time.

My solutions provides an absolut "correct" line of trigger point for time comparison:

For ilustration example some asumptions befor entering the loop :
   interval =1000 (msec)
   first previousMillis= 1300 (msec)

Then two cases

1) my code delivers a line of previousMillis like that : 1300, 2300, 3300, 4300, 5300, ..... = 1300 + n * 1000.

2) your example might show something like this for previousMillis : 1300, 2301, 3305, 4307, 5311, .....  = 1300 + n *(1000+jitter)
The jitter error will accumulate over the loops.
The reason while we have that jitter is, that we never have
(currentMillis - previousMillis) == interval !!! Only in that case, we have the timeline like example 1.

You can test your code by replacing the if block with (havn´t compiled it by my own) to show the jitter
Code: [Select]

unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
Serial.println (currentMillis - previousMillis - interval);
}
This should show a row of zeros, if the code is fine.

Well the error is quite small, but look what will happen, if the interval is set to interval = 20 ? The we have a much bigger error accumulation during the loops. If we try to build relieable controler software, we have to take care for predicticality.
Ditmar

Coding Badly

Quote
Because your sugesstion has a kind of a jitter of reading millis() depending on loop execution time.

The Blink Without Delay code does have "jitter" (I prefer to call it "drift") but the change suggested by PaulS eliminates it.

Quote
This should show a row of zeros, if the code is fine.

I have the PaulS code running now and it returns a row of zeros...

Code: [Select]
const unsigned long interval = 1000;

unsigned long previousMillis;
unsigned long delta;


void setup( void )
{
 Serial.begin( 9600 );
 pinMode( 6, OUTPUT );
 digitalWrite( 6, LOW );
 delay( 3000 );
 digitalWrite( 6, HIGH );
 previousMillis = millis();
}

void loop( void )
{
 unsigned long currentMillis = millis();
 
 if( currentMillis - previousMillis >= interval )
 {
   delta = currentMillis - previousMillis - interval;

   if ( delta != 0 )
   {
     digitalWrite( 6, LOW );
     Serial.println( delta );
   }
   previousMillis = currentMillis;
 }
 
 if ( Serial.available() )
 {
   while ( Serial.available() )
   {
     Serial.read();
   }
   digitalWrite( 6, HIGH );
 }
}


The difference between your code and the code suggested by PaulS is how overruns are handled.  Your code will run twice in quick succession (number of runs over a given time span is preserved).  The code from PaulS skips overruns (delay between runs is preserved).  There is nothing wrong with either version.  It's simply a matter of choosing which is appropriate for the application.

Go Up