So this is the idea. (Excuse if it's been done before I'm rather a newbie on the Arduino). It's basically a way around "doing many things at the same time". I'm posting it here as it seems to clear up some of the more common problems that us newbies have and hopefully it may be of some use to others.
The concept is to have a stack of scheduled functions that will run when their time is due. Any function on the stack can either be executed once and then removed OR be executed at a regular time interval.
The overhead in SRAM is 11 bytes for every function that is waiting it's time on the stack.
So here's the example sketch to demonstrate how it helps with timing issues.
/*
This is an example that uses the schedule library
it will blink the onboard led every half second
and may also send SOS in morse code on pin 12
what it does when the user presses the button depends on how long it is depressed
Less than half a second will toggle the onboard blinking on or off
More than half a second it will start/stop sending SOS on pin 12
*/
#include <schedule.h>
const int onBoardLedPin = 13; //pin used for onboard LED
byte onBoardLedState = LOW; //Current state of the onboard LED
bool blinkActive=true; //Wether blinking is active or not
const int morsePin = 12; //pin with LED for sending SOS
bool morseActive=0; //wether we're currently sending SOS or not
char morseMsg[13]="... --- ..."; //The dots and dashes that make up the SOS signal
char morseBit=0; //exactly which of those dots or dashes we've got to so far
const int buttonPin = 10; //pin used for pushbutton
void setup()
{
Serial.begin(9600);
Serial.println("schedule Library demo");
pinMode(onBoardLedPin, OUTPUT);
pinMode(morsePin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
//make space for upto 4 scheduled events
setEventStackSize(4);
//schedule doBlink for 1/2 second from now and EVERY half second thereafter.
addEvent(&doBlink,500000,false);
addEvent(&doMorse,100000); //This function will only be called once
//It will add itself back to the stack later depending on circumstances
addEvent(&checkForKeyDown,100000); //check for KeyDown will also have to reschedule itself later
}
void loop()
{
//checkSchedule will run the appropriate functions as they become due
//So there's nothing much left to put in here
checkSchedule();
}
void doBlink()
{//if user has chosen to stop the onboard blinking we just turn it off and return
if(!blinkActive)
{digitalWrite(onBoardLedPin, LOW);//make sure the Light is off
return;
}
//otherwise we toggle it's state here.
onBoardLedState=!onBoardLedState;
digitalWrite(onBoardLedPin, onBoardLedState);
}
unsigned long int keydown;//stores micros of when key was pressed down
void checkForKeyDown()
{
unsigned long t=millis();
if (digitalRead(buttonPin) == LOW)
{//key is now down so take note of time
keydown=t;
addEvent(&checkForKeyUp,100000);//we'll now schedule checkForKeyUp
}
else
{//key isn't down at the moment, we'll check back in 1/10th of a second
addEvent(&checkForKeyDown,100000);
}
}
void checkForKeyUp()
{
unsigned long int k=millis();
if (digitalRead(buttonPin) == HIGH)
{//user has released the button so now we'll see how long it was down
if((k-keydown)>500)
morseActive=!morseActive; //if key was down more than 1/2 second toggle the morse Activity
else
blinkActive=!blinkActive; //otherwise toggle the onboard blink
//since user has released the button
//we'll now go back to checking for keydown
//(but not for another 300 microseconds)
addEvent(&checkForKeyDown,300000);
return;
}
//button wasn't released so we'll check back in another 10th of a second
addEvent(&checkForKeyUp,100000);
}
char morseState=0;
void doMorse()
{
unsigned long int dotLength=200000;//two tenths of a second for a dot
if(!morseActive)
{//NO panic at the moment so don't send morse
digitalWrite(morsePin,LOW); // Just make sure the led is off
addEvent(&doMorse,100000); // We'll check back in 1/10th of a second
return;
}
//
switch(morseState)
{case 0: //0 current bit needs sending, (led is off)
//turn on LED
digitalWrite(morsePin,HIGH); // turn the LED on
//now schedule the next call dependant on wether we're dealing with a dot or dash
if (morseMsg[morseBit]=='.')
addEvent(&doMorse,dotLength);//turn off in 200 milliseconds for a dot
else
addEvent(&doMorse,dotLength*3);//turn off in 600 milliseconds for a dash
break;
case 1: //currently ON SO it's now time to turn it off
digitalWrite(morsePin,LOW); //turn LED OFF and move on to next bit
morseBit++; //set morsebit to point to next bit
if((morseMsg[morseBit]==' ')||(morseBit==11)) //if there's a space (or end of string)...
{dotLength=3*dotLength; //we want the light out 3xlonger..
morseBit++; //and skip over the ' ' in the string
}
addEvent(&doMorse,dotLength); //we'll be back at the appropriate time
}
morseState=!morseState; //next time around we'll be doing the opposite
if(morseBit>10)//if we've sent all the dots and dashes, next time we'll be starting over
morseBit=0;
}
This is the include file
// FILE: schedule.h
// VERSION: 0.0.1 and probably the only
// PURPOSE: allow functions to be scheduled to run
// LICENSE:http://www.gnu.org/licenses/gpl.html
// but What the heck, use it if you find it useful!
// HISTORY:
// Author KenF but if it goes wrong I'll deny everything
// Date 25th September 2014
#ifndef schedule_h
#define schedule_h
struct scheduledItem
{public:
unsigned long dueFrom;//micros at start of countdown
unsigned long due;//number of micros to wait
void (*runFunction)(); //function that needs to run
bool onceOnly;//if 1 function is removed from the schedule after running
//if 0 function is set to run again after the same period
};
void setEventStackSize(int stackHeight);
void addEvent( void (*newFunction)() , unsigned long when);
void addEvent( void (*newFunction)() , unsigned long when, bool removeWhenDone);
void checkSchedule();
#endif
And here's the required .cpp file
// FILE: schedule.cpp
// VERSION: 0.0.1
// PURPOSE: maintain a stack of scheduled functions
// and execute them when they are due
// LICENSE: GPL v3 (http://www.gnu.org/licenses/gpl.html)
//
// HISTORY:
// KenF - Original version
//
#include "schedule.h"
#include <arduino.h>
int scheduleSpace=-1;
int eventCount=0;
scheduledItem* eventStack;
void setEventStackSize(int stackHeight)
{
unsigned int msize=sizeof(scheduledItem)*stackHeight;
eventStack = (scheduledItem*) malloc(msize);
scheduleSpace=stackHeight;
}
void addEvent( void (*newFunction)() , unsigned long when)
{addEvent(newFunction,when,true);
}
void addEvent( void (*newFunction)() , unsigned long when, bool removeWhenDone)
{
if (scheduleSpace==-1)
{Serial.println("addEvent before beginSchedule called");
return;
}
if (eventCount>scheduleSpace)
{Serial.println("function addEvent is out of space");
return;
}
eventStack[eventCount].dueFrom=micros();
eventStack[eventCount].due=when;
eventStack[eventCount].runFunction=newFunction;
eventStack[eventCount].onceOnly=removeWhenDone;
eventCount++;
}
void removeEvent(int whichone)
{
int i;
for(i=whichone;i<(eventCount-1);i++)
eventStack[i]=eventStack[i+1];
eventCount--;
}
void checkSchedule()
{
int i;
for(i=0;i<eventCount;i++)
{if ((micros() - eventStack[i].dueFrom) >=eventStack[i].due)
{
eventStack[i].runFunction();
if (eventStack[i].onceOnly)
removeEvent(i--);
else
eventStack[i].dueFrom=micros();
}
}
}