More complex, complete examples that do several things at once

JimboZA:
Bacon never takes that long to cook, though...

You haven't met my wife...

Thanks a lot for the responses, very helpful.

I really would love to see a more complete example of a real use-case doing LCD (or some other form of) UI, serial and some other things at once. In principle I know how it should be done, but like with the simpler examples, seeing how others do it (and do it well, unlike my own first exeriments) would help me, I think.

Are there no examples of larger projects with complete sourcecode to be examined by newbies?

It is also interesting to me how people organize code, what conventions they invent, etc.

Also it would interest me, how much people fit into their Arduinos

Using interrupts would be a simple way to achieve that.

My advice here (the programming, not cooking bacon) is to chart the program first. There are a number of ways to chart programs including the simple flowchart (which I learnt when learning Fortran) and the structure chart (which I learnt when learning Cobol).

Many many coding problems stem from us diving into the code without having thought through what it is we're trying to do, and how. And the more complex it is, the more fraught with danger it is....

Then once it's charted, maybe even flesh it out further on paper in pseudo-code. Pseudo code lets you write down the logic of the program without getting tied up in the nuances of the syntax.

And make a "coding to-do list" by mapping out the sketch (to use Arduino-speak) by typing in a heap of comments showing the structure of the code and that will remind you to code each part and not forget stuff.

I ALWAYS* do it that way....

Jim

  • just kidding

Hi,
This is not a great example as the code has 'evolved' rather than follow a plan but it does update an LCD Every second, respond to new lap times instantly, plays an audio count down while responding to laps and updating the LCD, operates in a menu, lap review or recording mode etc. It does so through the two mechanisms already mentioned - interrupts for laps and time/timestamps for screen and audio updates.

PM Me your email address for a zip file of the code - like I say its not text book perfect but is an example of a very usable project that has been rapidly built/evolved on Arduino -

Duane B

rcarduino.blogspot.com

dhenry:
Using interrupts would be a simple way to achieve that.

Strongly disagree. Using interrupts is a very poor and unscaleable way to manage concurrent activities. Retrolefty has already explained how to write non-blocking code that enables a sketch to manage multiple concurrent activities in a simple structure which scales up very well. Using interrupts does not help with this problem, and just introduces a whole new layer of complexity to be dealt with.

You have to choose what you need and for what reasons. Interrupts are often required for precise timing. Non blocking code is required for optimal resource utilization. Depending on what you want to achieve you may have to do both. For non blocking code it is often a good idea to think about state engines. Here is an example that uses both a state engine AND interrupts for different purposes: Flexible Sweep | Blinkenlight.

Bacon never takes that long to cook, though...

If you do it right, it does. I cook bacon on a rack in the oven on 350 degrees (F, for those on the other side of the pond). No grease splatter, the the bacon always comes out crisp. It takes a bit longer than a frying pan, but, then cleanup doesn't take all day, either.

ausserirdischegesund:
I absolutely love the way Arduino makes it easy to interact with the physical world. No matter what, you find easy to reproduce examples that do what you want in a few lines.

What I am struggling now is doning several things at once, e.g.

TIA
/ralph

Check out the "Timer One" library: Arduino Playground - Timer1

It allows you to run code at a defined repetition rate WHILE running other code.

It's useful if you want to do something at precise time intervals WHILE doing other things in the background (and don't want to worry how much time your other code uses).

All it really does is implement an ISR, but it's done with the high level library so all you do is specify the repeat rate and tell it which function to attach to (rather than complicated bit-level coding that would otherwise be required).

Hope this is of some help to you.

If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.

dc42:
If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.

Is that code available for general use?

I really would love to see a more complete example of a real use-case

A simple case would be something like this:

  static unsigned char current_task=0; //task indexer
  switch (current_task) {
    case 0: run_task0(); //run task0
               break;
    case 1: run_task1(); //run task1
               break;
    ...
    default: do_nothing(); //do nothing
  }
  current_task = (current_task == MAX_TASK-1)? 0: (current_task + 1); //increment to the next task

so it will run task0..x, each time it is invoked.

It is essentially a primitive task scheduler. It does require that each task will eventually exit (hopefully quickly).

You can fancy it up with other features but this basically is it.

ausserirdischegesund:
I really would love to see a more complete example of a real use-case doing LCD (or some other form of) UI, serial and some other things at once. In principle I know how it should be done, but like with the simpler examples, seeing how others do it (and do it well, unlike my own first exeriments) would help me, I think.

Are there no examples of larger projects with complete sourcecode to be examined by newbies?

It is also interesting to me how people organize code, what conventions they invent, etc.

Have a look at my project, it is written in a state machine way and monitors the lid continuously while playing the game:-
http://www.thebox.myzen.co.uk/Hardware/Dice_Game.html

dhenry:
A simple case would be something like this:

What do you see as the advantage of that, compared to just calling the tasks directly?

run_task0();
run_task1();

Of course you wouldn't use those names, you use names that tell you what the different functions actually do, like handleSerialPort(), handleButtons() etc.

Here is a short demo: running 5 tasks (the last one isn't actually executed, just to show as an example):

//demo for multi-tasking
#define MAX_TASKS  5 //maximum number of tasks

//task 0 - lcd1602
#include "LiquidCrystal.h"
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 13, 8, 9, 10, 11);
void task0(void) {
  static unsigned char i=0; //count
  lcd.setCursor(0, 1); //column 0, line 1
  lcd.print(i++); //print count, then increment it
}

//task 1
#define LED_1 1 //led1 on pin1
void task1(void) {
  static unsigned char count=0;
  count+=1; //increment count
  if (count==100) {
    count -= 100;   //reset count
    digitalWrite(LED_1, !digitalRead(LED_1)); //blink led1 every 100 turns
  }
}

//task 2
#define LED_2 2 //led1 on pin1
void task2(void) {
  static unsigned char count=0;
  count+=1; //increment count
  if (count==200) {
    count -= 200;   //reset count
    digitalWrite(LED_2, random(0,2)); //randomly turn on / off led_2
  }
}

//task 3
#define LED_3 3
void task3(void) {
  digitalWrite(LED_3, digitalRead(LED_1)); //follow led_1
}

//task 4
#define LED_4 4
void task4(void) {
  digitalWrite(LED_4, !digitalRead(LED_2)); //follow the opposite of LED_2
}

//task 5
#define LED_5 5
void task5(void) {
  digitalWrite(LED_5, !digitalRead(LED_5)); //flip led5 - never executed
}


void setup(void)  {
  //initialize all tasks
  
  //initialize for task0
  lcd.begin(16,2); //display is 16.2
  lcd.print("Hello, world!");

  //initialize for task1
  pinMode(LED_1, OUTPUT);

  //initialize for task2
  pinMode(LED_2, OUTPUT);

  //initialize for task3
  pinMode(LED_3, OUTPUT);

  //initialize for task4
  pinMode(LED_4, OUTPUT);

  //initialize for task2
  pinMode(LED_5, OUTPUT);


}

void loop(void) {
  static unsigned char current_task = 0; //task indexer
  switch (current_task) {
  case 0: task0(); break;
  case 1: task1(); break;
  case 2: task2(); break;
  case 3: task3(); break;
  case 4: task4(); break;
  case 5: task5(); break;  //never executed
  default: break;
  }
  current_task = (current_task==MAX_TASKS - 1)?0:(current_task+1); //increment task indexer

}

task0 is to run an lcd and count up a value; task1..4 are simple blinking of leds. task5 is excluded from execution, by the definition of MAX_TASKS.

The scheduler doesn't assign a fixed time slot to individual tasks: the next task is run as soon as the previous one finishes. You can fix that by using a systick, like millies() at the end of the task and define a minimum time slot for individual tasks.

You can introduce functional pointers and use them to insert user tasks into a scheduler shell, like the one above, to make it easier to install user tasks into the scheduler - and potentially keep the scheduler entirely out of the user code base.

That's basically the gist of pretty much any RTOS.

Krupski:

dc42:
If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.

Is that code available for general use?

Yes, it's at GitHub - dc42/arduino: Reusable modules, drivers and patches for the Arduino platform, although it could do with more examples and more documentation. There are plenty of other multitaskers available for the Arduino as well.

dhenry:
Here is a short demo: running 5 tasks (the last one isn't actually executed, just to show as an example):

Yes, but why would you add that complexity rather than just executing each of the 'tasks' in turn within loop()? What advantage are you trying to obtain by making loop() call one function at each pass, rather than call all the functions? In this example, there is no advantage that I can see.

There are a lot of good responses here on this subject and as you might notice, in some cases its a matter of personal preference. Hobby coding is kind of cool that way, once you figure out how to make it go, and it works, its right for you; even if someone else thinks its a total hack. This is not necessarily the case for a professional coding project where maintenance, upgrades, or even personal injury issues are at stake. Anyhow, your solution could be polling, interrupts, RTOS etc. I guess what I'm saying is there is no one universal good answer to the asynchronous problem for a hobbyist. Creative programming can overcome most situations.

For me, I started out looking for an asynchronous platform to build my application upon even though I don't have a concrete project yet, but that's just me. I didn't really find anything completely suitable, so I'm building my own personal asynchronous device framework. Funny thing is, right now I have no idea if it will hold up to a real project. Fortunately for me, building my own framework is just part of the fun of using a micro-controller; even if I throw the code away, I'll have gained good experience and had fun in the process. I encourage you to explore and have fun.

Last night I completed some tests on my framework and blogged about it this morning if you are interested - the code will be available for further hacking by anyone. I do not plan on making a library out of this code, its just a personal project for me.

ddm

You might try here Arduino Tutorials | tronixstuff.com starts off simple and adds and adds.
Gets pretty complicated fast, but explained well.
TomJ