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));
}
}