1

Arduino community. I have had experience with Arduino for 3 years now and have finally come to the conclusion to get rid of the delay function entirely.

Below is the code to blink two LEDs a complete 10 times. The problem is that the setup() doesn't repeat like a loop(), which means it doesn't last a long enough to find a time difference between PreviousMillis and CurrentMillis because it runs once; thus, defeating the idea of blinking a LED in the setup(). But, I have researched ways to imitate the loop() with while loops; I have used Adafruit's method to spice things up(Constructors and classes!), https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview.

But I need a little help because it doesn't work, instead, it blinks forever(Mission half-complete =)).

Notice how I have used while loops in the Update(), which is located within the Flasher Class, to mimic a loop() until the condition becomes true.

The problem is, I don't know why this code doesn't work and what to do from here.

The ultimate goal is to blink the LEDs at pin 12 and pin 13 a total of 10 Times, instead of repeatedly blinking for an eternity. I am using an Arduino Uno R3 and a Serial baud rate of 115200.

Ignore what I did in the loop(). It's the same function as the setup() code, instead, the led blinks an eternity (unfortunately), when a value of "1" is typed.

    class Flasher{
    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time

    // These maintain the current state
    int ledState;                 // ledState used to set the LED
    unsigned long previousMillis;   // will store last time LED was updated
    unsigned long currentMillis = 0;

  public: Flasher (int pin, long on, long off)    
{            // Constructor - creates a Flasher
    // and initializes the member variables and state


      ledPin = pin;
      pinMode(ledPin, OUTPUT);

      OnTime = on;
      OffTime = off;

      ledState = LOW;
      previousMillis = 0;
    }

    void Update(int a){
      // check to see if it's time to change the state of the LED
      for (int i = 0; i < a; i++) {
        while ((currentMillis - previousMillis <= OnTime) || ((currentMillis - previousMillis <= OffTime))) {
          currentMillis = millis();

          if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
          {
            ledState = LOW;  // Turn it off
            previousMillis = currentMillis;  // Remember the time
            digitalWrite(ledPin, ledState);  // Update the actual LED
          }
          else if ((ledState == LOW) && (currentMillis - previousMillis >=OffTime))
          {
            ledState = HIGH;  // turn it on
            previousMillis = currentMillis;   // Remember the time
            digitalWrite(ledPin, ledState);   // Update the actual LED
          }
        }
      }
    }
};


Flasher led1(12, 100, 400);
Flasher led2(13, 350, 350);

void setup(){

  Serial.begin(115200);
  led1.Update(10);

}

void loop(){

   /* if (Serial.available()) {
      String str = Serial.readString();

      if (str.substring(0, 1).equals("1")) {
        led1.Update(10);

      }
  }*/
}

4 Answers 4

1

SimpleTimer and similar millis()-based timer libraries are made for just this kind of job. SimpleTimer even allows you to specify a number of repetitions as well. The gist of it is you set a timer to an interval and perhaps a number of repetitions, and a callback function to be called at the end of each interval. Your main code just calls the timer's update function, "run()" in this case, very frequently. Your callback function will be called at the appropriate times. This is one of the easiest ways to implement this kind of job, mimicking foreground/background but without the complexity of interrupts and interrupt service routines.

1

I've modified your sketch so that when you instantiate a LED Flasher object with a 4th argument, it flashes forever. If you instantiate a Flasher object with 3 arguments, use the Start() function to start the LED flashing the number of times you specify. Just call it once, not repeatedly in the loop() function (e.g. when a button is pressed). You can call the Start() function again and specify a different number of flashes. You can have multiple LED flasher objects. Each object can have different on/off times. Some LEDs can flash forever, and some the number of times specified.

EDIT

I've added a few functions in the class to make it more versatile: Stop(), SetPinHighTime(), SetPinLowTime() and SetFlashForever().

class Flasher{

  // Class Member Variables. These are initialized at startup.
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  // Constructor. Create a LED Flasher object.
  // Initialize the member variables and LED state.
  public:

  Flasher(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }

  void Update(){

    // Check to see if it's time to change the state of the LED.
    unsigned long currentMillis = millis();

    if((ledState == HIGH) && (currentMillis - previousMillis >= highTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = LOW;
      previousMillis = currentMillis;        // Remember the time.
      digitalWrite(ledPin, ledState);        // Update the actual LED.
      if(flashCounter > 0){flashCounter--;}  // Update the flash counter.
    }
    else if((ledState == LOW) && (currentMillis - previousMillis >= lowTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = HIGH;
      previousMillis = currentMillis;        // Remember the time.
      digitalWrite(ledPin, ledState);        // Update the actual LED.
      if(flashCounter > 0){flashCounter--;}  // Update the flash counter.
    }
  }

  void Start(unsigned int flashNumberOfTimes){
    flashCounter = flashNumberOfTimes * 2;
    flashForever = 0;
    ledState = LOW;
    digitalWrite(ledPin, ledState);
  }

  void Stop(){
    Start(0);
  }

  void SetPinHighTime(unsigned long pinHighTime){
    highTime = pinHighTime;
  }

  void SetPinLowTime(unsigned long pinLowTime){
    lowTime = pinLowTime;
  }

  void SetFlashForever(byte oneOrZero){
    flashForever = oneOrZero;
    if(oneOrZero == 0){Start(0);}
  }

  void SetPinToInput(){
    pinMode(ledPin, INPUT);
  }

};

// Used for testing purposes only.
byte doOnce = 0;
const byte led1PinNumber = 2;
const byte led2PinNumber = 3;
const byte led3PinNumber = 4;

// Instantiate LED Flasher Object(s).
// e.g. Flasher object_name(Pin Number, ON Time, OFF Time, OPTIONAL Flash Forever);

// Three argument constructor.
// The number of times to flash the LED is specified
// when you call the Start() function. The LED will NOT
// start flashing until you call Start().
Flasher led1(led1PinNumber, 10, 990);
Flasher led2(led2PinNumber, 100, 400);

// Four argument constructor.
// Flash an LED forever. This will start the LED flashing immediately.
Flasher led3(led3PinNumber, 1000, 1000, 1);

void setup(){}

void loop(){

  // Call the Update function as fast as possible.
  led1.Update();
  led2.Update();
  led3.Update();

  // Test output. Flash 3 LEDs with different on/off times.
  // Use the Start() function to flash 2 of the LEDs X number
  // of times, then restart each LED flasher object with
  // a different number of flashes. Set 1 LED to flash forever,
  // then stop it and start a different LED flashing forever,
  // altering the flash rates of one LED before restarting it.
  // Finally, set the 3 LED pins to INPUT.

  // Start the led1 object 4 seconds after Arduino startup.
  if(millis() > 4000 && millis() < 5000 && doOnce == 0){
    led1.Start(3);  // Flash the LED 3 times.
    doOnce = 1;
  }

  // Start the led2 object 5 seconds after Arduino startup.
  if(millis() > 5000 && millis() < 6000 && doOnce == 1){
    led2.Start(5);  // Flash the LED 5 times.
    doOnce = 0;
  }

  // Restart the led1 object 10 seconds after Arduino startup.
  if(millis() > 10000 && millis() < 11000 && doOnce == 0){
    led1.Start(7);  // Flash the LED 7 times.
    doOnce = 1;
  }

  // Restart the led2 object 15 seconds after Arduino startup.
  if(millis() > 15000 && millis() < 16000 && doOnce == 1){
    led2.Start(9);  // Flash the LED 9 times.
    doOnce = 0;
  }

  // Stop flashing the led3 object.
  // Start flashing the led1 object forever.
  if(millis() > 30000 && millis() < 31000 && doOnce == 0){
    led3.SetFlashForever(0);
    led1.SetFlashForever(1);
    doOnce = 1;
  }

  // Stop flashing the led1 object.
  // Start flashing the led3 object forever and change
  // it's flash rate.
  if(millis() > 50000 && millis() < 51000 && doOnce == 1){
    led3.SetPinHighTime(250);
    led3.SetPinLowTime(500);
    led3.SetFlashForever(1);
    led1.SetFlashForever(0);
    doOnce = 0;
  }

  // Stop flashing the led3 object. No LEDs should be flashing.
  if(millis() > 60000 && millis() < 61000 && doOnce == 0){
    led3.Stop();
    doOnce = 1;

    // My test shield has one High and one Low LED per pin.
    // Set the pins to input to turn off the Low LEDs.
    led1.SetPinToInput();
    led2.SetPinToInput();
    led3.SetPinToInput();
  }

}
7
  • From pure C++ point of view, this implementation might be correct, however, 1) for Arduino, setup() get call in Arduino main() after some initialisation of hardware, it is why Arduino advocate to have initialise hardware with a begin() method than using the construct (it happened before the initialisation of the hardware).
    – hcheung
    Oct 25, 2021 at 1:16
  • 2) For C++, if your implementation of class methods within the Class, by default, all the methods would be considered as inline methods. what this means is that for example the update() will be inserted as inline at where it get called. It is therefore necessary to separate the Class prototype from implementation.
    – hcheung
    Oct 25, 2021 at 1:22
  • @hcheung - The constructor takes care of initializing the required variables when the object(s) are instantiated. If you could answer the OP's question with the code required to implement the begin() function, I would up-vote such an answer.
    – VE7JRO
    Oct 25, 2021 at 1:36
  • @hcheung - 2) "It is therefore necessary to separate the Class prototype from implementation. - Says who? I can write "Arduino code" any way I like, so long as it compiles, is the smallest compile size, and works perfectly. I think the compiler is smarter than me, and can take code written by millions of different users world wide, and make it work just fine :) Feel free to answer the question with a better answer.
    – VE7JRO
    Oct 25, 2021 at 1:47
  • Well, I didn't say it doesn't work, and for Arduino, people may not care about the size of the code much either, in fact, most of Arduino users think of learning C++ is optional. It is not part of compiler optimisation, it is how C++ is defined. "A class's member functions can be declared inline, either by using the inline keyword or by placing the function definition within the class definition". You can check any book about C++ from Bjarne Stroustrup.
    – hcheung
    Oct 25, 2021 at 2:14
0

For such a simple task, the code is very complex.

First, write a piece of code that blinks the LEDs if sufficient passage of time has been met.

You can implement a blink counter, either in the main loop or the blink routine mentioned earlier.

Should be fairly simple.

edit: here is how I would have implemented it.

//blink the led for a given number of times
//cnt: number of times of the blinks needed
uint8_t led_blinky(uint8_t cnt) {
    static uint8_t led_cnt=0;       //blinky count
    static uint32_t millis_prev=0;  //previous millies

    if (led_cnt>=cnt) return led_cnt;   //return blink count
    if (millis() > millis_prev + LED_ms) {    //enough time has passed
        millis_prev += LED_ms;                      //update the time
        digitalWrite(LED, (digitalRead(LED)==HIGH)?LOW:HIGH);       //flip led
        led_cnt+=1;                 //increment the led count
    }
    return led_cnt;                 //return blinky count
}

the code itself is fairly self-explanatory -> the only (marginally) tricky part is how it maintains timing accuracy over a long period of time. in this particular implementation, the test is done inside the led_blinky() routine so it is self-contained -> it does return the number of blinks that have been executed.

Here is the execution of performing 100ms blinks 4 times:

enter image description here

-1

I'd like to proposes a couple of changes to @VE7JRO's implementation.

For C++, it is perfectly normal to initialise an Class instance via its construct, however, for Arduino, the setup() get call in Arduino main() after some initialisation of the hardware, including pin definition, etc., it is why Arduino advocates in Arduino Style Guide for Writing Libraries to use a begin() to initialize a library instance (it is perfectly okay to use the construct to setup some variables but not for something like pinMode() in the construct). Because very often the creating of a Class instance happened before the setup() get called.

With this in mind, the propose changes:

class Flasher{

  // Class Member Variables. These are initialized at begin().
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  public:

  Flasher() {}
  
  begin(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }
  ....
};

To use the Class:

Flasher led1;
Flasher led2;
Flasher led3;

setup() {
  led1.begin(led1PinNumber, 10, 990);
  led2.begin(led2PinNumber, 100, 400);
  led3.begin(led3PinNumber, 1000, 1000, 1);
}

Another change has to do how C++ defined inline function or method. "A class's member functions can be declared inline, either by using the inline keyword or by placing the function definition within the class definition". What this means is that all the functions defined in the Flasher class in this case are implied as inline methods. so when led.update() get call, it is placed as inline code by the compiler at where it get call (and multiple times). There are certain methods that are perfectly okay and in fact should be inline, for example, SetPinHighTime(), and SetPinToInput() would actually benefit from being inline, but you will probable don't want the update() method to be inline. In order to do that, create a Class prototype and have the methods that you'd want to be inlined implemented within the Class definition, but separate the implementation of update() method.

#ifndef FLASHER_
#define FLASHER_
#include "Arduino.h"

class Flasher{

  // Class Member Variables. These are initialized at startup.
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  public:

  // All methods are inline, except update()

  Flasher() {}
  void begin(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }

  void Update();

  void Start(unsigned int flashNumberOfTimes){
    flashCounter = flashNumberOfTimes * 2;
    flashForever = 0;
    ledState = LOW;
    digitalWrite(ledPin, ledState);
  }

  void Stop(){ Start(0); }

  void SetPinHighTime(unsigned long pinHighTime){
    highTime = pinHighTime;
  }

  void SetPinLowTime(unsigned long pinLowTime){
    lowTime = pinLowTime;
  }

  void SetFlashForever(byte oneOrZero){
    flashForever = oneOrZero;
    if(oneOrZero == 0){Start(0);}
  }

  void SetPinToInput(){ pinMode(ledPin, INPUT); }

};
#endif

Define update() method separately in implementation Flasher.cpp:

#include "Flasher.h"

Flasher::update() {

    // Check to see if it's time to change the state of the LED.
    unsigned long currentMillis = millis();

    if((ledState == HIGH) && (currentMillis - previousMillis >= highTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = LOW;
      previousMillis = currentMillis;        // Remember the time.
      digitalWrite(ledPin, ledState);        // Update the actual LED.
      if(flashCounter > 0){flashCounter--;}  // Update the flash counter.
    }
    else if((ledState == LOW) && (currentMillis - previousMillis >= lowTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = HIGH;
      previousMillis = currentMillis;        // Remember the time.
      digitalWrite(ledPin, ledState);        // Update the actual LED.
      if(flashCounter > 0){flashCounter--;}  // Update the flash counter.
}

I hope this will help to make the class better and better understanding on C++ and Arduino, and next time when you read the source of an Arduino library, you had better understanding on why the library implement its class and methods the way it does.

1
  • So, you took my 3 lines of code used to instantiate the 3 objects (via the constructor), added 3 more lines to it, which accomplishes the same thing as the original 3 lines did. Did you compare the compile size / memory usage for your method VS my method? -1 for taking simple code and making it more complex.
    – VE7JRO
    Oct 25, 2021 at 3:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.