Yet ANOTHER millis() blink with different on/off durations

I was bored this evening - so here's the same old' millis() blink - but showing different on/off durations. Indenting has been pushed - to help identify the separate blocks of code! Also demonstrates conditional compilation with #define #ifdef and #endif

// BLINK ON-BOARD LED using millis() 
// Demonstrating variable length on/off duration.
// Also illustrate using a function() without parameters
#define SERIAL_COMMENTS  115200 //comment-out, UNDEFine or delete if not required
#define LED_pin 13  // ths inbuilt UNO LED
unsigned long state_started_at;

// these /could/ be a smaller data type - sufficient to hold the durations
unsigned long blink_on_duration = 100;  // mSecs LED on
unsigned long blink_off_duration = 1000;  // mSecs LED off

bool LED_state = LOW;  // initial state will be toggled as part of setup()

// set the LED pin direction to OUTPUT, and enable Serial if it is needed
// then send the initial LED_state out to the LED
void setup() {
    pinMode (LED_pin, OUTPUT);
                #ifdef SERIAL_COMMENTS  // these lines can be deleted if you understand the code
                    // ensure serial is opened -before- calling toggle - so the LED_state can be displayed !
    toggle_LED();   // call a function to invert the LED_state

void loop() {
    // depending on prevailing state and period - toggle LED to the 'other' state
    if (LED_state) { // if the LED state is currently ON
        // and the ON duration has elapsed
        if  (millis() - state_started_at >= blink_on_duration) {
                #ifdef SERIAL_COMMENTS  // these lines can be deleted if you understand the code
                    Serial.print("ON duration elapsed...");
    } else { // then, the LED state must be OFF
        // and the OFF duration has elapsed
        if  (millis() - state_started_at >= blink_off_duration) {
                #ifdef SERIAL_COMMENTS  // these lines can be deleted if you understand the code
                    Serial.print("OFF duration elapsed...");

// change the LED state from on->off and vice-versa with each call
void toggle_LED() {
    LED_state = !LED_state;  // invert the LED_state variable
                #ifdef SERIAL_COMMENTS  // these lines can be deleted if you understand the code
                    if (LED_state)
                        Serial.println ("TURN ON"); 
                        Serial.println ("TURN OFF"); 
    // start a new duration for current LED_state
    state_started_at = millis();
    digitalWrite(LED_pin, LED_state);  // push it out to the LED

Personally I would favour something like this

const byte ledPin = 13;
const unsigned long durations[] = {1000, 100};
byte indexToDurations = 0;
unsigned long startTime;

void setup()
  pinMode(ledPin, OUTPUT);
  startTime = millis();

void loop()
  if (millis() - startTime >= durations[indexToDurations])
    digitalWrite(ledPin, !digitalRead(ledPin));
    startTime = startTime + durations[indexToDurations];
    indexToDurations = !indexToDurations;

Yes, i agree if the purpose is solely to perform on/off blinking, but I feel it introduces too many skills inline for a beginner. - Indexing, arrays etc.

(I did consider folding the conditional block into a single line, but figured leaving it open would help newbies see the logoc more easily)

Most are looking for an understanding of why/how mills() can replace delay()
Good shot though, everything helps beginners grasp a bit more.


The inclusion of an array and an index index was deliberate as, I assume, was your use of a function and the scary looking #defines and #ifdefs, to say nothing of the truly nutty formatting.

For serial comments consider something like this:

// DebugMacros.h

/* Example of use:
   #define DEBUG          // <-----<<<< this line must appear before the include line
   #include <DebugMacros.h>

//If you comment the line:  #define DEBUG
//The Macro lines are defined as blank, thus would be ignored by the compiler.
//If the line is NOT commented, these macros will be included in the sketch.
// examples:
// This  converts to         >>>>----->         This OR a Blank Line. 
// DPRINTLN("Testing123");   >>>>----->     Serial.println("Testing123"); 
// DPRINTLN(0xC0FFEEul,DEC); >>>>----->     Serial.println(0xC0FFEEul,DEC);
// DPRINTLN(12648430ul,HEX); >>>------>     Serial.println(12648430ul,HEX);
// DPRINTLNF("This text came from flash");  Serial.println(F("This text came from flash"));
// DPRINT(myVariable);       >>>>----->     Serial.print(myVariable);
// DELAY(100);               >>>>----->     delay(100);
// SERIALBEGIN(9600);        >>>>----->     Serial.begin(9600);
// PINMODE(13,OUTPUT);       >>>>----->     pinMode(13,OUTPUT);
// TOGGLEd13;                >>>>----->     PINB = 0x20;  // D13 Toggle,for UNO ONLY
#ifdef DEBUG
//#define DPRINT(args...)  Serial.print(args)             //OR use the following syntax:
#define SERIALBEGIN(...)   Serial.begin(__VA_ARGS__)
#define DPRINT(...)        Serial.print(__VA_ARGS__)
#define DPRINTLN(...)      Serial.println(__VA_ARGS__)
#define DRINTF(...)        Serial.print(F(__VA_ARGS__))
#define DPRINTLNF(...)     Serial.println(F(__VA_ARGS__)) //Printing text using the F macro
#define DELAY(...)         delay(__VA_ARGS__)
#define PINMODE(...)       pinMode(__VA_ARGS__)
#define TOGGLEd13          PINB = 0x20                    //For the UNO only
#define SERIALBEGIN(...)   //blank line
#define DPRINT(...)        //blank line
#define DPRINTLN(...)      //blank line
#define DPRINTF(...)       //blank line
#define DPRINTLNF(...)     //blank line
#define DELAY(...)         //blank line
#define PINMODE(...)       //blank line
#define TOGGLEd13          //blank line 

Consider this kind of format for updating a timing variable.

while ( currentMillis - millisOne >= delayTime )
      millisOne = millisOne + delayTime;