LED blink without delay from function

Hi folks,
after a few days of trying myself and crawling through Google and the forum, it's time to ask you guys for help.
We all know the way how blink without delay works.
I wanted to further expand this with a function which has a few more features:

void blink(int count, int ms, char color);

Basically, the function should let an LED blink for 'count' times, with an 'ms' interval without interrupting / halting the loop function. Don't mind the 'color' variable here.

I went through this post which, unfortunately, didn't bring me further.
Here's my function code:

void blink(int count,int ms, char color) {
	static long ledCurrentMillis = millis();
	static long ledPreviousMillis = 0;
	Serial.print ("blink function triggered. Blinking ");
	Serial.print (count);
	Serial.println (" times.");
	for (int i = 0 ; i < count ; i++) {
		if (ledCurrentMillis - ledPreviousMillis > ms) {
				ledOff();
			} else {
				setLedColor(color);
				led.show();
			}
			ledPreviousMillis = ledCurrentMillis;
	}
}

Remark: I'm using the Adafruit ws2812b lib,
I'm sure I'm messing something up with the timing / millis, but I can't wrap my head around it.
The LED lights up for a fraction of a second and remains turned off after the first blink.

I would highly appreciate if you guys could help me out in this topic.
Thanks,
Paul

try to think about what happens in there, esp. the if part. look at the variables and what will change them

 for (int i = 0 ; i < count ; i++) {
		if (ledCurrentMillis - ledPreviousMillis > ms) {
				ledOff();
			} else {
				setLedColor(color);
				led.show();
			}
			ledPreviousMillis = ledCurrentMillis;
	}

(a non blocking code should not have a loop anyway)

You probably need an extra parameter that will 'activate' the blinking and then call the function just to maintain the blinking (ie prepare the environment and then just do the blinking until all blinks are done)

here is an example with info displayed in the Serial console @115200 bauds

bool blink(uint32_t nbBlinks, uint32_t halfPeriod, bool forceStart = false)
{
  static  uint32_t lastChrono;
  static uint32_t count;
  if (forceStart) {
    lastChrono = millis() - halfPeriod - 1;
    count = 0;
    Serial.println(F("STARTING, LED OFF"));
  }
  if (count >> 1 >= nbBlinks) return true; // we are done
  if (millis() - lastChrono >= halfPeriod) {
    lastChrono += halfPeriod;
    if (++count & 0b1) Serial.print(F("LED ON : #"));
    else Serial.print(F("LED OFF: #"));
    Serial.println(count);
  }
  return false; // still blinking
}


void setup() {
  Serial.begin(115200);
  blink(10, 500, true); // true --> prime the blinking
}

void loop() {
  if (blink(10, 500)) { // no need for last parameter as it's optional with default value false (don't force start)
    Serial.println(F("Blinking ended"));
    while (true); // we are done
  }
}

Hello,

Here is a small class I made for a project. Of course it's not exactly what you want, but you can easily adapt it to your needs

class LedXY
{
  public :

    LedXY();

    void attach( uint8_t & led );
    void on();
    void off();
    void update();

    void setOnColor     ( uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a = 255 );
    void setOffColor    ( uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a = 255 );
    void setOnColorRGB  ( uint32_t const rgb,  uint8_t const a = 255 );
    void setOffColorRGB ( uint32_t const rgb,  uint8_t const a = 255 );
    void setOnColorRGBA ( uint32_t const rgba, uint8_t const a =   0 );
    void setOffColorRGBA( uint32_t const rgba, uint8_t const a =   0 );
    void calcOffColor   ( uint8_t const alpha = 75 );

    void blink( uint16_t const * const pattern, size_t const length, int8_t const count = -1 );
    void blink( uint16_t const timeOn = 1000, uint16_t const timeOff = 1000, int8_t const count = -1 );
    void flash( uint16_t const timeOn );

    void setBlinkFinishedHandler( void (* func)( LedXY const & led ) );

    bool operator == ( LedXY const & obj ) const { return this == &obj; }
    bool operator != ( LedXY const & obj ) const { return this != &obj; }
    LedXY( LedXY const & ) = delete;
    LedXY & operator = ( LedXY const & ) = delete;
    void * operator new( size_t ) = delete;
    void * operator new[]( size_t ) = delete;

  private :

    uint8_t *        m_ledPtr;
    bool             m_isOn;
    uint8_t          m_colors[2][3];
    uint32_t         m_millis;
    uint16_t         m_time;
    int8_t           m_count;
    uint16_t const * m_patternPtr;
    uint16_t         m_pattern[2];
    size_t           m_patternLength;
    size_t           m_patternIndex;

    void setState    ( bool const on );
    void updateColor ( bool const on );
    void setColor    ( bool const on, uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a = 255 );
    void setColorRGB ( bool const on, uint32_t const rgb,  uint8_t const a = 255 );
    void setColorRGBA( bool const on, uint32_t const rgba, uint8_t const a = 0 );

    void (*m_blinkFinishedHandler)( LedXY const & led );
};

LedXY::LedXY() :
  m_ledPtr              ( nullptr ),
  m_isOn                ( false ),
  m_colors              { { 255, 255, 255 }, { 0, 0, 0 } },
  m_millis              ( 0 ),
  m_time                ( 0 ),
  m_count               ( 0 ),
  m_patternPtr          ( nullptr ),
  m_pattern             { 0, 0 },
  m_patternLength       ( 0 ),
  m_patternIndex        ( 0 ),
  m_blinkFinishedHandler( nullptr )
{
}

void LedXY::attach( uint8_t & led )
{
  m_ledPtr = &led;
  updateColor( false );
}

void LedXY::setState( bool const on )
{
  if ( m_patternLength > 1 && m_blinkFinishedHandler != nullptr )
  {
    m_blinkFinishedHandler( *this );
  }

  m_count = 0;
  m_patternPtr = nullptr;
  m_patternIndex = 0;
  m_patternLength = 0;
  m_isOn = on;
  updateColor( on );
}

void LedXY::on()  { setState( true  ); }
void LedXY::off() { setState( false ); }

void LedXY::updateColor( bool const on )
{
  if ( m_ledPtr != nullptr )
  {
    uint8_t const i = on ? 0 : 1;

    for ( uint8_t j = 0; j < 3; j++ )
    {
      m_ledPtr[j] = m_colors[i][j];
    }
  }
}

void LedXY::setColor( bool const on, uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a )
{
  uint8_t const i = on ? 0 : 1;

  if ( a != 255 )
  {
    float const m = a / 255.0;
    m_colors[i][0] = uint8_t( r * m );
    m_colors[i][1] = uint8_t( g * m );
    m_colors[i][2] = uint8_t( b * m );
  }
  else
  {
    m_colors[i][0] = r;
    m_colors[i][1] = g;
    m_colors[i][2] = b;
  }
}

void LedXY::setColorRGB( bool const on, uint32_t const rgb, uint8_t const a )
{
  uint8_t const * const c = (uint8_t *) &rgb;
  setColor( on, c[2], c[1], c[0], a );
}

void LedXY::setColorRGBA( bool const on, uint32_t const rgba, uint8_t const a )
{
  uint8_t const * const c = (uint8_t *) &rgba;
  setColor( on, c[3], c[2], c[1], a == 0 ? c[0] : a );
}

void LedXY::setOnColor     ( uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a ) { setColor( true,  r, g, b, a ); }
void LedXY::setOffColor    ( uint8_t const r, uint8_t const g, uint8_t const b, uint8_t const a ) { setColor( false, r, g, b, a ); }
void LedXY::setOnColorRGB  ( uint32_t const rgb, uint8_t const a )  { setColorRGB ( true,  rgb,  a ); }
void LedXY::setOffColorRGB ( uint32_t const rgb, uint8_t const a )  { setColorRGB ( false, rgb,  a ); }
void LedXY::setOnColorRGBA ( uint32_t const rgba, uint8_t const a ) { setColorRGBA( true,  rgba, a ); }
void LedXY::setOffColorRGBA( uint32_t const rgba, uint8_t const a ) { setColorRGBA( false, rgba, a ); }
void LedXY::calcOffColor   ( uint8_t const alpha ) { setColor( false, m_colors[0][0], m_colors[0][1], m_colors[0][2], alpha ); }

void LedXY::setBlinkFinishedHandler( void (*func)( LedXY const & led ) ) { m_blinkFinishedHandler = func; }

void LedXY::blink( uint16_t const * const pattern, size_t const length, int8_t const count )
{
  if ( count != 0 && length > 0 && pattern != nullptr )
  {
    m_isOn = true;
    updateColor( true );
    m_millis = millis();
    m_count = count;
    m_patternPtr = pattern;
    m_patternLength = length;
    m_patternIndex = 0;
    m_time = m_patternPtr[0];
  }
}

void LedXY::blink( uint16_t const timeOn, uint16_t const timeOff, int8_t const count )
{
  m_pattern[0] = timeOn;
  m_pattern[1] = timeOff;
  blink( m_pattern, 2, count );
}

void LedXY::flash( uint16_t const timeOn )
{
  m_pattern[0] = timeOn;
  blink( m_pattern, 1, 1 );
}

void LedXY::update()
{
  if ( m_count != 0 && millis() - m_millis >= m_time )
  {
    if ( m_patternLength == 1 )
    {
      setState( false );
    }
    else if ( ++m_patternIndex < m_patternLength )
    {
      m_time = m_patternPtr[ m_patternIndex ];
    }
    else
    {
      if ( m_count != -1 )
      {
        m_count--;
      }
      
      if ( m_count == 0 )
      {
        setState( false );
      }
      else
      {
        m_patternIndex = 0;
        m_time = m_patternPtr[0];
      }
    }

    if ( m_count != 0 )
    {
      m_millis = millis();
      m_isOn = !m_isOn;
      updateColor( m_isOn );
    }
  }
}

It's meant to be used like this for example:

...
LedXY myLed;

void onBlinkFinished( LedXY const & led )
{
  if ( led == myLed )
  {
    Serial.println( "myLed finished blinking" );
  }
}
...
void setup()
{
  ...
  myLed.setOnColor( 255, 0, 0 );
  myLed.setOffColor( 255, 255, 255, 75 );
  myLed.setBlinkFinishedHandler( onBlinkFinished );

  static const uint16_t blinkPattern[] = { 250, 750, 250, 750, 250, 4750 };
  myLed.blink( blinkPattern, sizeof(blinkPattern)/sizeof(blinkPattern[0]) );
  ...
}

void loop()
{
  ...
  myLed.update();
  ...
}

non-blocking means "always" executing

function loop does the looping.
IMOH the easiest way for blinking a limited number of times is to setup a variable to the number of blinks

not ready to use code but the basic principle with comments that try to explain the functionality

my_N_Time_Blink_function (int no_of_blinks, boolean mode) {

  // execute this function with every iteration of loop
  static int blink_Count
  
  if (mode == stop) {
    blink_Count = 0;
  }
  
  if (blink_Count == 0 && mode == start){
     blink_Count = no_of_blinks * 2; // each "ON" and each "OFF" decrement by 1
  }

  // but only to blinking if condition is true
  if (blink_Count > 0) {
  //do one switching ON/OFF 
   blink_Count--
  } 
}

this code needs some gloabel variables to do the start and stop

void loop() {
  my_N_Time_Blink_function (10,myMode);  
  //..

where variable mode is sometime set to a value that represents "start" and sometimes to a vlaue that represents "run"

best regards Stefan

Check out my example of non-blocking LedSequencing
https://www.forward.com.au/pfod/ArduinoProgramming/TimingDelaysInArduino.html#led

J-M-L:
You probably need an extra parameter that will 'activate' the blinking and then call the function just to maintain the blinking (ie prepare the environment and then just do the blinking until all blinks are done)

Awesome, that helped me out, I got it working how I want it.
I bet it would be more consistent to write a lib for such things, but I'll take one step after another.
Of course a big thanks to everyone who helped out!

Glad it helped!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.