Hi EssentialLED,
Well, you can use millis() here, but I'm not sure it will do what you expect it to do.
millis() in essence return the time, since the Arduino got powered up, in milliseconds (1000 milliseconds = 1 second).
So let's say we want to implement delay(200) by using millis(), you'd basically have to store the start time, and wait time to reach startTime + 200, before proceeding. Something like this:
unsigned long startTime = 0;
// ...
startTime = millis();
while (millis() < startTime+200) {
// do something here while we are waiting
}
In plain English:
1. Store the current time in startTime
2. Keeping doing (while) something as long as the current time is less that the startTime+200.
In that loop you could read the button for example, or print something to the serial monitor, or do simply nothing.
I hope that makes sense.
However, now comes the problem ...
Say in that time frame the button gets pressed, you'd probably want to call some sort of function to change the effect.
This will work, however, when that new function is done, it will return to this loop and continue where it left off.
When the called function has something similar (using millis() waiting for the button yet again), and this function will call yet another effect function, then your Arduino will keep accumulation positions where it should return to, which in the end (potentially) will chew up all your memory.
This is why I use the Interrupt and Reset function (here). What I do there is save the current effect in EEPROM (survives a reset), and reset the Arduino, and once the Arduino starts it will look for the value in the EEPROM so it knows what effect to start with.
I hope this helps, but I realize this may make it even more confusing 🤣
Note: A reset may not be needed, if you design your effect functions in such a way that it can return to the main loop when it sees that you pressed the button (and changed a global variable to store what the effect should be). The advantage of the reset is that it can brutally stop an effect.