Go Down

Topic: Demonstration code for several things at the same time (Read 223117 times)previous topic - next topic

el_supremo

#15
Mar 10, 2014, 11:10 pm
Hey, no, don't give up on switches!
'if' statements can go haywire too if you forget an 'else'!

Pete
Don't send me technical questions via Private Message.

Robin2

#16
Mar 10, 2014, 11:55 pm

I think I now see the point you are making. I wrote a couple of spreadsheets to experiment with numbers.

I then set out to modify my sketch but I've run into a very strange problem. All of the functions work fine your way ( += interval) EXCEPT the servo function. After a short period it goes haywire, yet it works perfectly using " = currentMillis".

I think the problem may be due to the much shorter interval (or may be showing up sooner because of the shorter interval). And I think the problem arises because at some stage prevMillis exceeds currentMillis so that currentMillis - prevMillis gives a "negative" number or, since it is an unsigned long, a very large positive number which always satisfies the if statement.

I've had enough for today. I will experiment more tomorrow.

I suspect that the error associated with doing it the "wrong" way (i.e. "= currentMillis") depends on the ratio between the interval between successive values of millis() and the size of interval for the blinks (or whatever). If the blink interval is relatively large the error may not matter. I haven't explored this with my spreadsheet yet.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PeterH

#17
Mar 11, 2014, 12:16 am

I suspect that the error associated with doing it the "wrong" way (i.e. "= currentMillis") depends on the ratio between the interval between successive values of millis() and the size of interval for the blinks (or whatever). If the blink interval is relatively large the error may not matter. I haven't explored this with my spreadsheet yet.

The basic problem is that the code may not evaluate the condition at the precise instant that the interval has elapsed. Suppose for the sake of argument that you were trying to take some action every 100ms and your loop took 5ms to execute. The original code would take up to 5ms to notice that the interval had elapsed. Since the next interval is calculated based on the value of millis() at the point where we notice the interval elapsed, this means the next interval will be delayed by up to 5ms too. In effect, any latency noticing that the condition is true will cause all subsequent processing to slip.

The preferred approach always schedules the next event based on when the current event became due, not when the code noticed that it had occurred. This avoids slippage when there is any latency noticing the event.

Robin2

#18
Mar 11, 2014, 08:34 am
I eventually figured out the problem while lying in bed.

The problem arises in the servo function because I change the interval after every sweep. As initially written "prevMillis = currentMillis" is at the bottom of the function after the interval has been changed. And when the revised version "prevMillis += interval" is used at the same location it erroneously adds the revised interval. When there is a switch from a short to a long interval that means the prevMillis is actually greater than the next version of currentMillis which gives a "negative" answer to the subtraction (actually a large +ve number) which always evaluates to true.

The solution to that problem is to move "prevMillis += interval" to a position before the interval is changed.

HOWEVER .... (and I'm delighted that this problem gave me time to look at the bigger picture)

FIRST ... @CodingBadly said in Reply #11 that I was assuming that delta (the difference between successive values of millis()) was 1. Of course, as he suspected, I hadn't thought about that at all. BUT ... millis() does in fact increment by 1.

SECOND ... (though, I had forgotten) The code in "BlinkWithouDelay" uses "previousMillis = currentMillis; "

MY CONCLUSION ... is that this is a discussion about angels on the head of a pin. So, in the absence of evidence of a real problem I will not post an updated version of my demo sketch. The present version works and is consistent with BlinkWithoutDelay.

I am wondering if I might write a short accompanying essay that covers the points raised here (this one, the use of Switch and the maintaining State in static local variables).

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

#19
Mar 11, 2014, 08:45 amLast Edit: Mar 11, 2014, 08:49 am by Coding Badly Reason: 1
BUT ... millis() does in fact increment by 1.

There you go again making assumptions.  This time you assume I made a mistake (and failed to test your assumption).

What about when the processor is running at 8 MHz?  What about when the fractional part reaches a whole number?

#20
Mar 11, 2014, 08:48 am
MY CONCLUSION ... is that this is a discussion about angels on the head of a pin.

Uh huh.  Except, of course, when a drifting clock is undesirable.

Robin2

#21
Mar 11, 2014, 09:18 am

BUT ... millis() does in fact increment by 1.

There you go again making assumptions.  This time you assume I made a mistake (and failed to test your assumption).

What about when the processor is running at 8 MHz?  What about when the fractional part reaches a whole number?

I made no assumptions, certainly not about your coding ability. I wrote a short sketch to test millis() on my Uno. I have another 328 which I could (maybe already have) set up for 8MHz but I haven't tested that. I also have some Attiny 45's that I could set up for 1MHz. Most beginners for whom my demo may be useful are likely to be using an Uno at 16MHz.

If you know what different increments of millis() are possible in different circumstances I would appreciate it if you would tell me, rather than teasing with questions.

As regards the drifting clock - I started this to produce a more extensive example of "BlinkWithoutDelay" - nothing more. I expect that someone who needs better performance will also know how to get it, or will start a new Thread about it. I don't see any need to complicate matters for newbies. It would be another thing entirely if my code did not do what it claims (and I believe it does).

None of this means that I have not been interested in your comments - I will certainly bear them in mind for my own projects.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PeterH

#22
Mar 11, 2014, 03:57 pm

MY CONCLUSION ... is that this is a discussion about angels on the head of a pin. So, in the absence of evidence of a real problem I will not post an updated version of my demo sketch. The present version works and is consistent with BlinkWithoutDelay.

I disagree completely - your conclusion is wrong. The technique used in your example is flawed and is vulnerable to slip. The classic 'blink without delay' makes the same mistake (as well as others). It is quite a common mistake, and the fact that it's taking so long to explain it to you demonstrates that it's quite a subtle problem - but it is still a problem.

The problem is that the timing will be subject to cumulative slip when there is any latency between the timed condition becoming true, and the code to detect that executing. As soon as that latency exceeds 1ms, the time of the next scheduled event will be wrong, and so on for all subsequent events. The timing will have slipped by a small amount. It will keep doing this every time there is any latency. There may be some situations where that doesn't matter, and it's even possible to design a sketch that relies on that behaviour, but as a general approach for making something happen at regular intervals this is a problem.

Fortunately the solution is easy: schedule the next event based on when the current event was due, not when it was detected.

#23
Mar 11, 2014, 06:23 pmLast Edit: Mar 11, 2014, 06:26 pm by Coding Badly Reason: 1
If you know what different increments of millis() are possible in different circumstances I would appreciate it if you would tell me, rather than teasing with questions.

Uno running at 16 MHz...

Code: [Select]
`void setup() {  Serial.begin( 115200 );  Serial.println( F( "Five seconds to lift off..." ) );}static unsigned long Mark;static unsigned long Previous;static unsigned short Histogram[256];void loop() {  unsigned long Current;  unsigned long Delta;    Current = millis();  Delta = Current - Previous;  if ( Delta != 0 )  {    Previous = Current;    if ( Delta < 256 )    {      ++Histogram[(unsigned char)Delta];    }    else    {      ++Histogram[255];    }        return;  }  if ( Current - Mark >= 5000ul )  {    for ( int i = 0; i < 256; ++i )    {      Serial.print( i );      Serial.write( '\t' );      Serial.println( Histogram[i] );    }    Serial.println();    while ( true );  }}`

Code: [Select]
`Five seconds to lift off...0 01 47662 1173 04 05 06 07 08 09 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 0100 0101 0102 0103 0104 0105 0106 0107 0108 0109 0110 0111 0112 0113 0114 0115 0116 0117 0118 0119 0120 0121 0122 0123 0124 0125 0126 0127 0128 0129 0130 0131 0132 0133 0134 0135 0136 0137 0138 0139 0140 0141 0142 0143 0144 0145 0146 0147 0148 0149 0150 0151 0152 0153 0154 0155 0156 0157 0158 0159 0160 0161 0162 0163 0164 0165 0166 0167 0168 0169 0170 0171 0172 0173 0174 0175 0176 0177 0178 0179 0180 0181 0182 0183 0184 0185 0186 0187 0188 0189 0190 0191 0192 0193 0194 0195 0196 0197 0198 0199 0200 0201 0202 0203 0204 0205 0206 0207 0208 0209 0210 0211 0212 0213 0214 0215 0216 0217 0218 0219 0220 0221 0222 0223 0224 0225 0226 0227 0228 0229 0230 0231 0232 0233 0234 0235 0236 0237 0238 0239 0240 0241 0242 0243 0244 0245 0246 0247 0248 0249 0250 0251 0252 0253 0254 0255 0`

2.4% of the time a two was returned otherwise a one is returned.  Which passes a basic stink test: 4766 + 2*117 = 5000.

#24
Mar 11, 2014, 06:29 pm
As regards the drifting clock - I started this to produce a more extensive example of "BlinkWithoutDelay" - nothing more. I expect that someone who needs better performance will also know how to get it, or will start a new Thread about it. I don't see any need to complicate matters for newbies. It would be another thing entirely if my code did not do what it claims (and I believe it does).

Perfect.  My only concern was that your audience knows what they are getting and, with that, they do.

Robin2

#25
Mar 11, 2014, 06:30 pm
Thanks for your code @CodingBadly. I will try it and then I also want to reply to @PeterH.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

3dprinter

#26
Mar 11, 2014, 08:03 pm
Me sits here feeling slightly miffed that my explanation (reply#8)
of the reason why timer += increment is the better way is quietly ignored by Robin2
-- only to be repeated by PeterH (reply#17)      (and I am sure PeterH invented his own).

Robin2 -
Code: [Select]
`void loop() {  delay(12) ;  Serial.println(millis()) ;  }`
millis() now increases by 12 in each loop ! Eh?! And sometimes by 13. Eh eh!?!?

Quote
SECOND ... (though, I had forgotten) The code in "BlinkWithouDelay" uses "previousMillis = currentMillis; "

Unfortunatly there are several things missing, misleading and sometimes plain wrong in the reference/tutorials.
These have been pointed out, but the Arduino team has choosen not to use advice from this forum on that matter.

Quote
MY CONCLUSION ... is that this is a discussion about angels on the head of a pin

You're entitled to your point of view. However, this is not a religous point - it is mathematically deterministically provable true.
It may be for some applications the slippage/jittery is of no consequence, but it is there.

PeterH said it succinctly "the fact that it's taking so long to explain it to you demonstrates that it's quite a subtle problem - but it is still a problem."

Robin2

#27
Mar 11, 2014, 08:07 pm
11 Mar 2012

@CodingBadly, I modified your code a bit to explore the phenomenon further. It seems that millis() increments by 2 every 41 or 42 steps. My code is below. Let me know if I have misrepresented your ideas. It could be interesting to try to figure out what give rise to the 2 step, and whether the frequency varies.

@PeterH, Based on the data from that sketch I think I can see the problem you refer to. For example, suppose the required interval is 41 millisecs. Let's also assume that on the previous iteration millis() was 40 (so the event did not happen). On the next iteration millis() will be 42 (because it jumped 2 places) and then the event will be triggered a millisecond late. If I use that new value of millis() as the base for the next event it should happen when millis() becomes 83. Whereas if I calculated from the originally intended event time (41) I would schedule the next event for 82. On the other hand if the required interval doesn't coincide with the millis() jump there won't be any error.

I think what has been confusing me (maybe still does) is your phrase "when there is any latency between the timed condition becoming true, and the code to detect that executing". I believe that by "saving" millis() at the start of each iteration of loop() it doesn't matter when the code actually gets around to executing the event. And the timing of the subsequent event is not linked to the actual time of execution of the previous event. Instead it is linked to the value of millis() at the time the loop() began its iteration.

Bearing in mind the limited purpose of my sketch and its intended audience I still think the problem is small enough that the balance of advantage lies with being consistent with the usage in Blink-Without-Delay rather than trying to explain this problem to newbies.

As I said earlier, I am thinking of writing a short additional note to cover this point and the two others that have been raised. Your comment has helped to clarify my understanding, and may make a description easier to write. Do feel free to write a few explanatory sentences/paragraphs on the subject yourself to save me the bother.

@Msquare, your post just came in as I finished this. Sorry if I was too thick to see your point. You, also, are invited to write a suitable explanation for newbies.

...R

Code: [Select]
`static unsigned long Mark;static unsigned long Previous;static unsigned short Histogram[256];static unsigned short OverOne[256];void setup() {  Serial.begin( 115200 );  Serial.println( F( "Five seconds to lift off..." ) );    unsigned long Current;  unsigned long Delta;    int count = 0;    while (count < 256) {    Current = millis();    Delta = Current - Previous;    if (Delta > 0) {        Previous = Current;      Histogram[count] = (unsigned char)Delta;      if (Delta > 1) {        OverOne[count] = count;      }       count++;    }  }  for ( int i = 1; i < 256; ++i )    {      Serial.print( i );      Serial.write( '\t' );      Serial.print( Histogram[i] );      Serial.write( '\t' );      Serial.println( OverOne[i] );    }  Serial.println();}void loop() {  }`
Two or three hours spent thinking and reading documentation solves most programming problems.

#28
Mar 11, 2014, 08:28 pm
Robin2,
This was how AWOL or CodingBadly or PaulStoffregen or maybe PaulS explained accurate time keeping to me:
Code: [Select]
`void loop(){currentMicros = micros();  // I have found micros to keep more accurate time// likely because 16 clocks is 1uS, and millis has some "fudge factor" in it.elapsedMicros = currentMicros - nextMicros;  // I had trouble with this evaluation in an if early on, so now I do it outsideif (elapsedMicros >= duration){// here's the key:nextMicros = nextMicros + duration; // set up time for next comparison/* nextMicros is kept independent of currentMicros - so if currentMicros capture was delayed for any reason, the delay does not impact when the next time comparison will happen. the comparison will still be at a multiple of "duration". the actual real-world time between comparisons may then wiggle some, this will be    independent of that wiggle. */// do the timed event  } // end time check} // end loop`
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.