
Today, I propose a light C# library to perform Ev3 MultiThreading, with which you will be able to program your droid to perform multiple tasks at the same time. You can download and use the library source code under the MIT License from Smallrobots repositories on GitHub.
Summary
→ Credits
The need for a scheduler
With the LEGO MINDSTORMS EV3 Home Edition software, you can program the EV3 Brick to perform multiple concurrent actions easily enough using its graphical tools. However, It’s not so straightforward to do the same things using the C# and the .NET Framework with the Monobrick firmware library.
If you have a look at the first programs I’ve published on this blog, namely the Sup3rCar and the R3PTAR, you’ll notice that the scheduling of the periodic control tasks is delegated to the .NET Framework threading libraries. Pro: super-tested routines with less code to write and easy to maintain – you just ask the .NET Framework to wake up your thread when the sleep time elapses. Cons: there is no guarantee that each periodic routine is executed exactly each time its deadline is due.
The following example shows the routine that generates the sine wave that the R3PTAR follows to emulate slithering like a snake. Even if the thread is not woke up precisely each waveThreadSampleTime milliseconds, no one will notice 🙂 !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/// <summary> /// Generates a sinusoidal wave /// </summary> void WaveThread() { UInt64 k = 0; // Discrete time counter double t = 0; // Time double angularPulsation = 2 * Math.PI * frequency; // Angular pulsation while (!stopWaveThread.WaitOne(waveThreadSampleTime)) { // Current time t = ((double) k * waveThreadSampleTime) / 1000; // Wave signal computation wave = meanValue + amplitude * Math.Sin (angularPulsation * t); // Update next discrete time instant if (k < UInt64.MaxValue) { k++; } else { k = 0; } } } |
Generally speaking, when the system under control is stable, only control performances are affected if a periodic control task experiences random delays at its execution start; the eventual decay of performances is acceptable or not depending on the particular application. However if the system under control is unstable, even a good control strategy will likely fail to stabilize the system if its timely execution is not guaranteed.
The Rolling Ev3rstorm clearly represents an unstable system: if the program on board the Ev3 Brick fails to stabilize the robot upright on the two wheels… well you can easily imagine what happens 🙂
During my first attempts to stabilize the Rolling Ev3rstorm, I used the .NET Threading API to schedule the tasks: no matter what values I did set for the controllers gains, it always fell down; that’s why I concluded that something was wrong with the control algorithm implementation and not with control algorithm itself.
The Ev3 MultiThreading scheduler description
The idea is then simple: instead of delegating the task scheduling to the .NET Framework, I designed a simple scheduler, implemented basically as an infinite loop, which starts the periodic tasks at the correct time.
The class diagram below shows the general idea: a Robot owns a TaskScheduler which schedules PeriodicTask (s). Please note that a the time of writing the proposed scheduler works only with periodic tasks.

The scheduler works as follows:
- Check which is the next PeriodicTask in the queue and determine its next due time;
- If the next due time is far to come, put the scheduling thread to sleep until about 200ms from next due time;
- Actively wait for the time to reach next due time;
- Start the scheduled task on time;
- After task execution re-schedule the task in the queue according to its next due time;
- If there is a conflict with an already scheduled periodic task, schedule the new due time after the conflicting periodic task to avoid task starvation.
- Repeat from point 1. until the scheduler gets stopped.
Point #3 will of course consume all the computing resources of the Ev3 Brick. But if all the droid activities are coded as periodic tasks, each of the task will be always executed on time.
Thing to note: the scheduler is not preemptive. This means that is your responsibility to write tasks that terminate correctly before it’s time for the next task to start. This also means that if a single task crashes, all the robot will crash. While this could be certainly considered a major issue in real robotic applications, for our little Lego Mindstorms Ev3 droid friends doesn’t matter too much.
See the code below for the TaskScheduler :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
/// <summary> /// The scheduler algorithm /// </summary> protected void theScheduler() { // Sleeps until first due time stopWatch.Reset(); stopWatch.Start(); // LcdConsole.WriteLine("Stopwatch started"); if (taskQueue.Count != 0) { while (keepScheduling) { // Gets current time task data long msec = stopWatch.ElapsedMilliseconds; long dueTime = taskQueue.Keys[0].nextDueTime; long numberOfExecutions = taskQueue.Keys[0].numberOfExecutions; // LcdConsole.WriteLine("Extracting data"); // Time to wait until next execution int rest = (int) (dueTime - msec); // Put this thread to sleep if the dueTime is further than 200ms if (rest > 200) { // LcdConsole.WriteLine("Waiting asleep"); Thread.Sleep(rest - 200); } // When dueTime is closer than 200ms, this function actively waits the stopwatch while (true) { // LcdConsole.WriteLine("Waiting awake"); if (stopWatch.ElapsedMilliseconds >= msec + rest) { break; } } // When the stopwatch reaches the dueTime, the callBack function is invoked // But before checks the the scheduler has not been stopped // while waiting if (keepScheduling) { PeriodicTask task = TaskQueue.Values[0]; if (withLog) { // Logs the execution time executionLog = executionLog + stopWatch.ElapsedMilliseconds.ToString() + " " + task.Name + "\r\n"; } // Executes the action // LcdConsole.WriteLine("Launching action " + task.Name); // LcdConsole.WriteLine("Sched error: " + Math.Abs(dueTime - stopWatch.ElapsedMilliseconds).ToString("F2")); task.Action(robot); // Determines next due time // LcdConsole.WriteLine("Computes next due time"); long nextDueTime = (numberOfExecutions + 1) * task.Period; bool slotFound = false; // Updates the due time PeriodicTask updatedTask = task; taskQueue.RemoveAt(0); while (!slotFound) { // Check for task already scheduled at the same time if (AlreadyScheduled(nextDueTime)) { // If a task is already scheduled at the same time // try next millisecond nextDueTime = nextDueTime + standardDelayWhenOverscheduled; } else { // First task to be scheduled at this nextDueTime slot slotFound = true; } } // schedules the task TaskTimingStructure key = new TaskTimingStructure(nextDueTime); key.numberOfExecutions = numberOfExecutions + 1; taskQueue.Add(key, task); } } } stopWatch.Stop(); } |
Three examples with videos
The name I’ve given to this C# library is Ev3ControLib. Check the following post in this blog for some examples of droid programmed using this library:
- The Rolling Ev3rstorm – a self balancing droid using the Ev3 Gyro Sensor chasing the Ev3 IR Beacon and firing at it when at firing range
- The Formula Ev3 – a racing car which can be remotely piloted with the Ev3 IR Remote
- The Sentin3l – an insect like droid remotely guided with the Ev3 IR Remote
Credits
In his work published on GitHub, Tomoaki Masuda presents a single periodic task executed with high timing accuracy using the concept of actively waiting for the due time when reaching its close proximity. In Ev3ControlLib library I extended this interesting concept to a periodic task scheduler to run multiple task at the same time on the Lego Mindstorms Ev3 Brick with the Monobrick firmware library.