Multitasking made easy [code example]

Bored of searching on internet how to really multitask a UNO, and only find small sketches to blink 2 or 3 leds at various rates? If you want to concurrently run various sketches, like an alarm clock, running concurrently with a garage door opener, a temperature regulation process, or whatever you want, without using a heavy multitasker, or if you need to multitask a fast process (like controlling 10 stepping motores at a time, when a complex multitasker is too slow), read below: you will see how to make2 or more cakes simultaneously, while sending a morse message, while you... almost anything you want, simultaneously!

suggestion: paste the code in a real text editor software, or in the Arduino IDE: reading it will be a lot easier.
[code]
/* MAKING A CAKE,
    OR EXECUTING MULTIPLE TASKS CONCURENTLY WITHOUT A HEAVY MULTITASKER,
    OR DEALING WITH FUNCTIONS COMPRISING WAITING STATES,
    OR EXECUTING MANY SKETCHES CONCURRENTLY


    For those who could be interested in this technique:

    My objective is to describe my personal approach to get a small microprocessor do several tasks
    concurrently, concomitantly, simultaneously, at the same time, etc.
    If you take time to analyse this simple code, you will see that a small UNO can do several various
    related or unrelated tasks at a time, like making a cake, sending morse messages, managing a garage door opener,
    run an alarm clock, controlling an industrial process, etc. all these tasks beeing done concurrently,
    without the overhead of managing a heavy multitasker.

    The technique used here is based on state machines. To make the programmer aware that a given function is a state machine,
    their names start with a present participle verb. These functions return true when the function is active,
    return false when the function has finished doing its task. These functions, when returning true, are in a waiting state,
    and thus must be called repetitively to continue and complete their task. You will see how below.

    The following cakeMaking() sketch is a pseudo-sketch: it is compilable, but does not make a real cake.
    I chose a trivial application because I did not want the reader to focus on the task itself of making a cake,
    but on how tasks are beeing done CONCURRENTLY.
    This cakeMaking() function is just a pretext to demonstrate how I manage waiting conditions in my projects,
    how I use the the waiting periods (available processing time) to get the processor execute other tasks during these waiting times,
    and how I have the processor return to the waiting tasks when the waiting condition has cleared.
    The sketch, and the technique too, can be improved, feel free to adapt this recipe the way you want.
    The cakeMaking sketch is not complete, it may even have errors, omissions, etc.
    Please, examine the skeleton: how tasks are done CONCURRENTLY, see the whole picture, not the details on the cake.

    The way most people make a cake is simply:
    1- start the oven
    2- Make the dough:
          - mix the dry ingredients
          - mix the wet ingredients
          - mix the dry and wet ingredients together
          - cook in the oven for a while
    3- Make the icing:
          - mix the dry ingredients
          - mix the wet ingredients
          - mix the wet and the dry ingredients together
    4- When dough is cooked,
          - Stop the oven
          - let the cake cool down for a while
    5- Apply icing, decorate, etc. And:
    6- Bon appétit!

   Let us see how a small microprocessor like a UNO runs CONCURRENTLY the various tasks involved in making a cake. (Or any other process)
    ----------------------------------------------------------------------------------------------------------------------------
*/
//Timer is often an essential part in any control application: here is my timer flavor.
//-----------------------------------------TIMER CLASS-------------------------------------
class uStimer {                //micro second timer up to 71 minutes et 34 seconds, resolution 4 uSec.
  private:
    unsigned long uSvalue;
  public:
    //--------------------------------
    void start() {
      uSvalue = micros();
    }
    //--------------------------------
    bool counting(unsigned long len) {
      if (micros() - uSvalue >= len  ) {
        uSvalue += len;
        return false;
      }
      return true;
    }
    //--------------------------------
    unsigned long see() {
      return (micros() - uSvalue);
    }
};
//------------------------------------------------------------------
void doSomething() {}                                                       //dummy function for demonstration purpose
bool condition() {}                                                         //dummy condition for demonstration purpose
byte aPin = 0;                                                              //a dummy pin
unsigned long someTime = 10000;                                             //a dummy time 10 mSec

//-----------------------------------------OVEN CLASS-------------------------------------
class oven {                                                                //let us create an oven class, should our application require more than one oven.
  private:                                                                  //But even if we needed a single oven , classes are so convenient...
    byte state = 0;                                                         //0: oven is off. !=0 oven is on
    byte hot = false;                                                       //oven is at desired temperature
    byte ovenPin = aPin;
  public:
    int temperature = 72;                                                   //actual oven temperature in Fareinheit
    //---------------------------------------
    void begin(byte aPin) {
      ovenPin = aPin;
      pinMode(aPin, OUTPUT);
    }
    //---------------------------------------
    void setOn() {
      state = 1;
    }
    //---------------------------------------
    void setOff() {
      state = 0;
      hot = false;
    }
    //---------------------------------------
    bool isHot() {
      return hot;
    }
    //---------------------------------------
    bool heating(int setPoint) {                                            //the main oven function: turns the oven on, and regulates its temperature
      switch (state) {
        case 0:
          setOn();                                                          //turn the oven on
        case 1:                                                             //regulating the temperature
          doSomething();                                                    //measure the temperature using analogRead(), etc.
          if (temperature < setPoint - 5 ) {
            digitalWrite(ovenPin, HIGH);                                    //turn the heating element on;
          }
          else {
            if (temperature >= setPoint) {
              digitalWrite(ovenPin, LOW);                                   //turn the heating element off;
              hot = true;
            }
          }
          return true;                                                      //indicates that the function never ends.
      }                                                                     //Use setOff() to turn the oven off when not in use.
    }
};
//Should we need more than one cake maker in our application, let us also define a cakeMaker class.
//-----------------------------------------CLASS CAKE MAKER-------------------------------------
class cakeMaker {
  private:
    uStimer aTimer;                                                         //create a timer. More than one timer is required, but I define only one for demonstration
    oven cakeOven;                                                          //create an oven to cook our cake
    byte addIngredientState = 0;                                            //the state for this state machine
    byte mixingDryIngredientsState = 0;                                     //the state for this state machine
    byte preparingDoughState = 0;                                           //the state for this state machine
    byte makingCakeState = 0;                                               //the state for this state machine
    //                                                                        these machines will work concurrently
    //-------------------------------
    bool addingIngredient(unsigned int qty, unsigned long someTime) {       //adding, not add. =>this is a state machine: it has waiting states.
      switch (addIngredientState) {                                         //task: open a valve until a given weight of the ingredient is dispensed.
        case 0:
          if (condition) {                                                  //condition: the weight of the ingredient is not zero => error
            doSomething();                                                  //process the error: the present sketch DOES NOT include error processing.
            return false;                                                   //the task is done. The caller should check a status, but I do not check for errors here.
          }
          digitalWrite(aPin, HIGH);                                         //open a valve to add the ingredient
          aTimer.start();                                                   //start a chronometer
          addIngredientState = 1;                                           //next time addingIngredient() will be called, it will execute at case 1, not at case 0.
        case 1:                                                             //addingIngredient(), but no more than someTime (to avoid hanging in case of problem)
          if (condition) {                                                  //the weight of the ingredient is lower than the desired weight...
            if (aTimer.counting(someTime)) return true;                     //and the time allowed to add the ingredient has not elapsed: the normal case.
            //NOTE curled braces {} are not required if a single instruction would be executed within these braces.
            //Here, timer elapsed without the right qty of ingredient beeing dispensed: process the error here.
          }
          digitalWrite(aPin, LOW);                                          //we had the right qty before timer elapsed: close valve
          addIngredientState = 100;                                         //100: all tasks states are arbitrary 100 when done. Select the number you wish.
        case 100:                                                           //all the future calls to the function will return : the task is done!
          return false;                                                     //task is done. Calling the function again will return "the task is already done".
      }
    }
    //-------------------------------
    void addIngredientAck() {                                               //Used by the caller of addingIngredient() to acknowledge that the task has been done,
      addIngredientState = 0;                                               //so that the future call to addingIngredient() will start at case 0:
    }                                                                       //not continuing the former addingIngredient() calls.
    //-------------------------------
    bool addingDryIngredient1() {}                                          //Let us create 12 dummy functions, that could be similar to the ones above.
    bool addingDryIngredient2() {}                                          //There are more efficient ways doing this, but for simplicity, let us go with 12 functions
    bool addingDryIngredient3() {}                                          //3 functions to add 3 different dry ingredients, and 3 other for 3 different wet ingredients.
    void addDryIngredient1Ack() {}                                          //accompanied by their respective ack.
    void addDryIngredient2Ack() {}                                          //(since we want the 6 addingIngredient be concurrent, each one should create its own timer.)
    void addIDryngredient3Ack() {}
    //                               there are more efficient ways of doing things, but that one is clear and simple.
    bool addingWetIngredient1() {}
    bool addingWetIngredient2() {}
    bool addingWetIngredient3() {}
    void addWetIngredient1Ack() {}
    void addWetIngredient2Ack() {}
    void addIWetngredient3Ack() {}

    //Now, let us define a mixingDryIngredients() function, that CONCURRENTLY adds 3 different ingredients, and then mixes them for a while.
    //-------------------------------
    bool mixingDryIngredients() {                                           //after having added 3 ingredients, we will mix them for a while.
      switch (mixingDryIngredientsState) {
        case 0:
          while (addingDryIngredient1() + addingDryIngredient2() + addingDryIngredient3()) return true;
          /* NOTE: You must use the +  not the ||  to implement this logical or function on booleans,
              because of the way the || instruction is implemented in C.
              The C || instruction ceases the evaluation of the rest of the conditional expression as soon as it finds a true. Nice, but this is not wanted here.
              Consequence: when addingIngredient1() returns true, addingIngredient2() is not called, nor addingIngredient3().
              Using ||, addingIngredient2()will be called only after addingIngredient1() returns false, (has finished adding its ingredients) and so on.
              Consequently, using || would call the 3 functions in sequence, one after the other. This is the exact opposite of what we want:
              WE WANT TO ADD THESE 3 INGREDIENTS CONCURENTLY, SIMULTANEOULSLY, AT THE SAME TIME, CONCOMITANTLY.
              We want to open the 3 valves simultaneously, and close them individually when required. Using + instead of || forces the compiler to evaluate
              all members of the condition, hence calling the 3 functions. (+ operates a complete logical or function, on all boolean members.)
              In the while() above, we are adding the 3 ingredients concurrently, and we are waiting for all 3 operations to be done before going further.
              (Also note that curled brackets {} are not required after a while or a if, when a single instruction would be enclosed within these brackets.)
          */
          addDryIngredient1Ack(); addDryIngredient2Ack(); addDryIngredient2Ack();      //acknowledge that ingredients have been added
          doSomething();                                                      //turn the mixer on
          aTimer.start();                                                     //start chronometer. For concurrency, a timer should be allocated for this procedure.
          mixingDryIngredientsState = 1;                                      //note that the addIngredients timers are not in use... you may use them to time something else.
        case 1:                                                               //at the next call, mixingDryIngredients()will execute here at case 1:, not at case 0:
          while (aTimer.counting(someTime)) return true;                      //mix ingredients for someTime: waiting for someTime to elapse.
          doSomething();                                                      //ingredients are mixed, turn the mixer off
          mixingDryIngredientsState = 100;                                    //100 = task is finished
        case 100:
          return false;                                                       //the task is done
      }
      //CAREFULLY NOTE: the function mixingDryIngredients CONCURRENTLY adds 3 ingredients, and then mixes them for a while.
    }
    //-------------------------------
    bool mixingWetIngredients() {}                                            //Great! Let us create these 2 functions for the wet ingredients;
    void mixWetIngredientsAck() {}                                            //they could be similar to mixingDryIngredients, or whatever is needed.
    // NOTE: the functions mixingWetIngredients() and mixingDryIngredients() concurently add respectively 3 ingredients and mix them. This is 6 concurrent tasks.
    //Now we can add and mix wet and dry ingredients together.
    //-------------------------------
    bool preparingDough() {
      switch (preparingDoughState) {
        case 0:
          while (mixingDryIngredients() + mixingWetIngredients()) return true;//you should now understand what this single-line instruction does.
          // Things are become interesting: at this point in preparingDough(), we are concurently executing 6 addingIngredients() operations simultaneously, in two different bowls,
          //and then, one mixings operations in each bowl, concurrently ! And wait, that's not finished!
          doSomething();                                                      //acknowledge the mixing operations
          doSomething();                                                      //combine the dry and the wet ingredients together in a bowl, and mix them for a while
          doSomething();                                                      //start the mixer
          aTimer.start();                                                     //start chronometer
        case 1:
          while (aTimer.counting(someTime)) return true;                      //mix wet and dry ingredients together for someTime
          doSomething();                                                      //stop the mixer
          preparingDoughState = 100;
        case 100:
          return false;                                                       //dough is ready!
      }
    }
    //-------------------------------
    void prepareDoughAck() {}                                                 //you know why...
    //-------------------------------

    // OK. We have a preparingDough() function that runs many various tasks concurrently. But we also need a preparingIcing()!
    // Let us write this function. preparingIcing() could be similar to preparingDough(), or whatever is needed.Say it also runs 6 tasks concurrently.

    bool preparingIcing() {}                                                   //executes many concurrent tasks too
    void prepareIcingAck() {}

    // GREAT! we now have 2 important functions preparingDough() and preparingIcing() each running their respective tasks,
    // each involving many sub-tasks. Ready to go further?
  public:
    bool makingCake() {
      switch (makingCakeState) {
        case 0:
          while (cakeOven.heating(350),                                            //The first thing to do when making a cake: turning the oven on!
                 preparingIcing() , preparingDough() ) return true;                //prepare the dough and the icing CONCURENTLY: many small tasks are running concurrently!
          /* NOTE the above while() instruction turns the oven on, regulates its temperature, prepares the dough and prepares the icing,
            all tasks beeing done CONCURENTLY. This at least 12 tasks concurrently done.
            NOTE: about using commas within the conditional part of a while () or a if (): function followed by a comma is called,
            but only the last one serves as the condition. In other words, we are waiting only for dough-ready here,
            but meanwhile we also regulate the oven temperature, prepare the icing and prepare the dough, CONCURRENTLY.
            When dough is ready, the icing could be ready or not, as the oven could be hot or not, we don't know.*/
          prepareDoughAck();                                                       //acknowledge the dough
          makingCakeState = 1;
        case 1:                                                                    //here the dough is ready
          while (cakeOven.heating(350),
                 preparingIcing(), !cakeOven.isHot() ) return true;                //commas: continue regulating the temperature, preparing icing, while waiting for a hot oven
          doSomething();                                                           //the oven is hot: put the dough in the oven
          aTimer.start();                                                          //chrono
          makingCakeState = 2;
        case 2:                                                                    //waiting for the cake to be cooked.
          while (cakeOven.heating(350) , preparingIcing(), aTimer.counting(someTime) ) return true; //no comment: you understand now what this single-line instruction does.
          doSomething();                                                           //cake is cooked: put the cake onto the counter to let it cool down for a while
          cakeOven.setOff();                                                       //Don't forget this step: Take care of your electricity bill!
          aTimer.start();                                                          //chrono
          makingCakeState = 3;
        case 3:                                                                    //let the cake cool down for a while
          while (preparingIcing(), aTimer.counting(someTime) ) return true;        //the icing is propably done, but...
          makingCakeState = 4;
        case 4:
          while (preparingIcing()) return true;                                    //Now the cake is cool, let us wait for the icing, should it be not ready yet.
          prepareIcingAck();                                                       //acknowledge the icing
          doSomething();                                                           //apply icing, decorate cake, etc
          makingCakeState = 100;
        case 100:
          return false;                                                            //cake is done! The caller who asked for a cake must acknowledge the cakeMaking() process.
      }
    }
    //In makingCake(), we activate 3 concurrent tasks: cakeOven.heating(350), preparingIcing() and preparingDough().
    //the called sub-functions activate 12 small sub-tasks=> we are executing at least 15 concurrent tasks, and the synchronization is perfect. A beautiful dance!
    //-------------------------------
    void makeCakeAck() {}
    //-------------------------------
};                                                                                 //end of class cakeMaker


/*CONCLUSION AND THOUGHTS.
  THAT'S IT, THAT'S ALL: and that's quite simple: Each time we encounter a waiting condition, we create a state for the machine,
  and the machine waits for the waiting condition to clear; but instead of turning in circles (like delay() does), it returns true.
  A state machine returning true means that the task is not finished. (the opposite if returning false.)
  Since the task is not finished, the caller (or a caller higher or somewhere in the application) is responsible to come back:
  call the function again until the task is done. By successive returning true, the processor goes back and forth in the application
  until it finds a task ready to continue/execute. Simple and efficient. 

  Note that a waiting condition may be: waiting for time, for an input, for an internal or external event, 
  for a temperature to be over x degree, etc.
  Waiting for a keyboard input, for example: With this technique, a processor will execute various tasks with the simple:
  while (waiting_for_a_key) return true!

  One important thing in the whole technique is the longest main loop time: the longest time required to process the whole program.
  The loop period must be shorter that any time counted: if the loop is 45 uSec, we will not be able to time events shorter that this.
 
  Also note that what is true for || versus +, is also applicable for && versus *.
  Waiting conditions can be combined like: while ( (a + b) * c) return true.
  But we often need to identify which term of the condition made the expression false.
  See case 1: of addingIngredient() above.
  Also note that you may use "if(conditions) return true;" instead of "while(conditions) return true;" : a matter of preference.
  Personnaly, I like calling many functions in a while (conditions), calls separated by ||, +, && or *, or , as required.
  But there are also other ways to write it: a matter of preference.
*/
//------------------------------------------------------------------
void setup() {
  Serial.begin(9600);                                               //if required
  delay(250);
}
//------------------------------------------------------------------
cakeMaker john;                                                   //create john: a cake maker
//------------------------------------------------------------------
/*
   void loop() {
  while (john.makingCake());                                        //That's all! John is making a cake, all tasks are done concurrently, whenever possible.
  //                                                                 (we cannot concurrently cook the cake while preparing the dough!)
  john.makeCakeAck();                                               //looping, and john makes another cake.Forever.
  }
*/
//------------------------------------------------------------------

/* john is efficient: he does many tasks concurrently: adding many ingredients in different bowls, mixing them
    combining them, mixing again, regulating the oven temperature, preparingIcing while preparing dough, etc. CONCURRENTLY.
    To fully understand the process, imagine you are john, and fully analyse in details the case 0: of makingCake(),
    see step by step, call by call, return by return, what john does.

    Nice! But we can also create another cake maker, and easily make them both work concurrently, like this:
*/
//------------------------------------------------------------------
cakeMaker bill;
//------------------------------------------------------------------
void loop() {
  if (!john.makingCake())  john.makeCakeAck();
  if (!bill.makingCake())  bill.makeCakeAck();                       //both john and bill work concurrently, at the same time. They can even share data, resources, etc.
}                                                                    //john and bill are doing an infinite numbers of cakes, 2 cakes at a time. This means 24+ tasks run simultaneously.
//------------------------------------------------------------------
//moreover we could create various applications like:
bool openingGarage() {}
bool alarmingClock() {}
bool processingTask() {}
bool sendingMorse()  {}

//and call them in a main loop:

/*
  void loop() {
  john.makingCake();
  bill.makingCake();
  openingGarage();
  alarmingClock();
  processingTask();
  sendingMorse();
  //all these tasks will be done concurrently, easily, transparently. Tasks simply require to be written correctly as state machines.
  //One crucial point to watch however: the longest main loop time for the whole program.
  //No len in all counting(len) calls must be less than the main loop time. Here is how to estimate:
  }
*/
uStimer loopTimer;
unsigned long loopTime;
unsigned long maxLoopTime;
/*
  void loop() {                                               //A way to estimate the longest loop time
  loopTimer.start();

  //call all your loop functions here

  loopTime = loopTimer.see();
  if (loopTime > maxLoopTime) {
    maxLoopTime = loopTime;
    Serial.print("maxLoopTime = "); Serial.print(maxLoopTime); Serial.println("uSec.");
  }
  }
*/

/* Fancy guys could implement a task-priority manager in the main loop, etc.
    But my objective here is not challenging multitaskers, nor multitaskers designers, but...
    JUST HAVE FUN!
*/
//If the above is of some interest for you, feel free to TECHNICALLY comment! All other comments will be ignored.
[/code]
1 Like

Do you know Task Macros? Search the forum...

By the way, should you wish to blink many leds at various rates, try my method:

if (! myTimer.counting(500000)) digitalWrite (13, !digitalRead (13)); //led #1 blinks 1 Hz
if (! myTimer.counting(600000)) digitalWrite (12, !digitalRead (12)); //led #2 blinks 1,2 Hz

info please...

Or if you want to execute one or more functions on a timed basis,

if (!myTimer.counting(500000)) {
function1(); //function1() and function2() are called every half a second
function2();
}

I think it will be fine, unsigned integers nicely wrap around when there is an overflow. Or am I missing something else here?

Many people believe this will crash at roll-back, but they are wrong. Try this:

[code]

//------------------------------------------------------------------
void setup() {
  Serial.begin(9600);                                    
  delay(200);

  byte a = 0x80;
  byte b = 0x90;
  byte q = b - a;
  Serial.println(q, HEX);
  byte c = 0xf0;
  byte d = 0x10;   //here, d rolled back
  byte r = d - c;
  Serial.println(r, HEX);

  unsigned int e = 0x8000;
  unsigned int f = 0x9000;
  unsigned int s = f - e;
  Serial.println(s, HEX);
  unsigned int g = 0xfff0;
  unsigned int h = 0x0010;
  unsigned int t = h - g;      //here, h rolled back
  Serial.println(t, HEX);
  // The same for unsigned long.

  //the same for unsigned long, etc.
  //it always works if all members are unsigned, and same data type: e, f and s for example.
}

void loop() {
}
[/code]

Thanks, I understand unsigned arithmetic well :slight_smile:

Yes, I was wrong about your function

1 Like

I searched the forum, and did not find significant results. But if "task macros" is for calling functions on a timed basis, see my post #3 and #5

The main thread

unfortunately is in German. Libraries are available on GitHub from Combie and me.

Here is a more complex example: sending two morse messages concurrently. Note where cases: are located in this sketch: a case can be declared within a while. ( a if, or a for loop). Nice!
For those interested, I have written an alarm clock with setting buttons, display, beeper etc. One can easily and transparently send 2 morse messages while processing the alarm clock... while regulating a temperature, etc.
HAVE FUN!

[code]
#include "Timer.h"


//--------------------------------CLASS MORSE SENDER---------------------------------
char morseTable[][9] = {                   //Note: 9 because the longest morse char has 8 symbols (dots and dashes)1=dot, 2=dash, 3=pause
  {'A', 1, 2, 0},                          //a dot dash. The last 0 indicates the end of character.
  {'B', 2, 1, 1, 1, 0},                    //b is dash dot dot dot
  {'C', 2, 1, 2, 1, 0},                    //c is dash dot dash dot
  {'D', 2, 1, 1, 0},                       //d is dash dot dot
  {'E', 1, 0},                             //e is dot
  {'F', 1, 1, 2, 1, 0},                    //f is dot dot dash dot
  {'O', 2, 2, 2, 0},                       //o dash dash dash
  {'S', 1, 1, 1, 0},                       //s dot dot dot
  {' ', 3, 0},                             //a space char is 2 pauses
  //space between characters
  //                                       etc.. code all letters
  {0,}                                     //end of table
};
//----------------------------------------
class morseSender {
  public:
    char msg[33] = {"abcdef sos " };                   //A null terminated string, 32 char max see arduino-reference\string
    //                                                 //A null at index 0 indicates there is no more message to send.It aborts the task.
    //initialize variables with ...begin
  private:
    int morsePin = 13;                                 //pin number that activates a bip
    unsigned int dotLen = 0;                           //dot duration
    unsigned int dashLen = 0;                          //dash duration
    unsigned int pauseLen = 0;                         //the pause (time) between 2 char
    timer timer;                                       //a local timer named timer.
    int state = 0;
    int msgIndex = 0;                                  //an index in msg pointing the char beeing sent / to send.
    int lineIndex = 0;                                 //an index pointing a line in morseTable
    int columnIndex = 0;                               //an index pointing a column in morseTable
    int len = 0;                                       //the len of the bip beeing sent (dotLen or dashLen): to allow calculating this len once
    //----------------------------------------
    int findMorseCode(char x) {                        //returns an index in morseTable for the char, or -1 if the char does not exist.
      int index = 0;                                   //an index for searching in morseTable
      while (morseTable[index][0] != 0) {
        if (morseTable[index][0] == x) {
          return index;                                //returns the index if char is found in morseTable
        }
        else index++;
      }
      return -1;                                       //otherwise returns -1.
    }
  public:
    //----------------------------------------
    void begin(int aMorsePin, int aDotLen, int aDashLen, int apauseLen ) {
      //Serial.println("initializing...");
      morsePin = aMorsePin;
      dotLen = aDotLen;
      dashLen = aDashLen;
      pauseLen = apauseLen;
      state, msgIndex, columnIndex = 0;
      pinMode(aMorsePin, OUTPUT);
    }
    //----------------------------------------
    int msgBuffLen() {
      int x = sizeof(msg);
      return (x);
    }
    //----------------------------------------
    bool sendingMorse() {
      if (msg[0] != 0 ) {
        switch (state) {
          case 0:
            while (msg[msgIndex] != 0) {                                        //while there are char to send
              if (isAlpha(msg[msgIndex])) {
                bitClear(msg[msgIndex], 5);                                     //make this alpha char lower case.
              }
              lineIndex = findMorseCode(msg[msgIndex]);                         //find the code to send for the char.
              if (lineIndex >= 0) {                                             //the char has been found in morseTable.
                //                                                              Serial.print("sending "); Serial.println(morseTable[lineIndex][0]);
                columnIndex = 1;                                                //lineIndex is set, columnIndex to the next bip to send.
                while (morseTable[lineIndex][columnIndex] != 0) {               // while there are bips to send,
                  if (morseTable[lineIndex][columnIndex] < 3) {                 //if a dot or a dash
                    if (morseTable[lineIndex][columnIndex] == 1) {              //SET LEN FOR A DASH OR A DOT
                      len = dotLen;                                             //Serial.println("    DOT");
                    }
                    else {
                      len = dashLen;                                            //Serial.println("    DASH");
                    }
                    digitalWrite(morsePin, HIGH);
                    timer.start();
                    state = 1;
                  case 1:
                    while (timer.counting(len)) return true;                    //send a len bip
                    digitalWrite(morsePin, LOW);
                    timer.start();
                    state = 2;                                                  //Serial.println("...");
                  case 2:
                    while (timer.counting(dotLen)) return true;                 //send a dotLen silence after a bip
                  }//6
                  else {
                    digitalWrite(morsePin, LOW);
                    timer.start();
                    state = 3;                                                  //Serial.println("SPACE");
                  case 3:
                    while (timer.counting(pauseLen)) return true;               //send a pauseLen silence
                  }
                  columnIndex++;                                                // next morse symbol
                }                                                               //while symbol to send
              }                                                                 //all symbols have been sent
              timer.start();
              state = 4;                                                        //Serial.println("Pause");
            case 4:
              while (timer.counting(pauseLen)) return true;                     //make a pause between characters 
              msgIndex++;
                                                                                //Serial.println("next char");
            }
            state = 0;
            // msg[0] = 0;
            msgIndex = 0;
            columnIndex = 0;
        }
      }      
      return false;
    }
};
//----------------------------------------

morseSender Tom;  //Create a morseSender called Tom.
morseSender Jack; //Create a morseSender called Jack.


//------------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Tom.begin(3, 80, 240, 400); 
  Jack.begin(2, 60, 180, 300);
  strcpy(Jack.msg, "sos abc ");
}
//------------------------------------------------------------------------------------
void loop() {
  Tom.sendingMorse();
  Jack.sendingMorse();
}
//------------------------------------------------------------------------------------
[/code]

image

Quick Guidance on Multi-Tasking Project - Using Arduino / Project Guidance - Arduino Forum

-----> GitHub - DrDiettrich/ALib0: Arduino library for button handling and parallel execution.

Here is a more complex example: sending two morse messages concurrently.

my library Timer.h is similar to the one given in post #1,(class uStimer) except it uses millis();

Where's myTimer coming from? bc 'class Timer' has no member named 'counting'.

See the code in post #1 above.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.