For their recent high altitude balloon project LVL1 member [Brad] programmed a pretty complicated brain based on an Arduino. It was responsible for collecting data from all of the sensors, and reporting back in a few different ways. One of the things he did to simplify the project was develop a task scheduler for the Arduino board. It lets you add functions to a queue of jobs, along with data about when they should be run.
The task scheduler does make coding a bit easier, but where it really shines is in situations like this where you don’t have access to the hardware if there’s a problem. In his description of the scheduler [Brad] mentions the possibility that one of the sensors could fail as the cold of the upper atmosphere takes its toll. This could leave the whole system stuck in a subroutine, and therefore it will stop sending reports back to the team on the ground. Since he was using the task scheduler it was a snap to add watchdog timer servicing to the mix. Now if program execution gets stuck the watchdog will reset the chip and all is not lost.
[Thanks JAC_101]
Just as I start to look deeper into Arduino this pops up and will certainly save me some headaches.
If some sensor is causing program to malfunction, probably it will malfunction again a moment after restart. Nice scheduler though, have you considered using linked list with sorting by insertion? Worked for me, I could use more than 15 events but when less, it didn’t use so much memory. Such task scheduler helps with debouncing and interactivity. When you detect key press just schedule checking if its still pressed after 5ms and you still can do other things in the meantime.
The watchdog wasn’t so much to avoid failing sensors (the sensors were very reliable!), but to avoid failures in other systems from affecting the flight computer. The ballast controller, sensor controller and communications controller all had their own microcontrollers communicating over the I2C bus. Due to the craptacular nature of the Arduino Wire library, they would fail with more frequency than we wanted for our transatlantic flight.
With the watchdog, the flight computer could survive a failure ultimately caused by an outside system, and carry on.
Since we wouldn’t be allocating memory dynamically, we went with a ring buffer instead of a linked list. The ring buffer is slightly smaller and speedier with that constraint.
Cute design, but many of us that have been writing embedded code have been doing this for decades. In areospace a failed sensor reports a failed response and after 3 fails it is not read again until a commande from ground control to attempt again.
They are doing bi directional telemetry right?
Never tried to claim it was novel or groundbreaking, just trying to publish something for others to use. When I started working on this, I was unable to find anything comparable for Arduino/AVR.
My balancing robot has something similar: https://github.com/lpereira/Balancing-Robot
And, yes, schedulers like these are pretty straightforward to implement, and saves a lot of time if you need to run multiple things in different frequencies. My robot uses that to run the PID controller at 100Hz, receive radio commands (at some other frequency I forgot), and, of course, blink an LED (at 1Hz). These things wouldn’t been difficult to write without a task manager, but hey — it was fun to write.
Things get (a little bit more) hairy if preemptive multitasking is needed.
Great work! This is exactly how the Apollo Module & Lander was coded to handle the moon landing. Handle the navigation and landing tasks no matter what.
Cool Documentary on this – Moon Machines
http://youtu.be/4oukJyZBpx0
And as of right now their server is overloaded.
Update: Back online.
Wow, I thought I heard hints at this before. Incredible it finally exists!
LVL1 has contributed so much.
Next up: discovering the unbelieveable ‘volatile’ keyword !
Could solve save me some headaches :D Thanks.. What IDE/Editor is that?
Haha Woops.. Ignore my comment Sorry
How does this differ from DuinoOS, the Arduino RTOS? I wrote all my code like the millis() checking in the BlinkWithoutDelay sketch, but I don’t think that is the best way. What is the best way to schedule things?
DuinoOS includes a lot of additional functionality, in addition to having a task scheduler. Their task scheduler has more features than this one. This is great if you need what they’re offering.
Not everyone needs a complicated RTOS or Task Scheduler, though. This one is small and to the point, which certainly has some benefits.
A task scheduler is a simple time management software. Simple forms can be used w/o having the additional library code required. It is as simple as requesting the “time”, comparing it to an old time and see if enough time has passed. It is really efficient and allows some blocks of code to run faster than other blocks of code. etc…
Yes, but there is a correct and incorrect way to do this. If you just check the time over and over with no gaps, it actually disables the millis value increment since millis() disables the interrupt while fetching the value.
I believe the correct way to switch between tasks is to do what the example sketch does above and use delay() for the GCD of your task intervals so that your microcontroller can sleep and not keep checking the time.
If you check the interval at thge top of your loop it is not “disables the millis value increment”. I run serial retrieval and frame recognition code in my “off time”. It is not Costently checking the millis fcn…
also, delay(x) doesn’t put the arduino to sleep….
Using http://bleaklow.com/2012/06/06/avr_performance_monitoring_using_the_openbench_logic_sniffer.html could be used to determine what type of task scheduler/manager/rtos would be best suited for the given need.
A few takes on the “task” scheduler idea, http://bleaklow.com/2010/07/20/a_very_simple_arduino_task_manager.html
OR
reduce the code -> http://www.robopeak.net/blog/?p=131
I added a similar feature to MHVLib for AVRs a while back:
http://www.makehackvoid.com/project/MHVLib
One of the problems with this approach is that the tasks are running in the interrupt context, so long-running tasks can prevent other interrupts from executing.
My solution was to set a volatile flag and poll this in a loop in main(), together with a sleep to power down as much of the microcontoller as possible until the next event occurs.
See sleep_cpu()/sleep_mode() in the AVR LibC documentation:
http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
For an example of the RTC usage in MHVLib, check the mhvlib-tutorial-RTC project included in the MHVLib distribution (above).