Multitasking question

I have a newbie question regarding multitasking. I want a sketch that goes like so:

void loop() {
  if(interval elapsed) {
    do your short task
  }
  if(data from Serial arrived) {
    do your huge task
  }
}

What I need is sketch doing its business checking time and performing simple repetitive task, and if the right conditions are met to calculate horrendous things in the background all while maintaining its simple routine. I seem not to be able to find how to go about this. Can you help me?

if the right conditions are met to calculate horrendous things in the background

You will need to break your "horrendous" task into smaller steps to enable the repetitive task to carry on.

Look at Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

Multitasking is something else, on a small microcontroller there's only one thread of control and its used to handle events, and where possible without blocking. Background processing ties up the processor unless you have real multitasking or break up the processing into tiny steps.

To truly multitask you need multiple hardware stacks, which generally requires more memory resources than the ATmega328.

But there is a way sometimes to get round this, which is when all the event processing can be done with ISRs, then the main program can do the background task without blocking progress.

There are RTOS libraries available for Arduino that'll allow you to have multiple threads albeit only running one at any moment.

Or you can code a way to do bites of the big task quickly enough to get back to the little task in time as UKHeliBob suggests.

But this sounds like a job for different hardware - consider a Raspberry Pi.

wildbill: But this sounds like a job for different hardware - consider a Raspberry Pi.

Just depends on ;)

@JanKokes, read through this example. It kinda does what you are suggesting. Make sure you do not add any blocking code.

https://forum.arduino.cc/index.php?topic=525240.0

uMT works great on the DUE with a single core processor in making it look like there is some multitasking going on. I leave the loop() function empty with uMT. uMT runs really nice on the DUE.

Or you can try an ESP32 and freeRTOS using the   xTaskCreatePinnedToCore( fDoLIDAR, "fDoLIDAR", TaskStack10K5, NULL, Priority4, NULL, TaskCore1 ); //assigned to core 1. And xTaskCreatePinnedToCore( fLIDAR_ServoAspectChange, "fLIDAR_ServoAspectChange", TaskStack10K, NULL, Priority4, NULL, TaskCore0 ); // assigned to core 0 as an example You can assign a task to a core and have a 'real' multitasker with careful planning of your tasks operations. With an ESP32 and freeRTOS, code in the loop function will, most likely, not be ran.

With uMT or ESP32 running freeRTOS loop looks like this void loop() {} // loop

Multitasking is easy peasy on Arduinos. I run.. 98% of my stuff in the "background".

Here's what you will need to do it, in a marketing arm waving kind of description.

1 A dynamic memory link list library. (Very handy!) You'll need a linked list manager class and a linked list object class.

2 Inherit these two classes. The manager becomes your background task manager. Your list objects become the base class your classes will inherit to make yours have an idle() method. (And a hookup() method)

3 Create a global instance of your manager class with a global function "void idle(void)" When you call this global function, the manager just runs down its linked list and calls every linked object's idle() method.

4 Place your global idle() function, as the first thing call in your loop(); function.

Now, each of your classes that inherit from "idler" will have an idle() method that is called repeatedly. You can have as many as you have RAM for, and they will all work away at their individual tasks. It works great for things like screen buttons and animations. Watching serial ports, managing hardware communication etc.

That's how I do it. Love how it works!

-jim lee

I reckon the approach in Several Things at a Time is even simpler. (And I am not claiming credit for any of the concepts within it)

...R

UKHeliBob: You will need to break your "horrendous" task into smaller steps to enable the repetitive task to carry on.

I thought this would be the case. Thank you.

MarkT: ...ISRs...

Got it. Thank you. I told you I was newbie.

wildbill: There are RTOS libraries available for Arduino that'll allow you to have multiple threads albeit only running one at any moment.

Or you can code a way to do bites of the big task quickly enough to get back to the little task in time as UKHeliBob suggests.

But this sounds like a job for different hardware - consider a Raspberry Pi.

I found some but I was uncertain whether they are solution for me. I made application model in html and javascript for easy debugging and I discovered I needed about 3,000,000 bools to hold in an array, which is not possible, so I made two arrays, one with chunks of bools and second as a conversion table to tell me when to use which row of the first table (I came from web apps development, including DBs), all this only to make sure I will not run out of SRAM. I count every single variable in the sketch. I am afraid of using libraries which could make my code unrunable it they use too much.

Yes, that will be the first thing I will try. However I would prefer to have control of motors even during calculation, because otherwise they may be moved by spindle etc, and I have no feedback from rotary encoders because those are 3-phase motors and I need 6 pins per motor, meaning I already ran out of pins just for motors and nothing else. Should the motors slip little error may occur and aggrevate over time.

I have Arduino UNO at a time and no chance of getting anything stronger soon enough. I have been naughty at work and now I have really bad references, so I dwell at gvt. support and work my way up again, in slightely different field. I already have a very fine job proposal if I can do what I am doing.

larryd: @JanKokes, read through this example. It kinda does what you are suggesting. Make sure you do not add any blocking code.

https://forum.arduino.cc/index.php?topic=525240.0

This looks promissing. It does mention high demands on SRAM, but that would be the case in either way. I saved the page with pdfs and will study it at home, I only have very limited time online, frequenting libraries. The ones with books. Same applies to ISRs and other things I may yet discover.

Idahowalker: uMT works great on the DUE ...

Wonderful, thank you. I will consider DUE prior to Raspberry. I had no idea some Arduinos had dual processors. However I will not give up UNO any time soon.

jimLee: Multitasking is easy peasy on Arduinos. I run.. 98% of my stuff in the "background".

Here's what you will need to do it, in a marketing arm waving kind of description.

1 A dynamic memory link list library. (Very handy!) You'll need a linked list manager class and a linked list object class.

2 Inherit these two classes. The manager becomes your background task manager. Your list objects become the base class your classes will inherit to make yours have an idle() method. (And a hookup() method)

3 Create a global instance of your manager class with a global function "void idle(void)" When you call this global function, the manager just runs down its linked list and calls every linked object's idle() method.

4 Place your global idle() function, as the first thing call in your loop(); function.

Now, each of your classes that inherit from "idler" will have an idle() method that is called repeatedly. You can have as many as you have RAM for, and they will all work away at their individual tasks. It works great for things like screen buttons and animations. Watching serial ports, managing hardware communication etc.

That's how I do it. Love how it works!

-jim lee

I have really hard time understanding. It sounds great however. Little more digging to do. Excellent. I thought you might answer me, I see your posts all over the place.

Robin2: I reckon the approach in Several Things at a Time is even simpler. (And I am not claiming credit for any of the concepts within it)

...R

Again, it looks promissing, and when I looked for it I did not find it. Thank you. Should I post my results back onto a forum when I have them?

JanKokes: Should I post my results back onto a forum when I have them?

Keep going with this Thread. Your experience may be useful for others.

...R

As a note: a Raspberry Pi will be a lot slower than a ESP32. The RPi has a lot of system overhead.

I got it working.

In the end ignored all the good advices and pioneered my own way using TimerOne library. It was very convenient in my case because it takes microseconds in parameter, it does what I need and when testing it gobbled 11 bytes of SRAM, which is acceptable sacrifice in my eyes. I was ready to accept 512 or less. I used little recursive function to effectively create second loop independent of first one. Rough tests turned out ok, so I will use that unless it turns sour later on.

Direct port manipulation would be preferred to digitalWrite but I had no pin mapping table at the time of writing.

#include <TimerOne.h>

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  Timer1.initialize(1000000);
  Timer1.attachInterrupt(fn); // first call to recursive function
}

void loop() {
  delay(3900); // large task
  Serial.println("MAU");
}

void fn(){ // recursive function 
  Serial.println("faffaffaffaffaffaffaffaf");
  digitalWrite(13, !digitalRead(13)); // small task
  Timer1.attachInterrupt(fn); // when done call yourself (and again and again...)
}

JanKokes: Timer1.attachInterrupt(fn); // when done call yourself (and again and again...)

I think you over-simplified the example. You don't need to re-attach. It didn't detach by itself.

Question: how do you add a third task using your method?

I thought you might answer me, I see your posts all over the place.

Really? That in it self is somewhat frightening!

-jim lee

I probably search things you are interested in.

Hi all, just a quick update. Things are not looking pretty.

I finished my application and spent many evenings tweaking things to make it run as fast as possible. With much difficulty I made small task to run 2 ms and large task 46 ms. These are values I can live with, they enable me to run three motors 6 pins each at maximum frequency of about 40 Hz, or 2400 rpm. So far so good. The catch is that when I turn the threading on I need to increase the small task time to 10 ms to run at all and large task takes whooping 2360 ms. This means that if I run gcode with say 1,000,000 lines it will take over 4 years to complete. This is the case with Timer1, with Thread it takes longer still.

One of my choices is to inline the script and live with it 46 ms not holding motors hoping they won't get too far off during such short period. I may add ballast to z axis to counterballance its weight and/or perhaps jerk the spindle up a little when one line is done and back down again when another line starts. The motors are brought to full stop at the end of each line by sw, so the only problem are those 46 ms from stand still. I am not sure how to deal with them.

Another option may be to add physical brakes, but they may take longer than that to turn on or off. Plus I have no pins left.

Yet another option is stronger machine preferably with multicore processor or some sort of farm.

If you have any ideas on the subject please let me know.

JanKokes: The catch is that when I turn the threading on I need to increase the small task time to 10 ms to run at all and large task takes whooping 2360 ms.

Without seeing your program how can we know what might be an improvement.

One of the problems with "clever" multi-tasking arrangements is that they consume CPU cycles. No such thing as a free lunch.

A less clever system (even though it may take longer to write the program) might be faster.

An interrupt is invaluable when you want to capture a fleeting event. But every interrupt call consumes a significant number of CPU cycles to save the program counter, jump to the ISR code, restore the program counter and jump back to the main program. If there is a need to save and restore other registers the overhead is even greater.

And, of course, it may be a better use of your resources (time vs money) to buy a faster microprocessor rather than to optimize your code on a slow microprocessor.

Have you tried the digitalWriteFast library - it is nearly as fast as port manipulation but a lot easier to use. However it only works if the I/O pins are known at compile time.

...R

Thaks for the reply. I agree, faster processor. Slow switching is not the problem, even with digitalWrite it takes 4us for each pin which is at least 2 orders of magnitude less than small task with no threading. But I can surely use it, why not. Time is not all that much of a concern, I work on machine by myself for myself so there are no deadlines.