Arduino Forum

Forum 2005-2010 (read only) => General => Frequently-Asked Questions => Topic started by: jdiezlopez on Jan 06, 2009, 01:07 am

Title: Arduino Multithreading
Post by: jdiezlopez on Jan 06, 2009, 01:07 am
Hi, I want to do two things at the same time, for example: turn on two servo motors.

Or I dunno, but can the Arduino handle multithread tasks?
Like calculating [ch960] and flashing a led at the same time.
Title: Re: Arduino Multithreading
Post by: halley on Jan 06, 2009, 03:10 am
No such thing as a thread, to the microcontroller.  But you could write your own simple task scheduler, and bounce between those tasks according to their timing requirements.
Title: Re: Arduino Multithreading
Post by: dmesser on Jan 06, 2009, 07:18 am
Here's a function I came up with to do what halley is talking about.

Code: [Select]
boolean cycleCheck(unsigned long *lastMillis, unsigned int cycle)
{
 unsigned long currentMillis = millis();
 if(currentMillis - *lastMillis >= cycle)
 {
   *lastMillis = currentMillis;
   return true;
 }
 else
   return false;
}


Basic usage:

For each event you want to deal with, set up an unsigned int for how often it should trigger in ms (cycle), and an unsigned long to keep track of the last millis() reading since the last event (lastMillis). You call the function passing the address of the lastMillis for that event and the cycle for that event. The function returns a true if the event is due for action, and a false if the syscle time hasn't elapsed yet.

Typical usage example (Simple blinking 2 LEDs at different rates)

Code: [Select]
#define ledPin1 11
#define ledPin2 12

#define led1Cycle 100U
#define led2Cycle 275U

unsigned long led1LastMillis = 0;
unsigned long led2LastMillis = 0;

boolean led1State = false;
boolean led2State = false;

boolean cycleCheck(unsigned long *lastMillis, unsigned int cycle)
{
 unsigned long currentMillis = millis();
 if(currentMillis - *lastMillis >= cycle)
 {
   *lastMillis = currentMillis;
   return true;
 }
 else
   return false;
}

void setup()
{
 pinMode(ledPin1, OUTPUT);
 pinMode(ledPin2, OUTPUT);
}

void loop()
{
 if(cycleCheck(&led1LastMillis, led1Cycle))
 {
   digitalWrite(ledPin1, led1State);
   led1State = !led1State;
 }
 if(cycleCheck(&led2LastMillis, led2Cycle))
 {
   digitalWrite(ledPin2, led2State);
   led2State = !led2State;
 }
}


I've found this to work very well, and it keeps the main loop code very clean. You can even make the cycle times variable by making them variables instead of #defines. Then you can change the cycle times during execution. For example, you want to change the blink rate with a potentiometer.
Title: Re: Arduino Multithreading
Post by: MikMo on Jan 06, 2009, 07:23 am
That's a very good generalized solution to a common problem.
Title: Re: Arduino Multithreading
Post by: dmesser on Jan 06, 2009, 07:25 am
Thanks. Hope it's useful to some... Someday I'll make the function deal with millis() rollover, too....
Title: Re: Arduino Multithreading
Post by: skumlerud on Jan 07, 2009, 12:25 pm
Quote
For each event you want to deal with, set up an unsigned int for how often it should trigger in ms (cycle), and an unsigned long to keep track of the last millis() reading since the last event (lastMillis). You call the function passing the address of the lastMillis for that event and the cycle for that event. The function returns a true if the event is due for action, and a false if the syscle time hasn't elapsed yet.


I use a very similar function myself, the difference is that I'm using a struct to hold the last_millis- and interval-values. I'm also using similar functions to poll switches, changes in analog input etc. As you said, it keeps the main loop clean and the code is much easier to follow.
Title: Re: Arduino Multithreading
Post by: dmesser on Jan 07, 2009, 06:03 pm
I like the idea of using a struct for the last millis and cycle parameters. Good idea.
Title: Re: Arduino Multithreading
Post by: dcb on Jan 07, 2009, 06:26 pm
Maybe have a look at http://www.bdmicro.com/code/threads/ too.

Title: Re: Arduino Multithreading
Post by: jkone27 on Feb 17, 2010, 04:43 pm
Hello i have to play a sound on a buzzer (so repeat HIGH and LOW on its pins, at a given frequency (the note)) and while i'm doing this, i have to check if to continue or not.

i check from an incoming message from the usb (a midi message), if the message is NoteOff then it has to stop the BUzzer.

maybe i could use this code to deal with that? :-?
Title: Re: Arduino Multithreading
Post by: mem on Feb 17, 2010, 08:31 pm
giacomo, have a look at the Arduino tone function introduced in release 0018: http://arduino.cc/en/Reference/Tone
Title: Re: Arduino Multithreading
Post by: jkone27 on Feb 22, 2010, 04:34 pm
thaaaaaanks!
Title: Re: Arduino Multithreading
Post by: AlphaBeta on Feb 22, 2010, 04:48 pm
Could have a look at this next time you need to time some reoccurring actions:
http://www.arduino.cc/playground/Code/TimedAction
:)
Title: Re: Arduino Multithreading
Post by: efficens on Apr 09, 2010, 08:51 pm
Hi.

I wrote some lines using structs and 2 task running, one for send display data and other for read keyboard buttons.

Task for display run every 3000 milliseconds
Keyboard task run every 100 milliseconds

Enjoy ...

Bruno

typedef struct Timer
{
   unsigned long start;
   unsigned long timeout;
};

char TimerExpired ( struct Timer * timer )
{
   if ( millis () > timer->start + timer->timeout )
       return true;

   return false;    
}

void TimerStart ( struct Timer * timer )
{
   timer->start = millis ( );
}

//Display task running every 3000 milliseconds
Timer timerDisplay = { 0, 3000 };
//Keyboard task running every 100 milliseconds
Timer timerKeyboard = { 0, 100 };

void setup (void)
{
}

void loop (void)
{
   if ( TimerExpired ( & timerDisplay ) )
   {
       taskDisplay ( );
       TimerStart ( & timerDisplay );
   }

   if ( TimerExpired ( & timerKeyboard ) )
   {
       taskKeyboard ( );
       TimerStart ( & timerKeyboard );
   }
}

void taskKeyboard ( void )
{
}

void taskDisplay ( void )
{
}
Title: Re: Arduino Multithreading
Post by: federico.dami on Apr 11, 2010, 08:05 pm
Try this:
http://robotgroup.com.ar/noticias/20091102/duinos-sistema-operativo-multitarea-para-arduino
Title: Re: Arduino Multithreading
Post by: Tchnclfl on Jun 29, 2010, 06:11 am
You can you use the Arduino's in-built PWM timers to change the brightness (as you see it) of an LED.

Check out analogWrite() (http://www.arduino.cc/en/Reference/AnalogWrite).
Title: Re: Arduino Multithreading
Post by: juliandasilva on Aug 12, 2010, 12:30 am
Hi, the link to download DuinOS had changed to this:

http://robotgroup.com.ar/duinos/wiki

There you will find in the downloads section the v0.1 and the v0.2 versions.

Regards,
Julián
http://robotgroup.com.ar
Title: Re: Arduino Multithreading
Post by: juliandasilva on Aug 25, 2010, 02:46 pm
Hi, the link to download DuinOS (the port of FreeRTOS to Arduino) had changed to this:

http://robotgroup.com.ar/duinos/wiki

There you will find (in the downloads section) the v0.1 and the v0.2 versions.

Regards,
Julián
http://robotgroup.com.ar
Title: Re: Arduino Multithreading
Post by: juliandasilva on Aug 25, 2010, 02:48 pm
I forgot the forum DuinOS discussion thread:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1256745982/0

Regards,
Julián
http://robotgroup.com.ar
Title: Re: Arduino Multithreading
Post by: ooishi on Aug 31, 2010, 10:24 am
Hi.
I want to keep pushing the button to the sound which is played at regular intervals.And,I want to output the time the button is pushed.
At this moment,I respectively made the module which outputs time by the button push and the module which plays a sound at regular intervals.

1.the module which plays a sound at regular intervals
Code: [Select]

#include <Tone.h>
Tone notePlayer;

void setup(void)
{notePlayer.begin(13);}

void loop(void)
{notePlayer.play(1000,50);  
 delay(1000); // ISI
}


2.The module which outputs time by the button push
Code: [Select]

#define BUTTON 7

int val=0;
int  old_val=0;
unsigned long time=0;

void setup(void)
{pinMode(BUTTON,INPUT);
Serial.begin(9600);}

void loop(void)
{ val=digitalRead(BUTTON);
 delay(5);
 time=millis();
 
if (((val==HIGH) &&(old_val==LOW)))
{Serial.println(time);}
 
old_val=val;  }


Next, I want to integrate these by using multithread.
What should I do?
Title: Re: Arduino Multithreading
Post by: pluggy on Aug 31, 2010, 10:47 am
Lose the delay statements, take a look at the blink without delay example in the IDE, then the two sketches have to be interleaved so they work as one.  

The arduino is a primitive computer by anything approaching modern standards, it has no multithreading or multitasking ability, it can do only one thing at once, although with care, it needn't be much hardship.
Title: Re: Arduino Multithreading
Post by: marklar on Sep 02, 2010, 02:21 pm
I would not attempt to check them both in the loop due to the fact that your loop may be busy and not see the button being pressed.

Here is a working example of how I multi-task using your button example code.  This should just plug right in.

In your exact case the loop process may be quick .. but that is bound to change so a process like this will help you be able to expand your application later.

Overview:
You can use a timer for the button check.

If you do use a timer, you will want to remove the delay or your code will stall due to not being able to complete the task in the small timer window.

You should not need the delay or need to debounce if you put your button check code in a timer routine.

Example Code:
Code: [Select]

//Find timer at http://www.arduino.cc/playground/Main/MsTimer2
#include <MsTimer2.h>


//--- Button Pin
#define BUTTON 7
//--- used to store old value
int  old_val=0;
unsigned long time=0;
boolean buttonPressNew = false;

void checkButton(){
 //IMPORTANT: NO DELAY OR LONG ACTION HERE .. just change button status and take action in the loop

 //-- no need for val to be global .. just takes up 2 bytes for nothing being global.
 int val = digitalRead(BUTTON);
 //-- If they are not the same then ...
 if( val != old_val ){
   //-- Reset last value
   old_val = val;
   //-- If value is turned on .. then we need to tell the system it was hit once ..
   //   but not do it over and over until released
   if( val ){
     buttonPressNew = true;
   }
 }

}

void setup() {
 //--- Starndard Setup
 pinMode(BUTTON,INPUT);
 Serial.begin(9600);

 //--- Initialize timer2 .. using 20ms for this example
 MsTimer2::set(20, checkButton);
 MsTimer2::start();
}

void loop() {
 //Do any normal processing here ... it can run as long as you like and not effect the reading of the button.

 //Simple Example ... a delay .. this will only process a single button click.
 // so in this example .. if the button is pressed twice in this delay period .. it picks up only one time.
 // if the button check was in the loop .. it would not see the button at all.
 // if multiple button presses being excepted while in this delay .. and the action is quick .. it can be run in the loop
 //   or a counter added
 delay(5000);
 
 // This flag is set when a button is pressed .. take action in the loop and reset
 if( buttonPressNew ){
   // Take action based on the button being pressed...

   // then reset
   buttonPressNew = false;
   //IMPORTANT: Notice serial printing done here .. not in timer process
   Serial.println("Button Was Pressed");
 }
 
}



Final Notes:
You could add a counter to track the number of times the button was pressed while the loop was busy if desired.  

Also .. if the action you are taking is really fast .. then you can go ahead and just take the action right in the checkButton() function .. you may need to expand beyond 20ms to fit in your process.  Expanding too far will effect your button press responsiveness.


Hope this helps.
Title: Re: Arduino Multithreading
Post by: mpeuser on Sep 02, 2010, 04:28 pm
Just as an exercise, this is an example of typical co-operative scheduling. As C does not have co-routines there is a trick needed to split the time consuming phases of tasks into sections, by switching into thenext section through action codes.

This example can easily be extended by more specific return codes, as for priorities, or synchronization to time marks...
Code: [Select]
// Simplified co-operative scheduling for Arduino
// 2010 by DeSilva v1.0

byte (*action[])(byte,long) ={pA, pB};
const int nmbActions =sizeof(action)/sizeof(&pA);
byte actionCode[nmbActions] ;
byte nextAction =0;

void setup() {
 
}

void loop() {
   actionCode[nextAction]  = (*action[nextAction])(actionCode[nextAction], millis());
   if (++nextAction>=nmbActions) nextAction = 0;  
}

byte pA(byte code, long theTime){
 switch (code) {
   case 0:
        return 1;
   case 1:
        return 2;
   default:
        return 0;        
  }  
}
byte pB(byte code, long theTime){
   return 0;        
}