Here is a short demo: running 5 tasks (the last one isn't actually executed, just to show as an example):
//demo for multi-tasking
#define MAX_TASKS 5 //maximum number of tasks
//task 0 - lcd1602
#include "LiquidCrystal.h"
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 13, 8, 9, 10, 11);
void task0(void) {
static unsigned char i=0; //count
lcd.setCursor(0, 1); //column 0, line 1
lcd.print(i++); //print count, then increment it
}
//task 1
#define LED_1 1 //led1 on pin1
void task1(void) {
static unsigned char count=0;
count+=1; //increment count
if (count==100) {
count -= 100; //reset count
digitalWrite(LED_1, !digitalRead(LED_1)); //blink led1 every 100 turns
}
}
//task 2
#define LED_2 2 //led1 on pin1
void task2(void) {
static unsigned char count=0;
count+=1; //increment count
if (count==200) {
count -= 200; //reset count
digitalWrite(LED_2, random(0,2)); //randomly turn on / off led_2
}
}
//task 3
#define LED_3 3
void task3(void) {
digitalWrite(LED_3, digitalRead(LED_1)); //follow led_1
}
//task 4
#define LED_4 4
void task4(void) {
digitalWrite(LED_4, !digitalRead(LED_2)); //follow the opposite of LED_2
}
//task 5
#define LED_5 5
void task5(void) {
digitalWrite(LED_5, !digitalRead(LED_5)); //flip led5 - never executed
}
void setup(void) {
//initialize all tasks
//initialize for task0
lcd.begin(16,2); //display is 16.2
lcd.print("Hello, world!");
//initialize for task1
pinMode(LED_1, OUTPUT);
//initialize for task2
pinMode(LED_2, OUTPUT);
//initialize for task3
pinMode(LED_3, OUTPUT);
//initialize for task4
pinMode(LED_4, OUTPUT);
//initialize for task2
pinMode(LED_5, OUTPUT);
}
void loop(void) {
static unsigned char current_task = 0; //task indexer
switch (current_task) {
case 0: task0(); break;
case 1: task1(); break;
case 2: task2(); break;
case 3: task3(); break;
case 4: task4(); break;
case 5: task5(); break; //never executed
default: break;
}
current_task = (current_task==MAX_TASKS - 1)?0:(current_task+1); //increment task indexer
}
task0 is to run an lcd and count up a value; task1..4 are simple blinking of leds. task5 is excluded from execution, by the definition of MAX_TASKS.
The scheduler doesn't assign a fixed time slot to individual tasks: the next task is run as soon as the previous one finishes. You can fix that by using a systick, like millies() at the end of the task and define a minimum time slot for individual tasks.
You can introduce functional pointers and use them to insert user tasks into a scheduler shell, like the one above, to make it easier to install user tasks into the scheduler - and potentially keep the scheduler entirely out of the user code base.
That's basically the gist of pretty much any RTOS.