Go Down

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

GoForSmoke



I also prefer a different bracketing style to yours, but that's neither here nor there.


Ah, but if you use "Auto Format", it arranges things in a certain way for you.



Not completely at all. If you put the open brace on its own line, it will stay on its own line easier to see and balance.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

polymorph


Code: [Select]
...
    if (currentMillis - previousOnBoardLedMillis >= onBoardLedInterval) {
...
       previousOnBoardLedMillis = currentMillis;
    }


The average interval will be slightly to significantly more than onBoardLedInterval.  For most uses (like blinking an LED) the longer interval is insignificant.  For some uses the longer interval is a failure.

Code: [Select]
       previousOnBoardLedMillis += onBoardLedInterval;

...produces a bit more code but keeps the average interval at exactly onBoardLedInterval.


Oh, I get it, maybe. If you make previousOnBoardLedMillis equal to currentMillis, then errors due to the time it takes to step through the program are cumulative. But if you merely add onBoardLedInterval to previousOnBoardLedMillis, while each time around will have a little error in timing, it is not cumulative.

Is that right?
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - https://tinyurl.com/Technote8
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts

Robin2

#77
Jun 04, 2014, 09:12 pm Last Edit: Jun 04, 2014, 09:16 pm by Robin2 Reason: 1

Is that right?


Very close.

The errors that creep in and could accumulate happen because currMillis = millis() may happen a tiny bit late. Remember that the test is if (currMillis - prevMillis >= interval) so it may be a little greater, but it will never be a little less.

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

GoForSmoke

When you read millis() into currMillis, you set a time point.

The math (currMillis - prevMillis >= interval) evaluates the time point as TRUE or FALSE

When that is TRUE, you can eliminate slippage to less than 1 ms.
First, calculate millis past interval it took to run the check.
lateMillis = (currMillis - prevMillis) - interval; // partly to keep the compiler from rearranging terms
Then set your start time to the time point plus the overage at the time point.
prevMillis = curMillis + lateMillis
Slippage will be less than 1 ms, unless you time micros.

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Robin2


Slippage will be less than 1 ms, unless you time micros.


prevMillis += interval    has NO slippage and is much simpler.

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

GoForSmoke

Oh. Right.
Tired lately. Crash after caffeine burnout.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Paul Beaudet

First of all, I commend Robbin's effort of showing newbies how to manage time.

Going to point out something a little less technical  though. Sometimes people need to step in poo before really understanding it smells. Even after countless people have told them how much it smells. In the figurative sense my feet have a lot of lingering smell, probably the worst offender of this.

Tried to show a bunch of kids how to set robot motors without delays. Even basically built the functions for them. Long story short, gave up and told them to use delays. After that, they started making progress as opposed to being confused. The quality of my explanation mattered very little.

A smart person knows when they are right. A wise person knows when being right matters.

Robin2

@Paul Beaudet,  I completely understand what you are saying.

And I am a great believer in learning by doing - the theory often makes complete sense later.

From reading a lot of stuff here it is obvious that some people grasp concepts quickly and others never do. It is always difficult to know where to pitch advice when you don't get the instant feedback that you get in a face-to-face situation.

One of the difficulties about the delay() vs BWoD choice is that it can be very difficult to convert a complex piece of code from using delay()s to using BWoD. For that reason I am inclined to encourage the early use of BWoD even when, technically, it doesn't make a lot of difference.

Conversion is not helped by the understandable "decision" by many newbies to bundle all their code into loop(). I put the word decision in quotes, because, of course it is an absence of a decision to be more organized - usually for want of experience.

Another complication is that you never know how long a newbie is going to stick with Arduino - if s/he is going to lose interest after the first project almost all teaching will be a waste of time. I think the only practical approach is to treat everyone as a long term prospect.

Following from that, my perception is that it is not helpful to propose (as opposed to assist with) second rate solutions.

And, of course, the reader is always free to reject the advice. I hope that people who use my example do so because they themselves think it is appropriate.

Thanks for your comments.

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

polymorph

Quote
One of the difficulties about the delay() vs BWoD choice is that it can be very difficult to convert a complex piece of code from using delay()s to using BWoD. For that reason I am inclined to encourage the early use of BWoD even when, technically, it doesn't make a lot of difference.


Yes! Agree 100%!
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - https://tinyurl.com/Technote8
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts

GoForSmoke

The more time and money invested in a mistake, the harder it is to walk away.

Never time to do it right, always time to do it over.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

polymorph

Yeah... a friend of mine has the unenviable job of cleaning up sloppy quick-fixes in other people's programming.

And this is why I'm on kind of a tear, criticizing the over-use of delay() to time things. It really annoys me how just about every book, class, website, and video that purports to be for beginners to Arduino, use delay() for everything.
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - https://tinyurl.com/Technote8
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts

JimboZA

At last I got round to looking at this thread, although I've seen Robin2 link to it numerous times.

The esoteric discussion on latency and slippage aside- most if not all of which went splat on the wall behind me- it's an excellent piece of work and a good look at the whole bwod thing. Well done and karma to you Robin2.

I'm going to use this approach in some stuff I intend to do today- probably get really advanced and have different durations on the LEDs  8), and include some other stuff I'm fiddling with.

[whisper]It's such a pity that this thread went off the rails a number of times but that's life I guess.[/whisper]
Johannesburg hams call me: ZS6JMB on Highveld rep 145.7875 (-600 & 88.5 tone)
Dr Perry Cox: "Help me to help you, help me to help you...."
Your answer may already be here: https://forum.arduino.cc/index.php?topic=384198.0

spruce_m00se

Well, i just stumbled across this example after starting this thread:
http://forum.arduino.cc/index.php?topic=248643.0

I had already written a higgledy piggledy code that more or less did what I want it to do, but some things are just seemingly messed up by timing during other events using delay()

I had already taken each part of the code into individual functions as in your example before reading it and was looking into how to use millis in various functions at the same time,

this was very useful having stepped in the previously mentioned poo and looking for a way to clean my foot,

Thanks,

polymorph

And if you need finer grained timing, there is also micros(). Although it counts in microseconds, you should know that the granularity is 4us.
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - https://tinyurl.com/Technote8
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts

GoForSmoke

#89
Jun 23, 2014, 11:50 pm Last Edit: Jun 24, 2014, 12:01 am by GoForSmoke Reason: 1
Months ago I saw this thread but since the principle is the same as what Nick Gammon covers very will in his blog (that explains blocking code and other basic concepts in a slow-pitch way), I just linked people to that.

If you haven't seen, this is Nock's (edit, errr Nick's) tutorial:
http://www.gammon.com.au/forum/?id=11411
Note to self: start using the new keyboard that doesn't have half the letters rubbed off.

Not long ago I got back and learned one very good lesson from Robin2 (karma to you, Robin) of not updating start time to millis() in a time check that may be late but rather adding the interval to the start which is ALWAYS right.

So this time I actually looked the code over and I see something that I have done very often before, especially when under the gun, but that does not make it good practice at all. In fact, even as beginners we are reminded to not do this thing.
And what is that?
Copy&paste then modify to suit code multiplication. Because it works and what works is good, right? Wrong.

Do I want to add a block of code for every led I add? No. That's why there's arrays, etc.

I had a pretty good day today, healthwise, so I dug out the breadboard, parts boxes and magnifier to do something.

Here is Blink Multiple Leds Without Delay in 2 versions.  
1st is the short version that I count as 24 lines of actual code (not counting braces).
2nd is a longer teaching version with loads of comments and serial report options. 1st is the short version that I count as 24 lines of actual code (not counting braces).

Mess with it. Ask questions. I had all that addressed then the forum timed me out and poof, all that is gone!
Add leds. If you want more than 8 then change ledState to unsigned int or long.

Short version:
Code: [Select]

//  Blink Multiple leds With Uneven Delays, Short Version

const byte    LEDS = 4;
const byte    ledPin[ LEDS ] = // I use Autoformat. It won't let this be 1 line.
{
 13, 3, 5, 7
};

byte          ledState = 0; // this will handle 8 led states as bits
byte          led = 0; // this example processes 1 led per pass through loop()

unsigned int  millisNow; // fill with the lower 16 bits of millis()
unsigned int  startBlink[ LEDS ]; // these all default to zero
unsigned int  waitBlink[ LEDS ][ 2 ]; // one for OFF time, one for ON time

void setup()
{
 for ( led = 0; led < LEDS; led++ )
 {
   waitBlink[ led ][ 0 ] = 250U * ((unsigned int) led + 1U ); // OFF millis
   waitBlink[ led ][ 1 ] = 10U * ((unsigned int) led + 1U ); // ON millis

   pinMode( ledPin[ led ], OUTPUT ); // default output is LOW
   digitalWrite( ledPin[ led ], bitRead( ledState, led ));
 }  

 led = 0;
}

void loop()
{
 millisNow = (unsigned int)( millis() & 0xFFFF ); // lower 16 bits roll over just fine

 if ( millisNow - startBlink[ led ] >= waitBlink[ led ][ bitRead( ledState, led ) ] )  
 {
   startBlink[ led ] += waitBlink[ led ][ bitRead( ledState, led ) ]; // update start time

   bitWrite( ledState, led, ( bitRead( ledState, led ) == 0 )); // change led state bit
   digitalWrite( ledPin[ led ], bitRead( ledState, led ) ); // change the led pin  
 }

 led++;
 if ( led >= LEDS )
 {
   led = 0;
 }
}


Long 'learning' version with serial reporting options.
Code: [Select]

//  Blink Multiple leds With Uneven Delays, Serial reporting optional

/*
The hardest part of this was getting the debug/report prints to not screw the timing
by more than 1 milli. I even left in some commented-out lines just for comparison.
Serial is not free, it blocks execution!
*/

// make SERPRINT 1 to enable serial preorti
#define  SERPRINT  1

// #if SERPRINT ---- if I leave this in, the sketch won't compile
byte runBeforeStop = 199; // to limit serial print lines set this to < 200
//#endif

const byte    LEDS = 4;
const byte    ledPin[ LEDS ] = // I use Autoformat. It won't let this be 1 line.
{
 13, 3, 5, 7
};

byte          ledState = 0; // this will handle 8 led states as bits
//            to track more than 8 leds, use larger unsigned integers than byte
byte          led = 0; // this example processes 1 led per pass through loop()

unsigned int  millisNow; // fill with the lower 16 bits of millis()
unsigned int  startBlink[ LEDS ]; // these all default to zero
unsigned int  waitBlink[ LEDS ][ 2 ]; // one for OFF time, one for ON time
//               using the low 16 bits of millis allows 65.535 second blink
//               however if you need longer, change these to unsigned long
//          you can initialize this array as const or use PROGMEM to store it
//          I chose to use a formula. With a different formula I could get rid
//          of the whole array and use one or more variables to run the formula.

// This is code. Make your own way all you *can*! More you do, better you get.
// And note, this mess just begs to be turned into a Class Objects example.

void setup()
{
#if SERPRINT
 Serial.flush();
 Serial.begin( 9600 );
 Serial.println( "\nMulti-led blink test\n" );
#endif

 for ( led = 0; led < LEDS; led++ )
 {
#if SERPRINT
   Serial.print( " Led " );
   Serial.print( led, DEC );
   Serial.print( " >> " );
#endif

   // setting the blink delays by formula here
   // YES for this the array is not needed, the code below could use the formula
   // however this example can also use a pre-compiled array
   // and if User I/O or other dynamic change method is added, the array may serve that
   // But Please, change it all to suit your ideas as they occur!

   waitBlink[ led ][ 0 ] = 250U * ((unsigned int) led + 1U ); // OFF millis
   waitBlink[ led ][ 1 ] = 10U * ((unsigned int) led + 1U ); // ON millis

#if SERPRINT
   Serial.print( " OFF millis = " );
   Serial.print( waitBlink[ led ][ 0 ] );
   Serial.print( " ON millis = " );
   Serial.println( waitBlink[ led ][ 1 ] );
#endif

   pinMode( ledPin[ led ], OUTPUT ); // default output is LOW
   digitalWrite( ledPin[ led ], bitRead( ledState, led ));
 }  
#if SERPRINT
 Serial.println( );
 
 Serial.println( "Led <led> t <millisNow> x <pin state> n <next change>" );
#endif

 led = 0;
}

void loop()
{

 millisNow = (unsigned int)( millis() & 0xFFFF ); // lower 16 bits roll over just fine

 if ( millisNow - startBlink[ led ] >= waitBlink[ led ][ bitRead( ledState, led ) ] )  
 {
#if SERPRINT
   Serial.print( led, DEC );
   Serial.print( " t " );
   Serial.print( millisNow );
/*
   Serial.print( " states " );
   Serial.print( ledState, BIN );
*/
#endif

   startBlink[ led ] += waitBlink[ led ][ bitRead( ledState, led ) ]; // update start time
   // Thank You Robin2, this really does take care of late updates

   bitWrite( ledState, led, ( bitRead( ledState, led ) == 0 )); // change led state bit
   digitalWrite( ledPin[ led ], bitRead( ledState, led ) ); // change the led pin  

#if SERPRINT
/*  put these in and see what it does occasionally to timing. then add more serial
   Serial.print( " change " );
   Serial.print( ledState, BIN );
   Serial.print( " start " );
   Serial.print( startBlink[ led ] );
   Serial.print( " wait " );
   Serial.print( waitBlink[ led ][ bitRead( ledState, led ) ] );
*/
   Serial.print( " x " );
   Serial.print( bitRead( ledState, led ) );
   Serial.print( " n " );
   Serial.print( startBlink[ led ] + waitBlink[ led ][ bitRead( ledState, led ) ] );
   Serial.println();
   
   if ( runBeforeStop < 200 )
   {
     if ( !runBeforeStop-- )
     {
       while( 1 );
     }
   }
#endif

 }

 led++;
 if ( led >= LEDS )
 {
   led = 0;
 }
}

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Go Up