I am new to this forum and to arduino in general, so i apologize if this is the wrong board topic.
As a first project, I am building a small box that should have 5 buttons to trigger songs, and a different light blinking pattern for each song.
Aditionally i have an ON-OFF-ON switch to do mode selection and therefore duplicate the usage of my 5 buttons (the two ON positions address different songs, so it plays 10 different songs, and the OFF position allows the button to manually trigger custom light patterns).
I also added an extra button for pause/play the songs, and a volume knob .
I am using Arduino Nano classic, DFPlayer equivalent Board, and a small 8-LED strip (WS2812).
My problem is that i want the light pattern to shine for the whole duration of the song, for that i am using a bit of code similar to this
unsigned long startTime = millis();
while (millis() - startTime < duration){
//my pattern code
}
However this makes any other execution on the arduino impossible until the end of my light pattern.
As i would like any button push to take immedite action even during light pattern execution (for example play the song associated to the button) i am trying to find a solution to this issue.
I found out about interrupts, but it seems on the Nano only 2 pins are providing this capability while i would like to have it on all my 8 buttons (5songs btn+ play/pause btn + mode switch).
I was thiking about playing with the millis clock to execute my light pattern every other x ms and allowing reading of the input in the interval, but i wonder if there is a better way of handling this whole behaviour?
I am also not really willing to changing the nano to a different board that allows interrupt on more pins.
I also don't really want ot change the behaviour of the box, even though last resort solution would be to use the play/pause button with an interrupt to stop the whole routine and allow different song selection by doing 'pause and then any other button select).
unsigned long startTime = millis();
while (millis() - startTime < duration){
//my pattern code
}
As you have discovered, this is blocking code, ie you are stuck in the while loop until the exit condition is met. This is a misuse of the mills() function
You could solve the problem by reading inputs in your pattern code or you could restructure your code to sue millis() in a different way
As @UKHeliBob says, you have the right idea in using the millis function, but you need to use it in the right way.
Trying to use interrupts to fix it ISNT the right way. Generally, as a beginner you should avoid using interrupts.
You might find this useful (esp example 3)
Thanks a lot for pointing that out, this is indeed a very much simpler solution.
As i am new to Arduino i somehow didn't realize i could read buttons input outside of the main loop() function.
I'll follow your advice and instead of trying to achieve it via interrupts (and being limited by the number of pins accepting this behaviour) I'll simply read my buttons change within my light pattern play function.
That way i can simply update my volume on the fly in case the knob is touched, and update global state variable to exit this blocking light loop in case of a song button input, then use another global variable with button index in the standard loop() to trigger the action from the song button input.
I'll also try to use millis() to have some delay read on buttons input and avoid multiple reads on a single button push as illustrated in the code example you linked. I had the board crashing several times testing my pause button and i suspect it might be because of this.
if you were writing your "light pattern" without any delay based on the examples shown in "Blink Without Delay", you could easily read the button in loop.
Just don't block your code with long lasting delay(), for or while loops.
As @UKHeliBob states, this is a limiting use of the millis(), as you may think of millis() as an Arduino clock. If you need to place an alarm in your smartphone you don't want the device to be blocked just showing you the current time an the programmed alarm time until it rings What you are really asking is your phone to keep doing ALL it's usual stuff with the addition of checking once in a while for the list of programmed alarms, compare it to the current time and sound the alarm when it's time comes.
And that's the unblocking use of the millis() mechanism all about: once in a while (you have the opportunity in every loop() repetition. even several times along the loop) you check the millis() value against your preset value (calculating the difference of millis() is well documented everywhere and not much more than a substraction), and execute your code whent the time comes.
Inside, autside... unless you're working within a multithreading environment like RTOS, any code that hangs the execution while expecting something will block the code execution.
Use any of the many debouncing libraries available, if you want to be sure the button reading executes only once even if kept pressed use a library that includes a function execution (callback function) only once when it enters the pushed state.
Thanks everyone, first project and first post, seeing all those useful answers makes me feel very happy with this community!
With all the advices i manage to make it work as i expected, using somewhat of a state machine and software debouncing (not having the electronic components handy for a hardware version).