Status LED function needed

I need a function to blink LED status codes such as you find on lots of electronic equipment today. For example, constantly fast blinking LED means the program is running OK without problems (heartbeat), two blinks followed by a pause means, for example, status (or error) '2' has occurred, five blinks followed by 3 blinks means error '53' has occurred, etc. I would like the code to be compact and not use any delays (similar to the Blink Without Delay example). I figure a function such as: void StatusLED(codenumber). Something similar to: MEI VN 2512 Bill Acceptor - LED Status Codes - YouTube

I have been fiddling and googling all afternoon without success so far. Anyone able and willing to help me?

1 Like

Take a look at the Multiblink example in the Playground - it lets you set up the patterns in a table and you should be able to modify it to do what you need.

There was also an updated version here http://arduino.cc/forum/index.php/topic,120100.0.html that was more flexible, but you probably don't need the flexibility.

Thanks Marco but I guess I didn't explain myself well. I don't want many LEDs. I don't want lots of delay()'s. Just one LED to display status.

There really is no standard on blinking LED for status code. You'd need to set up the blink pattern and timing, and you can make them in any way you want or to match existing code of the product you're mimicing.

I've seen ways to make LED blink at specific pattern and time without using delay so the uC can still do other things such as read button(s) and control other LED(s)

Thanks wilykat but I'm not looking for a standard. I'll make up my own codes.

I've seen ways to make LED blink at specific pattern and time

Do you remember where you saw these ways? I can't find anything.
I thought this forum would be full of LED blinking experts. Maybe this is a programming problem rather than a LED hardware question.

Here is the function I have come up with. Now I just have to get rid of the delay()'s so the processor is not wasting time and can do other things.

//function to blink LED status code
void blinkLED(int code)
{
  for(int i = 0; i < code; i++)
    {
    	digitalWrite(ledPin, HIGH);
    	delay(200);
    	digitalWrite(ledPin, LOW);
    	delay(200);
    }
    delay (1000);
}

Usage would be like this:

void loop()
{
    blinkLED(4);
}
1 Like

It may be better in the Programming section, but LED blinking seems to take up a lot of discussion time in both forums (fora?).

Actually, I think that your problem of blinking one LED is just a simpler version of blinking many LEDs.

Once you want to 'multitask' the Arduino, you need to increase the level of complexity of the code to eliminate the delay functions that abound in beginner code. You need to code using techniques that allow you to leave a function or task and then come back to it to continue later. Often this is implemented as a Finite State Machine. One important principle is that you need to separate the data (your pattern) from the code that implements it, so you can define many patterns and have one piece of general code that does what you want.

For me, MultiBlink would work for you as long as you define your own pattern tables and call the function with the right table as a parameter. For what it's worth, here is a version of my code that can display different patterns depending on what what pattern is selected.
Start by looking at loop() and the definition of table T1 and T2. Don't worry about the detailed implementation of the MulitBink function at this stage. Your code should select the table according to the error it wants to display and your tables (similar to T1/T2) will all have one line in them. If you want to you could even edit the tables using code - for example, if all you codes are 2 digits, then one line in one table that displays 2 digits is enough (say n short flashes with a long pause and then m other flashes and a longer pause), and the number of LED flashes is just defined in the looping parameter for each digit.

header file:

// Multi_Blink2.h
//
// Blink lots of LEDs at diffreent frequencies simultaneously
//
// Header file is required to be able to define the structured types
//
#include <Arduino.h>

#ifndef  MULTIBLINKH
#define  MULTIBLINKH

// State values for FSM
#define  MB_NULL     0
#define  MB_ON       1
#define  MB_OFF      2
#define  MB_FADE_ON  3
#define  MB_FADE_OFF 4    // stateDef.data is the start and end PWM Setting - use FADE_* macros
#define  MB_LOOP     5    // stateDef.data is the next state position and counter setpoint, use LOOP_* macros

#define  CTR_UNDEF  255

#define  FADE_PARAM(s, e)    (((s) << 8) | (e))
#define  FADE_START_GET(n)   highByte(n)
#define  FADE_END_GET(n)     lowByte(n)

#define  LOOP_PARAM(s, c)    (((s) << 8) | (c))
#define  LOOP_STATE_GET(n)   highByte(n)
#define  LOOP_SP_GET(n)      lowByte(n)

typedef struct
{
  uint8_t  stateID;       // state value for this state to be active (MB_* defines)
  uint16_t activeTime;    // time to stay active in this state in milliseconds
  uint16_t data;          // data store for state context information/parameters (depends on MB_*)
  uint8_t  counter;       // generic counter for the state context information. CTR_UNDEF is special value.
} stateDef;

typedef struct
{
  uint8_t  ledPin;         // Arduino I/O pin number
  uint8_t  currentState;   // current active state
  uint32_t nextWakeup;     // the 'time' for the next wakeup - saves the millis() value
  stateDef state[5];       // the MB_* state definitions. Add more states if required
} ledTable;

#endif

code:

// Multi_Blink2
//
// Blink lots of LEDs at different frequencies simultaneously, include fades, delays and loops in patterns
//
// Marco Colli - September 2012
//
// Demonstrates the way to carry out multiple time based tasks without using the delay() function
// Demonstrates the use of structures (and structures within structures)
// Demonstrates a data driven approach to programming to create compact, reusable code
//

#include "Multi_Blink2.h"  // type definitions

// Blink Table T - Modify this table to suit whatever the output requirements are 
// Add or delete lines as required to achieve the desired effects.
// Have multiple tables and switch between them to create different effects based on external inputs
// To add additional states the structure in the header file needs to be modified
//
ledTable  T1[] =
//Pin  St WUp  State 0              State 1              etc
{ 
  { 3, 0, 0, {{MB_ON, 50, 0, 0},   {MB_OFF, 100, 0 ,0}, {MB_LOOP, 0, LOOP_PARAM(0, 4), 0},  {MB_OFF, 800, 0, 0}, {MB_NULL, 0, 0, 0}}  },
  { 4, 0, 0, {{MB_OFF, 100, 0, 0}, {MB_ON, 50, 0, 0},   {MB_LOOP, 0, LOOP_PARAM(0, 4), 0},  {MB_OFF, 800, 0, 0}, {MB_NULL, 0, 0, 0}}  },
  { 5, 0, 0, {{MB_OFF, 800, 0, 0}, {MB_ON, 50, 0, 0},   {MB_OFF, 100, 0, 0}, {MB_LOOP, 0, LOOP_PARAM(1, 4), 0},  {MB_NULL, 0, 0, 0}}  },
  { 6, 0, 0, {{MB_OFF, 800, 0, 0}, {MB_OFF, 100, 0, 0}, {MB_ON, 50, 0, 0},   {MB_LOOP, 0, LOOP_PARAM(1, 4), 0},  {MB_NULL, 0, 0, 0}}  },
};
ledTable T2[] = 
{
  { 3, 0, 0, {{MB_ON, 20, 0, 0},   {MB_OFF, 50, 0, 0},  {MB_LOOP, 0, LOOP_PARAM(0, 3), 0},  {MB_ON, 250, 0, 0},  {MB_OFF, 450, 0, 0}} },
  { 4, 0, 0, {{MB_OFF, 450, 0, 0}, {MB_ON, 20, 0, 0},   {MB_OFF, 50, 0, 0},  {MB_LOOP, 0, LOOP_PARAM(1, 3), 0},  {MB_ON, 250, 0, 0}}  },
  
  { 5, 0, 0, {{MB_FADE_ON, 45, FADE_PARAM(1, 30), 0}, {MB_ON, 70, 0, 0}, {MB_FADE_OFF, 45, FADE_PARAM(30, 1), 0}, {MB_NULL,0,0,0}, {MB_NULL,0,0,0}} },
  { 6, 0, 0, {{MB_FADE_ON, 10, FADE_PARAM(0, 255), 0}, {MB_ON, 70, 0, 0}, {MB_FADE_OFF, 10, FADE_PARAM(255, 0), 0}, {MB_NULL,0,0,0}, {MB_NULL,0,0,0}} },
};

// Self adjusting constants for loop indexes
#define  MAX_STATE  (sizeof(T1[0].state)/sizeof(stateDef))

void BlinkInit(ledTable *pT, uint8_t tableSize)
{
  for (uint8_t i=0; i < tableSize; i++, pT++)
  {
    pinMode(pT->ledPin, OUTPUT);
    
    pT->nextWakeup = 0;
    digitalWrite(pT->ledPin, LOW);
    for (uint8_t j=0; j<MAX_STATE; j++)
    {
      pT->state[j].counter = CTR_UNDEF;
    }
  }
  return;
}

void MultiBlink(ledTable *pT, uint8_t tableSize)
{
  for (int i=0; i < tableSize; i++, pT++)
  {
      uint8_t  cs = pT->currentState;  // current state shortcut

    // check if the state active time has expired (ie, it is less than current time)
    if (millis() >= pT->nextWakeup)
    {
      pT->nextWakeup = millis() + pT->state[cs].activeTime;

      switch (pT->state[cs].stateID)
      {
        case MB_OFF:
        case MB_ON:    // Write digital value
        {
          digitalWrite(pT->ledPin, pT->state[cs].stateID == MB_ON ? HIGH : LOW);
          pT->currentState = (pT->currentState + 1) % MAX_STATE;
        }
        break;
          
        case MB_FADE_ON:
        {
          // first time in this state? Check the counter
          if (pT->state[cs].counter == CTR_UNDEF)
          {
            pT->state[cs].counter = FADE_START_GET(pT->state[cs].data);
          }

          analogWrite(pT->ledPin, pT->state[cs].counter++);
          
          if (pT->state[cs].counter >= FADE_END_GET(pT->state[cs].data))
          {
            pT->state[cs].counter = CTR_UNDEF; // set up loop counter
            pT->currentState = (pT->currentState + 1) % MAX_STATE;
          }
        }
        break;

        case MB_FADE_OFF:
        {
          // first time in this state? Check the counter
          if (pT->state[cs].counter == CTR_UNDEF)
          {
            pT->state[cs].counter = FADE_START_GET(pT->state[cs].data);
          }

          analogWrite(pT->ledPin, pT->state[cs].counter--);
          
          if (pT->state[cs].counter <= FADE_END_GET(pT->state[cs].data))
          {
            pT->state[cs].counter = CTR_UNDEF; // set up loop counter
            pT->currentState = (pT->currentState + 1) % MAX_STATE;
          }
        }
        break;

        case MB_LOOP:  // loop back to specified state if there is still count left
        {
          // first time in this state? Check the counter
          if (pT->state[cs].counter == CTR_UNDEF)
          {
            pT->state[cs].counter = LOOP_SP_GET(pT->state[cs].data);
          }

          // loop around or keep going?          
          if (pT->state[cs].counter-- > 0)
          {
            pT->currentState = LOOP_STATE_GET(pT->state[cs].data);
            pT->nextWakeup = 0;  // do it immediately
          }
          else 
          {
            pT->state[cs].counter = CTR_UNDEF; // set up loop counter
            pT->currentState = (pT->currentState + 1) % MAX_STATE;
          }
        }
        break;  
          
        default:  // just move on - handles NULL and any stupid states we may get into
        {
          pT->currentState = (pT->currentState + 1) % MAX_STATE;
        }
        break;
      }
    }
  }
  
  return;
}

void setup()
{
  BlinkInit(T1, sizeof(T1)/sizeof(ledTable));
  BlinkInit(T2, sizeof(T2)/sizeof(ledTable));
}

#define  BUTTON_PIN  7  // switch between patterns

void loop()
{
  if (digitalRead(BUTTON_PIN) == LOW)
  {
    MultiBlink(T1, sizeof(T1)/sizeof(ledTable));
  }
  else
  {
    MultiBlink(T2, sizeof(T2)/sizeof(ledTable));
  }    
}

Try this http://arduino-library-syncled.googlecode.com/svn/trunk/SyncLED/ from a thread in programming.

i did something similar. For me it was a heartbeat led that does 1500ms of fastblink when a button is pressed.

might not be the most efficient route as i am still learning. But it works exactly as i wanted it to work.

in abridged code:

void loop(){
heartbeat(heartbeatRate); //amount of time led is on and off are equal

  if (calButton){
    heartbeatRateTemp = heartbeatRate;  //store current heartbeat timer to restore later
    heartbeatRate = .05;  // quicker flash to signal a calibration has happened
    specialFlash = true;  
    specialFlashTimer = millis();

    calButtonPressed = true;  // set variable to true to show button is pressed
    lastPress = millis();  // set lastPressed to current time for debounce purposes
  }

  // if a calibration was performed and the quicker flashes happened for 1.5 seconds
  if (specialFlash && millis() - specialFlashTimer > 1500){ 
    specialFlash = false;
    heartbeatRate = heartbeatRateTemp;
  }

  // if the calibration button is not pressed and more time has passed than the debounceTimer setting, set the variable to false
  if (!calButtonState && (millis()-lastPress) > debounceTimer) calButtonPressed = false; 

}

void heartbeat(double rate){
  // Serial.print(statLEDstate);
  // Serial.println(millis()-statTimer);

  if (millis()-statTimer >= rate*1000){  // time since last flash is greater than rate set then change state of LED
    if (statLEDstate) digitalWrite (statLED, LOW); 
    if (!statLEDstate) digitalWrite (statLED, HIGH);
    statTimer = millis(); //reset timer
    statLEDstate = !statLEDstate;  // change led state variable
  }

This is my way and works great.

//Define pin names.
#define pin_Heartbeat_LED     13

//Setup globle variables.
byte HeartbeatLED = LOW;
long HeartbeatOldMillis = 0;

void setup()
{
//Setup pin states.
pinMode(pin_Heartbeat_LED, OUTPUT);
}

void loop()
{
  Heartbeat();
}

//Heartbeat to show program is alive.
void Heartbeat()
{
  long HeartbeatNewMillis = millis();
  if(HeartbeatNewMillis - HeartbeatOldMillis > 500)
  {
    HeartbeatOldMillis = HeartbeatNewMillis;
    if(HeartbeatLED == LOW)
    {
      HeartbeatLED = HIGH;
    }
    else
    {
      HeartbeatLED = LOW;
    }
    digitalWrite(pin_Heartbeat_LED, HeartbeatLED);
  }
}

If you want different flash rates then juat creat anouther globle variable for the high/low time and send the time to the fuction to replace to 500. Simple and will not lock up your sketch.

And here is the sketch that the flash rate can be changed for different function calls.

//Define pin names.
#define pin_Heartbeat_LED     13

//Setup globle variables.
byte HeartbeatLED = LOW;
int Interval = 500;
long HeartbeatOldMillis = 0;

void setup()
{
//Setup pin states.
pinMode(pin_Heartbeat_LED, OUTPUT);
}

void loop()
{
  Heartbeat(Interval);
}

//Heartbeat to show program is alive.
void Heartbeat(int Interval)
{
  long HeartbeatNewMillis = millis();
  if(HeartbeatNewMillis - HeartbeatOldMillis > Interval)
  {
    HeartbeatOldMillis = HeartbeatNewMillis;
    if(HeartbeatLED == LOW)
    {
      HeartbeatLED = HIGH;
    }
    else
    {
      HeartbeatLED = LOW;
    }
    digitalWrite(pin_Heartbeat_LED, HeartbeatLED);
  }
}

just change the interval time at the top of the scetch. Hope it useful.