Blink Function using millis

Hi all,

I'm trying to write a blink function that will enable me to specify a Digital Pin, LED onTime and LED offTime.

I've written the following code which works how I'd like.

int blinkState = 0;                     // initial condition Low                           
unsigned long closedMillis = millis();
unsigned long openMillis = millis();

struct blinkStatus{
  int blinkState;                           
  unsigned long closedMillis;
  unsigned long openMillis;
};

void setup() {
  pinMode(7,OUTPUT);
}

void loop(){

struct blinkStatus T7Stat = blinkModel(7,3000,1000,blinkState,closedMillis,openMillis);
blinkState = T7Stat.blinkState;
closedMillis = T7Stat.closedMillis;
openMillis = T7Stat.openMillis;

}

struct blinkStatus blinkModel(int pin,unsigned long onTime,unsigned long offTime,int blinkState,unsigned long closedMillis,unsigned long openMillis){
       
  unsigned long currentMillis = millis();
  struct blinkStatus tStatus;
  if (blinkState == 0){
     digitalWrite(pin,HIGH); 
     if (currentMillis - closedMillis >= onTime){
        blinkState = 1;
        openMillis = millis();
     }   
  }
  else{   
     digitalWrite(pin,LOW);
     if (currentMillis - openMillis >= offTime){
        blinkState = 0;
        closedMillis = millis();
    }
 }
tStatus.blinkState = blinkState;
tStatus.closedMillis = closedMillis;
tStatus.openMillis = openMillis;
return tStatus;
}

However, the code cannot be scaled easily, for many LED outputs. Ideally I'd just like to copy and paste one line of code i.e. the blinkModel function and edit the inputs accordingly.

Would love to hear any ideas on making it more efficient please. I've been looking at it too long and have become stuck.

Thanks :slight_smile:

"just one line to scale" is some kind of tricky, but I hope this gives you an idea, how to blink some LEDs

/* Blink a LED in a specific rhythm object orianted programing

  Turns on and off light emitting diodes (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

*/

// Variante: 3 Blinkled objects in an array



class BlinkLed {
    byte state = 1;                    // 0 off, 1 blink
    unsigned long previousMillis;      // last blink timestamp
    uint16_t on = 180;
    uint16_t off = 320;                // 180/320 is according ECE
    const byte ledPin;                 // a GPIO for the LED

  public:
    BlinkLed(byte attachTo, uint16_t _on = 180, uint16_t _off = 320):
      ledPin(attachTo),
      on (_on),
      off (_off)
    {}
    void begin() {
      pinMode(ledPin, OUTPUT);
    }
    void set(uint16_t _on, uint16_t _off) { // modify on/off times during runtime
      on = _on;
      off = _off;
    }
    void setState(byte _state) {           // 1 Switch on blinking; 0 switch off blinking
      state = _state;
    }
    void tick() {
      if (state) {
        uint32_t currentMillis = millis();
        if (digitalRead(ledPin) && currentMillis - previousMillis >= on) {
          previousMillis = currentMillis;            // save the last time you blinked the LED
          digitalWrite(ledPin, LOW);
        }
        else if (!digitalRead(ledPin) && currentMillis - previousMillis >= off) {
          previousMillis = currentMillis;           // save the last time you blinked the LED
          digitalWrite(ledPin, HIGH);
        }
      }
    }
};




BlinkLed led[] = {          // Array mit den 3 Objekten ... das ist das einzige "komplizierte" hier hierinnen
  BlinkLed(1),
  BlinkLed(2),
  BlinkLed(13)
};

//

void setup() {
  Serial.begin(115200);    //only used for debug

  //for (byte i = 0; i < 3; i++)         //anstatt 3 x die setup methode hinzuschreiben, nutzen wir das Array mit den Objekten, das Muster brauchen wir eh noch ein paar Mal.
  //{
  //  led[i].begin();    // each LED object has to be called once in the setup
  //}

  for (auto &i : led)
  {
    i.begin();    // each LED object has to be called once in the setup
  }

  // set a different on or off time

  led[0].set(400, 500);       // Ich lass die Offzeit auf 320ms, im Prinzip kann man damit aber gleich umgehen



}

void loop() {

  //for (byte i = 0; i < 3; i++)  // if you know exactly how many LEDs you have
  //{
  //   led[i].tick();             // each LED object has to be called once in the loop
  //}

  for (auto &i : led)
  {
    i.tick();                     // each LED object has to be called once in the loop
  }

  // unused methods in this sketch:
  // led[0].setState(0);   // switch blinking led 0 off
  // bzw:
  // led[0].setState(1);   // switch on blinking led 0

  //
  // put here other code which needs to run:
  //
}

basically you will need a member/object of your class for each led
call the method begin for each object in the setup()
call the method tick for each object in loop. It will check if something is to do for the object.

The other method is just in case if you want to turn on/off blinking a LED.

To make it easy, you can put the calls in a for loop.
To make it even easier - you put the led objects in an array so you don't need to adopt the for loop if you add more LEDs.

If you want to add an LED on PIN 8, just at the

BlinkLed(8),

Is that "upscaling" enough?

Thanks for the detailed reply. Certainly seems like what I'm looking for, I'll give it a go.

Also, someone PM me to point out Adafruit also has a solution here

Can scale with two lines of code for each LED.

Cheers!

This may be of interest PinToggle library

This sketch is written for beginners so has no class but does show how to time leds with faster, smaller, 16-bit unsigned values. AVR is an 8-bit machine so figure 16-bit math runs faster than 32. If you want uneven blink then you need 1 start time and 2 intervals per led, a byte for state and one for pin # == 8 bytes per led. How's that scale? 16-bit millis can time over 65 seconds. Who blinks a led for over a minute?

// add-a-sketch_multi_blinkers 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// 1 bug removed, 1/1/19 

// multi_blinkers vars
const byte blinkers = 2;
byte blinkerPin[ blinkers ] = { 2, 3 };
byte blinkerState[ blinkers ];
word startBlink[ blinkers ];
word waitBlink[ blinkers ] = { 1000, 250 }; // 2 millis timers per blinker
// type word (16 bit unsigned) good to 65,535. as millis it is > 1 minute.

// multi_blink switching vars
byte blinkCounter[ blinkers ], lastBlinkerState[ blinkers ]; 
byte switchBlinks[ blinkers ] = { 5, 8 }; // blink X times then pause   
word startOffTime[ blinkers ];
word waitOffTime[ blinkers ] = { 5000, 8000 }; // pause millis

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Blink Led, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch shows a led blink task turned on-off by another task." ));

  for ( byte i = 0; i < blinkers; i++ )
  {
    pinMode( blinkerPin[ i ], OUTPUT );
  }
}

void MultiBlinkers() // you change the blink by changing blinkWait[ blinker ]
{
  static byte index; // static vars keep their data. this 'index' is for this function
  
  if ( waitBlink[ index ] > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink[ index ] >= waitBlink[ index ] )
    {   // difference in time given by subtracting start from end
      blinkerState[ index ] = !blinkerState[ index ];  // ! is logical NOT, make opposite
      digitalWrite( blinkerPin[ index ], blinkerState[ index ] ); 
      startBlink[ index ] += waitBlink[ index ]; // next blink starts when it should.
    }
  }
  else if ( digitalRead( blinkerPin[ index ] ) > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( blinkerPin[ index ], LOW ); //  if it's on, turn the led is OFF
  } 

  // this adds 1 to index then compares to blinkers, keeps index inside the array 
  if ( ++index >= blinkers )    index = 0; // if at array end, loop around to start
  // note that only 1 blinker is checked per call. Don't Block, make loop run fast.
}

void MultiBlinkSwitcher()
{
  static byte index; // static vars keep their data. this 'index' is for this function

  // this task pauses the blinker after X blinks then turns blinking back on, repeat.
  if ( waitBlink[ index ] > 0 ) // while the led is blinking
  {
    if ( blinkerState[ index ] != lastBlinkerState[ index ] ) // blink has just transitioned
    {
      if ( blinkerState[ index ] == 0 ) // count a blink only if turned off, blink is finished
      {
        blinkCounter[ index ]++;
        if ( blinkCounter[ index ] == switchBlinks[ index ] ) // X blinks then none for 3 seconds
        {
          blinkCounter[ index ] = 0; // the if has the ++ first, count stats at 1.
          waitBlink[ index ] = 0; // turn the blinking off, start off timing
          startOffTime[ index ] = millis(); // set start to off time
        }
      }
      lastBlinkerState[ index ] = blinkerState[ index ];
    }
  }
  else // this only runs when blinking is off
  {
    // word( millis()) -- gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startOffTime[ index ] >= waitOffTime[ index ] )
    {
      startBlink[ index ] = millis();
      waitBlink[ index ] = 250;
    }
  }

  byte rtn = index;
  if ( ++index >= blinkers )    index = 0; // if at array end, loop around to start
}

void loop()
{
  // this is the interruptable blink task
  MultiBlinkers(); // sets 'now' only when blinking
  // this task pauses the blinker after 5 blinks then turns blinking back on, repeat.
  MultiBlinkSwitcher();
}

If you think matrix then think it won't be terribly bright. better to use shift registers or led drivers on SPI bus.