It's not related to long term behaviour. It's a localized effect, so if for example you had a "blink" type action it might go like this in milliseconds:
1000
1000
...
1000
1000
743
1000
1000
...
and that would happen every 49 days. If it's just an indicator light, yeah, who is going to notice or care?...
A warning for anyone is a special feeling of accomplishment when a few lines of code does something clever or obscure
Yeah, and if you really want to exercise those muscles, do it on something that really needs it, like some incredibly time sensitive low level operation that has zero chance to work any other way. It has no place in high level code, however... when I really can't resist the temptation, what I do, is write both the readable, literal if you like code, and the hack. Then I put both in the code and comment out the readable, literal code. This has two benefits. It makes it easy to understand the hack, and it makes it easy to replace the hack in short time if some problem develops from it later.
@xfpd's example does not blink five times, then pause. Nevermind.
More importantly, it provides an example that illustrates some common problems using millis().
First, the loop can be so fast that the LED is on very briefly. Or off very briefly.
In fact, every time it is turned on, it actually is turned on and off multiple times.
Every time it is turned off, it actually is turned off and on multiple times.
Sometimes it is an even number of times, and the LED stays on or off.
Second, millis() increments by two occasionally. The test will fail, and the LED will be left on (or off) for an additional 1000 ms.
Logic analyser trace showing what happens when the LED is turned on. That's a 12.5 kHz pulse train, showing the rate at which the loop is looping. So fast that millis() does not increment.
void loop() {
static int count ;
Serial.println(millis());
// rest of the loop as was
I could not understand how the RTOS solution functioned. I added the Serial.print() for another reason, but see that it executes not freely as the loop() should run, rather it reports times at the top of the loop to be
505
754
1004
1254
1504
It looks like something is throttling the loop. Was that by design?
Can you give brief explanation of how the RTOS solution times the blinks and the pause?
@noweare's code in #28 leaves the loop() blank, and the loop() will nicely spam if it has a print statement.
The task for the RTOS is easy to understand, and it seems like using an RTOS at its simplest gives one the ability to delay without blocking, so it would be very easy to take many delay-based sketches and translate them directly to the looks like delay but does not block idea.
You could add an external switch instead of the boolean constant to toggle between blocking an non-blocking modes.
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);
}
void loop() {
const bool doBlocking = true; // choose between blocking or non-blocking code
const unsigned long quickInterval = 250; //
const unsigned long slowInterval = 1000;
if (doBlocking == true) { // use delay() and for(;;)
for (int i = 0 ; i < 4 ; ++i) {
toggleLed();
Serial.print(i);
Serial.print(" ");
delay(quickInterval);
}
Serial.println("-- blocking");
delay(slowInterval);
} else { // unblocked loop() by:
// * replacing delays with millis() edge-detectors/state machines
// * replacing for(;;) loop with a counter state machine
// state variables to persist values past the end of loop()'s scope
static int i = 0; // digit to print
static unsigned long lastDigitMs = -quickInterval; // backdate by -quickInterval for immediate operation
static unsigned long lastLineMs = 0; // state variable for edge-detecting slow delay
unsigned long now = millis(); // remember the loop time
// edge detecting state machine {i,now-las}
if (i < 4 && now - lastDigitMs >= quickInterval) { // edge-detect interval change
lastDigitMs = now; // measure digit delay from last digit print
toggleLed();
Serial.print(i);
Serial.print(" ");
++i;
lastLineMs = now; // measure line ending delay from last print time
}
// edge-detecting state machine
if (i >= 4 && now - lastLineMs >= slowInterval) {
Serial.println( "-- nonblocking");
i = 0; // reset the count
}
}
}
void toggleLed(void){
digitalWrite(LED_BUILTIN,digitalRead(LED_BUILTIN)==HIGH?LOW:HIGH);
}
So 10 gives 5 flashes, but it cheats the last flash out of the extra 250 milliseconds that the OP's code does, and that at least some example code faithfully replicates.
So you gotta turn that up to 11.
Also, no matter the slide switch, it starts in blocking mode. Just heard the food signal, gotta jet.
Oops. I misread the problem, and tried to quickly force it into code from a different discussion. Solving by toggling 11 times would toggle the end state for the pause. Maybe toggle 10 times and absorb the tail-end 250ms into the long delay:
void setup() {
pinMode(12, INPUT_PULLUP);
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);
}
void loop() {
bool doBlocking = digitalRead(12)==HIGH; // choose between blocking or non-blocking code
const unsigned long quickInterval = 250; //
const unsigned long slowInterval = 1000 +quickInterval;
if (doBlocking == true) { // use delay() and for(;;)
for (int i = 0 ; i < 10 ; ++i) {
toggleLed();
Serial.print(i);
Serial.print(" ");
delay(quickInterval);
}
Serial.println("-- blocking");
delay(slowInterval);
} else { // unblocked loop() by:
// * replacing delays with millis() edge-detectors/state machines
// * replacing for(;;) loop with a counter state machine
// state variables to persist values past the end of loop()'s scope
static int i = 0; // digit to print
static unsigned long lastDigitMs = -quickInterval; // backdate by -quickInterval for immediate operation
static unsigned long lastLineMs = 0; // state variable for edge-detecting slow delay
unsigned long now = millis(); // remember the loop time
// edge detecting state machine {i,now-las}
if (i < 10 && now - lastDigitMs >= quickInterval) { // edge-detect interval change
lastDigitMs = now; // measure digit delay from last digit print
toggleLed();
Serial.print(i);
Serial.print(" ");
++i;
lastLineMs = now; // measure line ending delay from last print time
}
// edge-detecting state machine
if (i >= 10 && now - lastLineMs >= slowInterval) {
Serial.println( "-- nonblocking");
i = 0; // reset the count
}
}
}
void toggleLed(void){
digitalWrite(LED_BUILTIN,digitalRead(LED_BUILTIN)==HIGH?LOW:HIGH);
}
Or maybe its better to use a more clear set of state variables than {iteration, timer} to replace the InstructionPointer state variable of the OP code. Perhaps {iteration, led_state, timer} would be enough.
Heh -- I think it is a Wokwi-slide-switch initialization feature: On startup, the simulated hardware acts as if it toggles and bounces. (in the default switch"attrs": {"value":"0","bounce":"1"} where one toggles the switch in the UI to closed during one run and restarts the sketch for the next run)