I've written a library :)

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

some first remarks:

A library should not contain Serial.println() statements as it can corrupt the output of the sketch that uses the lib.
OR you should make them conditional

your indentation style is not the most common // that is a choice
using CTRL-T in the IDE improves this.

void doBlink()
{
  // if user stops the onboard blinking, turn it off
  if ( !blinkActive ) 
  {
    onBoardLedState = LOW;//make sure the Light is off
  }
  else
  {
    //otherwise toggle it's state .
    onBoardLedState = !onBoardLedState;
  }
  digitalWrite(onBoardLedPin, onBoardLedState);
}

mainly added indentations and spaces just like with English sentences.
refactored the code a bit so - onBoardLedState - reflects the actual state

void checkForKeyDown()
{
  if (digitalRead(buttonPin) == LOW) 
  {
    //key is now down so take note of time 
     keydown = millis();
     addEvent(&checkForKeyUp, 100000UL);  // UL ==> unsigned long 
  }
  else
  {
    //key isn't down at the moment, we'll check back in 1/10th of a second
     addEvent(&checkForKeyDown, 100000UL);
  }
}

removed the local var t as it has 50% chance of not being used.

void removeEvent(int whichOne)
{
  for (int i = whichOne; i < (eventCount-1); i++)
  {
    eventStack[i] = eventStack[i+1];
  }
  eventCount--;
}

or maybe even (or is the eventStack a sorted stack?)

void removeEvent(int whichOne)
{
  if (eventCount > 0)
  {
     eventStack[whichOne] = eventStack[eventCount];
  }
  eventCount--;
}

Rob
Thanks for your suggestions. Regarding the serial print. I wrote this library as part of a much bigger project so wasn't too sure how much space I'd need. (darned If I wanted to go through counting) So it was a debugging tool for myself that I should have perhaps removed.

I also appreciate you reminding me of the nuances of the language(such as UL ); It's been quite a while since I used C or C++ so
these things tend to slip.

It would be a lot more useful if the title of the Thread said what the library is for.

Personally I don't believe scheduling / timing libraries are good for beginners. Much better to provide a tutorial so they can learn how to do it themselves - especially within the limited resources available on an Arduino.

Unfortunately it is not standard Arduino practice (which is a euphemism for saying I have never seen it) to describe the resources that a library needs so one can easily see which libraries might conflict. May I implore you to start a new trend.

I don't see your documentation. In my opinion extensive documentation is at least as important as the code - and (again, unfortunately) is almost as rare as hens' teeth in the Open Source world.

...R