I'm having a bit of difficulty wrapping my head around the whole 'Not using Delay' thing.
I'm trying to rewrite my own animations to run without using delay so I can use a momentary push button to change the animations.
I have already figured changing to nextPattern with a push button, but I don't need that yet.
I have played around with the 'DemoReel100 with push button' example with some success, but when I add any of my own animations that use delay, obviously it stops the button working.
This animation should display a sequence of colours with each colour blacking out b4 the next colour.
I have mocked up a simple Wokwi with the minimum code needed to demonstrate my issue.
This is what I want it to look like (but without using delay)-
But this is the closest I have got-
If I only run the yellow loop it lights up as I want it to, and stays lit up.
If I can get the second Wokwi demo to look like the first one it will be, very useful with nearly all my animations and, a big leap forward.
Some of my other animations are in this Wokwi demo-
I don't expect or want anybody to correct my animations, I just need a little help with a tiny portion of one animation that will enable me to rewrite them myself. All part of the learning process.
It will be some time before I can look at your code, your code and your code correctly equipped/sober.
Thanks for posting the wokwi links. If you haven't, lock them so we stay on the same page, and move improvingments to a new sim.
Right away, however, I see the EVERY thing, whxch I have looked at before.
While it might work, or might be made to work for you, it is at the expense of doing a bit of learning how to make things go without using it.
I haven't look at the link @jremington offers, but hey, bald engineer, must be halfway good.
Personally I'd much rather you allow us to help you up and over the bump in the curve that is mastering the several concepts that go into delay-less programming. In your own words, that is to say without libraries or macros.
Re the 1 second wait... an easy way to think about this is a wait of 50ms x 20 times. If you do that then you have to do something every 50ms, so you only have 1 timing to consider.
Thanks for the replies, @jremington I have read this already and I do understand the principal of blink without delay, it's the pause between colours I was having trouble with.
@alto777 I have read this b4 as well, again I don't see anything to help with the pause. I will say thanx for the pointers though, maybe I'm missing something.
No, EXACTLY like.
This is just what I needed. The pause between colours is a lot clearer for me now.
If I rewrite all my animations with this method, the code could get quite big.
I might see if I can make the push button stop the animations from cycling, continue to play the current animation until pressed again to start the cycling from current pattern.
It will also have a push button to toggle the triangle between cycling and all white, like a little reading light as it will be placed above my bed.
I have already got this working, to some extent, but it's not required yet.
Thankx again folks, I am happy with the solution by @red_car so I can call this issue solved.
I will return with my next problem, whatever it may be.
There are definitely opportunities to optimise the code. I left it as written to ensure it was simple to understand.
But for example, the code for pausing (states 1, 3) is almost identical. All except incrementing the state variable... but you could use something like state = (state + 1) % 4; You can then put that code in a function and call that.
Similarly the code for turning on/off the LEDs is identical, except for the colour of the LED... again you could put that code in a subroutine, and just pass the colour in as a parameter.
If you find that you are writing very similar code over and over then there is usually an opportunity that it can be encapsulated in a function... and you just pass in as parameters anything that might be different or specific.
One last question on this for @red_car , or anybody else that cares to answer.
I have added 2 more colour wipes to the code and it works perfect as it is, so a massive thanks for that.
Last thing I would like is to have the Black run in reverse, so the colour fills then drains from the end to the start. I've been trying to do this for hours now, I just can't figure it out.
If I can solve this then all my own animations can be converted to run without Delay.
I will post a little example of an animation showing what I have learnt if I get this sorted.
Well generally to make things go backwards, you can use some maths.
case 10:
leds[44 - i] = CRGB::Black;
i++;
FastLED.show();
if (i == 45) {
state++; i = 0;
}
break;
!!! Untested.
Some notes:
i is a crummy variable. It makes working with you code harder.
leds[i++] = CRGB::Black;
is nice, but I would prefer to keep the two actions separate
leds[i] = CRGB::Black;
i++;
i is a crummy variable. It makes working with you code harder.
And this
state++; i = 0;
all over the place is OK here, but the idea of a state machine is that the next state is not necessarily the next in order - they can jump and dance all over the place, so I would
state = 10; i = 0; // go to state ten
and also probably jettison using numbers altogether and use manifest constants or an aponymous enum, viz:
enum {FADE_RED, WIPE_DOWN, KILL, REVERSE_KILL};
and case out by name
switch (state)
{
case FADE_RED :
leds[i++] = CRGB::Yellow;
FastLED.show();
if (i == 15) {
state = REVERSE_KILL; i = 0;
}
break;
so now you can see where it is and where it is going, and change around the order and stuff.
OK, I tested the small change, it works fine or appears to.
case 10:
leds[44 - i] = CRGB::Black;
i++;
FastLED.show();
if (i == 45) {
state++; i = 0;
}
break;
Life too short, so I also
if (currentMillis - changeTime > 15)
and lastly, as my beach buddy just texted, she who must not be kept waiting,
how on Earth are you getting along without doing any serial printing to reveal the values of key variables and demonstrate the correctness of the flow they inform?
These variables are both state variables that together are a state vector that tells the code just what it needs to be doing.
For repeating the transition code, it might be nice to add a one-shot flag as a third state variable, that makes it easy to transition and initialize near the running code:
bool oneShot = false;
void changeState( int nextState){
state = nextState;
oneShot = true;
}
...
case 10:
if (oneShot){ // initialize
oneShot = false;
i=0;
}
leds[44 - i] = CRGB::Black;
i++;
FastLED.show();
if (i == 45) { // transition
changeState(state+1);
}
break;
then, if you needed, you could have case-local static variables instead of the global i and it could be possible to switch animations and switch back where they were.
case 10:
{
static int j=0;
if (oneShot){ // initialize
oneShot = false;
if (j==45) j = 0;
}
leds[44 - j] = CRGB::Black;
++j;
FastLED.show();
if (j == 45) { // transition
changeState(state+1);
}
break;
}
Yes. The one shot idea affords an opportunity for major case initialisations of any kind.
And the changeState() function could be enhanced to print out the transition to a new state, after of course seeing if it was indeed going to. Be a transition. Mightn't. In which case also the one shot should not be reset.
I see you didn't forget to { enclose } all the code in the case… avoiding some nasty behaviour, especially since warnings are off by default in the IDE.
I had warnings on but failed to read them when that thing caught me, wasted my time and a small amount of forum time finding that out… do I now more carefully read the warnings?
Still gonna suggest moving away from numbers and incrementing for making transitions.
While I was testing the small change, I replaced the 50 constant with a variable, so I could rapidly get through the effects I wasn't checking or interested in, and then changed that variable to a larger number to s l o w t h i n g s d o w n a bit, and it occurred to me that @red_car's introduction of the frame rate gives a place to, say, read a potentiometer and adjust the speed of the whole thing.
I like the other layout of the LEDs better for some strange reason. I looked at the *.json file - did someone add , position and wire up all those LEDs or are we starting to write programs while output can be copied into the *.json?
@alto777 You may be pleased to know that the triple 7 will be the final most used layout.
I made the little triangle so it is easier for me to understand.
The .json file was made by me. It's mostly copy and paste with the wiring and pixels, then changing the position settings and rgb#:****. The wire colour was done with 'find and replace all'.
It took about an hour an' half.
All this information is going to take me some time to get through. I'm still new to coding and never thought a simple animation could get so technical so quick but, with all your help, I'm getting there.
Now that I have the tools to fix my animations, I will do 2 or 3 first and put them together with a couple of FastLED examples and just get them to play on the triangle. Then I will have a go at getting it working on the 7's. That way I will learn a bit more.
After that I will look at streamlining the whole code.
enum {FADE_RED, WIPE_DOWN, KILL, REVERSE_KILL};
I have seen enum b4 in some examples, I will be looking at it again.
Not quite got the one-shot thing yet, but I'm not far off.
For testing I'm using Wokwi and some of the DemoReel100 setup code. I don't think I'm good enough to write the entire code from scratch yet but I am learning a lot from playing around with the examples and reading threads on the forum.
I will definitely be changing the play order section because some patterns need to play for longer than others, such as confetti and sinelon. I have seen it in an example somewhere but can't remember where.
You people are amazing.......
I am in awe of your skills and knowledge. If I can learn half as much as you, I would know more than I could ever need.
The changing of durations (and speeds) for particular modes could be handled by the if(oneShot){ ... } initialization trick.
Per an example I read by stephan, I think of the CPU wandering around a kitchen checking on the bits that need doing ,and the state variable(s) as what it needs to remember about what it is doing.
One way to think of the oneShot state variable is as a message to yourself that you need to initialize. You could rename it as:
bool needToInitialize;
and consider that when you change states, you should write a message to the new state that it needs to initialize itself. Once it does the initialization it clears that message and operates as normal. The nice thing about it is it moves the initialization code out of the prior state (which makes it easier to reorder states) and into the code for the state which know what needs initializing.
Some folks do initializations or cleanup with extra states using a single, one dimensional state variable, like
case preWIPE_DOWN:
i = 0;
// other initializations...
state = WIPE_DOWN;
break;
case: WIPE_DOWN:
....
// oops: if(done=true) state = postWIPE_DOWN;
if(done==true) state = postWIPE_DOWN;
break;
case: postWIPE_DOWN:
i = 0; j=0;
sendEmail("done","daveX@gmail.com");
digitalWrite(valvePin,OFF);
state = preKILL;
break;
...