You need a finite state machine. Then you can have code that would typically require delay()
but without blocking the loop()
while it waits.
Here is a crude class I wrote to do it
stateMachine.h
/*
StateMachine
Class to perform some state machine logic on the Arduino
*/
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#include <pins_arduino.h>
#endif
class StateMachine
{
public:
// Constructor
StateMachine(void (*)(void));
// Functions
void update(void);
void nextState(uint16_t, uint32_t);
uint16_t currentState(void);
void stop(void);
private:
void (*callback)(void); // Callback to state machine code
uint16_t state; // Current state of the machine
uint32_t delay; // Delay until state change. Delay of -1 is stopped
uint32_t timer; // Timer to keep track of last run time
uint8_t isRunning; // Are we running?
int16_t counter[2]; // Two counters for general use
};
stateMachine.cpp
/*
StateMachine
Class to perform some state machine logic on the Arduino
*/
#include "StateMachine.h"
//
// Constructor
//
StateMachine::StateMachine(void (*callbackFunction) (void)) {
state = 0;
delay = 0;
timer = 0;
isRunning = 0;
callback = callbackFunction;
counter[0] = 0;
counter[1] = 0;
}
//
// Update the state machine
//
// Use in loop()
// Will keep running over and over unless stop()ed
//
void StateMachine::update(void) {
if (isRunning) {
if(millis() - timer >= delay) {
timer = millis();
//Call the state machine
callback();
}
}
}
//
// Current state
//
uint16_t StateMachine::currentState() {
return state;
}
//
// Next state
//
void StateMachine::nextState(uint16_t nextState, uint32_t nextDelay) {
state = nextState;
delay = nextDelay;
isRunning = 1;
//Restart timer
timer = millis();
}
//
// Stop
//
void StateMachine::stop()
{
isRunning = 0;
}
Then you need a function with the logic:
void exampleStateMachineCallback()
{
switch (exampleStateMachine.currentState()) {
case 0:
//initialise everything and then got to state 1 immediately
exampleStateMachine.nextState(1,0);
break;
case 1:
//do something then do state 2 500ms later
exampleStateMachine.nextState(2,500);
break;
case 2:
//do something then do state 1 500ms later
exampleStateMachine.nextState(1,500);
break;
}
}
Finally in the global variables we join them up
StateMachine exampleStateMachine = StateMachine(exampleStateMachineCallback);
And in loop()
we call update all the time
exampleStateMachine.update();
The states implement your logic. For example, state 1 in the above code could be turn on the led, and state 2 could be turn it off. State 0 might prepare some variables. If you wanted the led to start flashing with a button press it might look like
if(digitalRead(LEFT_BUTTON) == HIGH) exampleStateMachine.nextState(0,0);
And if you wanted to stop it:
exampleStateMachine.stop();
In the StateMachine class I put two counters, you could use these to do other timers. For example, to flash a led for 30 seconds it might look like
void exampleStateMachineCallback()
{
switch (exampleStateMachine.currentState()) {
case 0:
exampleStateMachine.counter[0] = 0;
exampleStateMachine.nextState(1,0);
break;
case 1:
digitalWrite(13,HIGH);
exampleStateMachine.nextState(2,500);
break;
case 2:
digitalWrite(13,LOW);
exampleStateMachine.counter[0]++;
if (exampleStateMachine.counter[0] >= 30) exampleStateMachine.stop();
else exampleStateMachine.nextState(1,500);
break;
}
}