What you have is a sequence of events. So you can create an array of events. Below code uses a struct to combine all information related to an event. A struct is like a record in a phone book with e.g. last name, first name and phone number.
// structure with information about a relay event
struct RELAY_EVENT //
{
const uint8_t pin; // the pin that the relay is connected to
const uint8_t pinState; // the state that you want to set the pin to (LOW or HIGH)
const uint32_t duration; // the duration that we need to wait before continuing with the next relay event
uint8_t smState; // the state of the relay statemachine (0 = set pin state and start delay timer, 1 = wait for delay timer to time out)
};
The first 3 fields of the struct are constants and can't be changed by the code.
Next we can create an array of events as shown below
// array with relay events
RELAY_EVENT events[] = {
{ 3, HIGH, 10000UL, 0 }, // relay 7
{ 4, LOW, 5000UL, 0 }, // relay 5
{ 6, HIGH, 10000UL, 0 }, // relay 6
};
You will need to complete it; I don't know which pin numbers you use so I just picked a few.
Next you can set the pinMode of the relay pins in setup() by iterating through the events array.
void setup() //
{
Serial.begin(115200);
// loop through the events to set the pin mode; note that they might be multiple times
for (uint8_t cnt = 0; cnt < NUMELEMENTS(events); cnt++) //
{
pinMode(events[cnt].pin, OUTPUT);
}
}
NUMELEMENTS is a macro that you place near the top of your code
// macro to calculate the number of elements in any type of array (int, char, struct)
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
Note that if a pin occurs multiple times, pinMode() for that pin will be executed multiple times. It's a bit of a lazy approach but was the easier way to implement.
Now you can write a function that iterates through the events and execute two steps
- sets the pin to the desired state and starts a timer
- waits till the timer has timed out.
/*
sequencer to control the relay sequence
Returns:
true if sequence is in progress
false if sequence is completed (or aborted)
*/
bool sequencer() {
static uint8_t activeEventNumber = 0;
static uint32_t startTime;
if (events[activeEventNumber].smState == 0) //
{
digitalWrite(events[activeEventNumber].pin, events[activeEventNumber].pinState);
startTime = millis();
Serial.print(millis());
Serial.print(F("\tStep: "));
Serial.print(activeEventNumber);
Serial.print(F(", smState = "));
Serial.println(events[activeEventNumber].smState);
events[activeEventNumber].smState++;
} //
else //
{
if (millis() - startTime >= events[activeEventNumber].duration) //
{
Serial.print(millis());
Serial.print(F("\tStep: "));
Serial.print(activeEventNumber);
Serial.print(F(", smState = "));
Serial.print(events[activeEventNumber].smState);
Serial.println(F(" timed out"));
events[activeEventNumber].smState = 0;
activeEventNumber++;
if (activeEventNumber >= NUMELEMENTS(events)) {
// if all step of the sequence are executed, return false to indicate that the sequencer is no longer in prograss
return false;
}
}
}
// indicate that the sequencer is in progras.
return true;
}
You have to keep on calling the function till it has done all events; at that point it will return false to indicate that it's no longer in progress.
A sequencer event starts with smState == 0; once that is done, the smState is set to 1 so the next tims that you call the function it will execute the 'delay'. Once the 'delay' is finished, the smState of the current sequencer event is set back to 0 and the active sequencer event is incremented.
Once the last event is finished, you have to reset the sequencer event and next indicate to the caller that the sequencer is no longer in progress.
And in loop() you can call it as shown below
void loop() //
{
if (sequencer() == false) {
Serial.println(F("Sequence complete"));
// hang forever
for (;;) //
{
}
}
}
The below is a complete demonstration including reading a button.
// macro to calculate the number of elements in any type o array (int, char, struct)
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
// structure with information about a relay event
struct RELAY_EVENT //
{
const uint8_t pin; // the pin that the relay is connected to
const uint8_t pinState; // the state that you want to set the pin to (LOW or HIGH)
const uint32_t duration; // the duration that we need to wait before continuing with the next relay event
uint8_t smState; // the state of the relay statemachine (0 = set pin state and start delay timer, 1 = wait for delay timer to time out)
};
// array with relay events
RELAY_EVENT events[] = {
{ 3, HIGH, 10000UL, 0 }, // relay 7
{ 4, LOW, 5000UL, 0 }, // relay 5
{ 6, HIGH, 10000UL, 0 }, // relay 6
};
// the button
const uint8_t buttonPin = 2;
void setup() //
{
Serial.begin(115200);
// loop through the events to set the pin mode; note that they might be multiple times
for (uint8_t cnt = 0; cnt < NUMELEMENTS(events); cnt++) //
{
pinMode(events[cnt].pin, OUTPUT);
}
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() //
{
if (sequencer() == false) {
Serial.println(F("Sequence complete"));
// hang forever
for (;;) //
{
}
}
if (digitalRead(buttonPin) == LOW) //
{
Serial.println(F("button is pressed"));
}
}
/*
sequencer to control the relay sequence
Returns:
true if sequence is in progress
false if sequence is completed (or aborted)
*/
bool sequencer() {
static uint8_t activeEventNumber = 0;
static uint32_t startTime;
if (events[activeEventNumber].smState == 0) //
{
digitalWrite(events[activeEventNumber].pin, events[activeEventNumber].pinState);
startTime = millis();
Serial.print(millis());
Serial.print(F("\tStep: "));
Serial.print(activeEventNumber);
Serial.print(F(", smState = "));
Serial.println(events[activeEventNumber].smState);
events[activeEventNumber].smState++;
} //
else //
{
if (millis() - startTime >= events[activeEventNumber].duration) //
{
Serial.print(millis());
Serial.print(F("\tStep: "));
Serial.print(activeEventNumber);
Serial.print(F(", smState = "));
Serial.print(events[activeEventNumber].smState);
Serial.println(F(" timed out"));
events[activeEventNumber].smState = 0;
activeEventNumber++;
if (activeEventNumber >= NUMELEMENTS(events)) {
// if all step of the sequence are executed, return false to indicate that the sequencer is no longer in prograss
return false;
}
}
}
// indicate that the sequencer is in progras.
return true;
}
This should give you the idea how you can implement a non-blocking sequence of events and you can basically build on that.