Choose Mode by grounding pin 7 (Delay Mode) or not (Time Mode) with keyboard Enter as pause/unpause scrolling control.
The loop count and status led13 only run in Time Mode.
Serial Monitor info looks very different between modes.
The demo shows how non-blocking code can run as parallel tasks smoothly and quickly, on my Uno R3 I get ? 64000 loops/second.. first second may be a partial count.
// DelayOrNoDelayWithLoopCount V1 by GoForSmoke 6/20/25 --
// LoopCount sketch lines added to run like the Status Blink as a parallel Task
// Serial Monitor output improved and more verbose -- Uno speed up to 64K range.
// DelayOrNoDelay V1 by GoForSmoke 6/3/25 -- some changes to...
// DualActionDelayMillis v1.1 by GoForSmoke 11/18/24 -- made for Uno R3
// expect: ground pin 7 to run delay mode, not grounded runs timer mode
// expect: enter key in Serial Monitor to pause action, unpause action
// code revised 2/7/25 -- now using arrays and function calls + status led
// note that Arduino millis is +/-1 and that printing takes time as well!
// line taken from my 2024 NoBlockLoopCounterLT sketch.
extern volatile unsigned long timer0_millis; // might be faster than millis()
// end taken
const byte jumperPin = 7; // a jumper from pin 7 to GND or pin 7 unterminated
byte jumperStateNow, jumperStatePrev; // to compare what is to what was
const word shortWait = 3; // ms delay for the contacts to settle
const byte statusBlinkPin = 13; // Uno board LED pin13 to run as status led
byte statusBlinkState; // led13 0=OFF, not-0=ON
unsigned long statusBlinkStart;
const unsigned int statusBlinkInterval = 500;
const unsigned long interval[ 2 ] = { 3000000, 700000 }; // for 2 timings, both modes wait time
unsigned long start[ 2 ]; // for 2 timings, timer mode
byte started[ 2 ] = { 0, 0 };
byte runInterval = 0;
void usage()
{
Serial.println( F( "\n\n Dual Action Delay Millis 2/7/25 \n" )); // now shows version
Serial.println( F( " Ground pin 7 to run delay mode, not grounded runs timer mode." ));
Serial.println( F( " You can jumper pin 7 to ground or even put a button between pin 7 and ground." ));
Serial.println( F( " Send Enter key in Serial Monitor to pause action, unpause action." ));
Serial.println( F( " Loop count will print, status led13 blink in Time Mode." ));
Serial.println( F( " Note that printing Serial can cause timing to be slightly late.\n\n" ));
}
// function taken from my 2024 NoBlockLoopCounterLT sketch.
void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
static unsigned long count; // only this function sees this
static bool lastBit10Set; // only this function sees this
word millis16 = timer0_millis;
count++; // adds 1 to count after any use in an expression, here it just adds 1.
bool currentBit10Set = millis16 & 0x0400; // leverage integral to bool implicit promotion
if (currentBit10Set != lastBit10Set) // 1 second
{
// Serial.print( millis16 ); // 16-bit binary into decimal text, many micros
// Serial.write('\t');
Serial.print( F( "Loops " )); // added for demo, not from the taken code
Serial.println( count ); // 32-bit binary into decimal text, load of cycles!
count = 0; // don't forget to reset the counter
lastBit10Set = currentBit10Set;
}
}
// end taken
void setup()
{
Serial.begin( 115200 ); // run serial fast to clear the output buffer fast
// set Serial Monitor to match
pinMode( jumperPin, INPUT_PULLUP );
jumperStateNow = jumperStatePrev = digitalRead( jumperPin );
pinMode( statusBlinkPin, OUTPUT ); // LOW by default
statusBlinkState = 0;
statusBlinkStart = millis();
usage();
}
void checkForUserJumperPinChange() // handles pin bounce, only changes once pin state is stable
{
jumperStateNow = digitalRead( jumperPin ); // check for mode change
if ( jumperStateNow != jumperStatePrev ) // if jumperPin changes state, stop and debounce then re-init
{
while ( jumperStateNow != jumperStatePrev ) // beginner debounce does block but here that doesn't matter
{
jumperStatePrev = jumperStateNow;
delay( shortWait ); // yes, freakout, user input gets delayed!
jumperStateNow = digitalRead( jumperPin ); // wait and read again, loops until no change
} // finished debounce, pin state is stable
started[ 0 ] = started[ 1 ] = 0; // initialize for timer mode, doesn't matter in delay mode
runInterval = 0; // init Interval
}
}
void pauseSerialMonitorIfSerialEntry() // stops the scrolling to allow highlight and copy (ctrl-C) for paste (ctrl-V)
{
if ( Serial.available()) // this is blocking code that does not matter
{
while ( Serial.available())
{
Serial.read(); // empty the buffer
delay( 1 );
}
usage();
while ( !Serial.available()); // wait for unpause
while ( Serial.available())
{
Serial.read(); // empty the buffer
delay( 1 );
}
started[ 0 ] = started[ 1 ] = 0; // re-init timer mode
}
}
void statusBlinker() // will not run properly in delay mode
{
if ( millis() - statusBlinkStart >= statusBlinkInterval )
{
statusBlinkState = !statusBlinkState;
digitalWrite( statusBlinkPin, statusBlinkState );
statusBlinkStart += statusBlinkInterval;
}
}
void delayModeTiming( byte interval_number )
{
Serial.print( F( "Delay " ));
Serial.print( interval_number );
Serial.write( ' ' );
Serial.print( interval[ interval_number ] / 1000 );
Serial.print( F( " ms now: " ));
Serial.println( millis());
delayMicroseconds( interval[ interval_number ] );
Serial.print( F( "End Delay " ));
Serial.print( interval_number );
Serial.print( F( " End time " ));
Serial.println( millis());
}
void timerModeTiming( byte interval_number ) // uses micros timing for closer precision, but shows millis time!
{
if ( started[ interval_number ] == 0 ) // only prints info line at the start
{
started[ interval_number ] = 1;
Serial.print( F( "Timer " ));
Serial.print( interval_number );
Serial.print( F( " Wait " ));
Serial.print( interval[ interval_number ] / 1000 );
Serial.print( F( " ms now: " ));
Serial.println( millis());
start[ interval_number ] = micros();
}
if ( micros() - start[ interval_number ] >= ( interval[ interval_number ] ))
{
started[ interval_number ] = 0;
Serial.print( F( "Timer " ));
Serial.print( interval_number );
Serial.print( F( " End time " ));
Serial.println( millis());
}
}
void loop()
{
if ( jumperStateNow == 1 ) // time mode
{
LoopCounter(); // the function runs as a task, the optimizer will inline the code.
}
if ( jumperStateNow == 1 ) // run timer mode
{
timerModeTiming( runInterval ); // only runs when it should, when pin 7 is grounded
}
else
{
delayModeTiming( runInterval ); // only runs when it should, when pin 7 is not grounded
}
runInterval = !runInterval; // ! changes 0 to 1, changes 1 to 0
if ( jumperStateNow == 0 ) // run delay mode
{
statusBlinker();
}
checkForUserJumperPinChange();
pauseSerialMonitorIfSerialEntry(); // stops Serial Monitor scrolling on key entry, resumes on next key entry
}