Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT

Stage 2:Getting Started With ArdOS for Arduino

4.88/5 (15 votes)
9 Nov 2014CPOL21 min read 65.8K   1.7K  
A Beginner's Tutorial for Arduino with ArdOS

Image 1

Figure 1: Architecture of the Tutorial Concept

Contents

1. Background
2. Setting up Arduino Development Environment
  2.1 Getting Started With Arduino Development Environment
  2.2 Basics of Arduino C Program
  2.3 Writing our First Arduino Program
      2.3.1 Simple Blinking Program
      2.3.2 Taking Input from Serial Port
      2.3.3 Working With Digital Switches
      2.3.4 Working with Sensors ( Touch Switch Example)
      2.3.5 User Defined Functions
3. ArdOS 
  3.1 Installation
  3.2 Working With ArdOS
4. Conclusion

1. Background

Arduino is a popular hardware platform which is meant for prototyping and hobby projects, but you can be rest assured of more complex and fun hardware design with Arduino. Arduino boards have a core microcontroller and several other chips and hardware necessory to get started with your hardware and hobby projects. 

A typical microcontroller basically runs a single program infinitely. That program may access several inputs, sensors, output and display devices. For example a microcontroller can be programmed to read light sensor LDR continuesly and display the light intensity in a LCD. When light intensity falls below certain level it might trigger a relay unity which switches on lights. But all of these logic runs in a single loop.

However there are handful of real time scenerios where the microcontroller is expected to do many tasks parallely. Parallel execution of tasks are more or less concurrent which means there is a task switching mechanism that switches from one subrouting to another. Conventionally microcontroller program handles this concurrency through cleaver usage of timer and jumping from one subrouting to another. But implementing an effective concurrency strategy and then share data between the subroutine is a tedious task.

Consider a simple IR remote control with Arduino. Depending upon the code generated, the device is expected to perform certain tasks. Now when IR signal is generated is not known. This is asynchrnous input. Therefore the device can't keep listening for the IR signal. The signal needs to be connected to interrupt pin such that as and when the signal arrives, an ISR is called which can then handle the IR signal. 

Now consider a simple lighting setup where sets of lights are switched on and off by the microcontroller in a specific order. Once an IR code is made ( say volume+ bytton is pressed in a remote control) then the order of LEDs must be changed. So there must be separate subroutines for Glowing LEDs, and then there must be ISR for handling IR code. IR code must immidiately change the order. Then the subroutines for different LEDs must be changed. So even a simple IR can be thought to have different event(consider different IR codes as being different event).

At the same time user might want to add extra logic to it. Say along with the LEDs, we want certain buzzer to function.  When new hardware and functions are to be added with existing logic, again entire code must be rewritten, compiled, tested and optimized.

In order to handle these requirements(Concurrency, Event Drivenness, Adding Hardware, Managing Tasks) of real time microcontroller driven applications, real time operating systems (RTOS)were developed.

A real time operating system is an independent microcontroller "program" which can handle low level taks like concurrency, task management, event triggers and so on. When an application is developed with RTOS, the operating system itself is distributed with the application as at the end of the day, microcontroller still runs a single program!

So we can think of real time operating system as a library which can be utilized by microcontroller program and one which handles low level "os specific taks" like managing devices, communication, tasks and operations.

ArdOS  is one such real time operating system for Arduino platform. It is meant to provide APIs that helps achieving great levl of parallelism for the Arduino programs along with outstanding support for handling events and triggers.

So in this tutorial we shall see the basic working with Arduino and then we shall extend our board to support ArdOS. We will compare certain applications being developed without ArdOS and with ArdOS and will compare their performances( Refer figure 1). 

I would be using Arduino Duelmilanova board for this totorial which we get for about $11 here in India. You can also use Arduino Decimilia, Uno R3 boards as par your suitability and availibility of the board.

 

2. Setting up Arduino Development Environment

2.1 Getting Started With Arduino Development Environment

First download latest Arduino IDE from Arduini's Official Software Download Site. Windows user will be able to download an Installer. The installer has a FTDI usb driver along with the IDE itself. Prefer to install the USB driver. This is essential. Without USB driver your IDE can not communicate with the Arduino Device.

Following is the Pin Description of the device that I am using.

Image 2

Figure 2.1: Arduino Diecimilia/Duemilanova with Pin Levels

Once the software is installed, plug Arduino's USB cable to your laptop. You will see a notification in tray "installing driver". Once driver installation is successfull Open Device manager ( start->right click on Computer icon->Properties->Device manager).

Image 3

Figure 2.2:  Device Manager After Arduino is Plugged in.

In the Ports(Com and LPT) section you see USB serial port. In case of Arduino Uno R3, you will see Arduino Uno marker along the port. When you plug out the cable, you won't find the port anymore. So this is the port at which your Arduino device is connected.

If you have connected your device for the first time, you will also notice that the LED near digital pin 13 is blinking. Most of the Arduino boards are preloaded with a blink program which blinks the LED connected to pin 13.

Before starting with our coding, we need just couple of more steps to setup IDE with the board.

Open the IDE. Tools->Boards select the board you are connecting as shown in figure below.

Image 4

Figure 2.3: Arduino Board Selection

Once the board is selected, you need to set the correct serial port as you have figured out from figure 2.2

Image 5

Figure 2.4: Selecting USB Serial Port in IDE

Before we go into programming the boards we will learn some simple basics of Arduino C programming.

2.2 Basics of Arduino C Program

Following is the basic structure of the Arduino program.

C++
void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

setup() is executed once ( when you reset the device, or restrat the device, or plug in the USB cable ). loop()

runs continuesly. Main logic must be implemented inside loop.

Arduino Duemilanova/Diecimilia/Uno R3 ( from here onwards will refer simply as Arduino) board has 14 Digital IP pins. Each pin can be program separately. A Pin can be in with OUTPUT mode or input mode which needs to be specified in setup using

pinMode(PIN_NO,OUTPUT) or pinMode(PIN_NO,INPUT); 

Input mode is used when you connect a switch to a pin and want the pin to read the status. These digital pins in input mode can only recognize logic level input like 0 and 1. PIN_NO ranges from 0-13 depending upon the pin you are programming.

you can write logical output in a pin in OUTPUT mode or can read the the value at the pin ( 1 or 0) using digitalWrite(PIN_NO,HIGH)/digitalWrite(PIN_NO,LOW) and int n=digitalRead(PIN_NO) respectively.

Few pins in Digital ports are marked as PWM( like 10 and 11 in figure). They can be used to generate gate pulse of different duty cycle. PWm pin can be programmed to produce perfect square gate pulse of specific duty cycles using analogWrite(PIN_NO,byteValue) Where byteValue can be betweet 0 to 255 where 255 represts highest or 100% duty cycle.  PWM pins can be used to control DC motors precise speed control or controlling intensity of electrical bulbs through MOSFETs/IGBTs and so on. 

Arduino has 6 analog read pins (0 to 5) opposite to the digital ports. These are input pins which are connected to 10 bit ADC. Sensors can be directly connected to these pins. Arduino can read the voltage at the pin with int value=analogRead(ANALOG_PIN_NO) 

The software comes with a Serial library, through which other programs can communicate with Arduino using Serial communication. Arduino IDE also comes with a Serial Monitor through which one can test the serial communication.

For initializing serial communication, in the setup function one needs to initialize the serial communication object with Serial.begin(BAUD_RATE) Baud Rate varies in the multiples of 96200. Serial.print("msg"),Serial.println("msg"), Serial.write(someVariable) are common functions by means of which the board can write data on the serial port which can be read by any external program capable of communicating with the device over serial port.

int v=Serial.read() is used to read data from Serial port written by other programs on serial port.

delay(delayInMilli); is used to make the next instruction wait for delayinMilli period of time which is specified in milliseconds.

Most of the basic hardware Input and output can be performed using above commands.

2.3 Writing our First Arduino Program

Having got a little bit of idea about the Arduino programming, lets now go the the most interesting part, the coding.

Now open your IDE from Desktop shortcut icon or from Program files. You will see the IDE with a default sketch. The C program that we write for Arduino is known as a sketch.

So let's make our hands dirty with Arduino coding without external hardware. Let us change the blinking time to 1s on period and 1sec off period.

2.3.1 Simple Blinking Program

The program is pretty straight forward and need no explaination.

C++
void setup() 
{
  pinMode(13,OUTPUT);
  // put your setup code here, to run once:

}

void loop() 
{
  // put your main code here, to run repeatedly:
  digitalWrite(13,HIGH);
  delay(1000);
  digitalWrite(13,LOW);
  delay(1000);

}

Having written and saved the program, click on Upload button as shown in following figure to upload the sketch in the board. It is that simple.

Image 6

Figure 2.5: Uploading the Sketch in the Board.

That's it. now you will see the LED associated with pin 13 is blinking with 1sec delay.

So you get to understand how Microcontroller works. Right? How about user input? Say when user gives 1 through serial port, the LED should glow. When user gives input as 0 , LED should be turned off. 

So for uploading sketch you need not save it. However, I would suggest you to save the sketch with some meaningful name. When you save the sketch, a folder will be created by the name of the sketch and the ino sketch will be put inside the folder.

2.3.2 Taking Input from Serial Port

First Let us establish Serial communication. We will establish a Serial communication with device using Serial.begin command. Serial.available() returns true if data is being written by the other device in the port. So we will first check for Serial data and if present we will print them.

C++
void setup() 
{
  pinMode(13,OUTPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);

}

void loop() 
{
  if(Serial.available())
  {
    int n=Serial.read();
    Serial.println(n);
  }

}

After you upload the sketch, to check the result you need to open the Serial Monitor window using Ctrl+Shift+M or from Tools menu option.

Image 7

Figure 2.6:Using Serial Monitor

Once you open the window, type 1 in the top input panel and enter, you will see 49, and for 0 you will see 48. Which means that the Serial monitor takes ascii input from keyboard. We will first convert it into normal number by subtracting 48 and then implement on and off logic.

Here goes the program.

C++
void setup() 
{
  pinMode(13,OUTPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

void loop() 
{
 
  if(Serial.available())
  {
    int n=Serial.read();
//    Serial.println(n);
   n=n-48;
   if(n==0)
   {
     Serial.println("LED is OFF");
     digitalWrite(13,LOW);
   }
   else if(n==1)
   {
     Serial.println("LED is ON");
     digitalWrite(13,HIGH);
   }
   else
   {
     Serial.println("Only 0 and 1 is allowed as input");
   }
  }

}

When you upload the code and run through Serial monitor you can see that the LED is turned on when you input 1, remains on till you give input as 0 and remains zero. 

Image 8

Figure 2.7: Output of Turning On and Off off LED from Serial Input

2.3.3 Working With Digital Switches

The idea of the article is to teach you Arduino tricks without much of hardware so that you can get accustomed with the programming environment easily.  

A Digital switch symbolically represented as following figure. Two points A and B are connected when the switch is closed and are not connected when the switch is open. There are different ways how the opening and closing is controlled which includes relayes, push buttons, lever switches etc.

Image 9

2.8: Simple Digital Switch

You can get several switches compatible to Arduino. But I will show you how to implement a switch logic just with a simple wire.

Image 10

Figure 2.9: Simple Switch with a Single Connecting Wire.

We take a connecting wire to connect +3v pin in the power port to another pin ( say 8) as shown in above figure, then whenever the connection is made, 8 will have same voltage that of other pin ( i.e. logical 1), when you take off the wire and connect it to ground, 8 will be open switch and will have logic 0.  When switch is closed, the LED should glow, when switch is opened, it should stop Glowing. This is not an ideal switch, but a little manipulation for getting the desired logic level for validating a program.

C++
void setup() 
{
  pinMode(13,OUTPUT);
  
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

void loop() 
{ int n=0;
  n=digitalRead(8);

    if(n==0)
   {

     digitalWrite(13,LOW);
   }
   else if(n==1)
   {

     digitalWrite(13,HIGH);
   }

  delay(200);// to avoid over polling by continuesly reading port data 
}

delay At the end is important. Without it the microcontroller has to continuesly poll the pin which results in degraded performance. You can change the delay value depending upon your application needs.

2.3.4 Working with Sensors ( Touch Switch Example)

Quote:

A Sensor converts the physical parameter (for example:  temperature, blood pressure, humidity, speed, etc.) into a signal which can be measured electrically

There are different types of sensor for measuring different physical activities which includes light intensity, temparature, humidity and so on. This wiki page gives you an elaborate list of sensors.

One of the important aspects of microcontroller devices is to read sensor data and take decisions accordingly.

So we should also learn how to handle sensors. But as this tutorial is all about learning Arduino without much of external hardware, we will again use certain simple concept to understand how sensor works.

The analog pins in Arduino are connected to 10 bit ADC. That means the analog voltage at the pins will be converted into digital value of 10 bits. Which means for a 5v analog input, a sensor pin will read 1024.

Now human body has potential difference from ground. Arduino is sensitive enough to detect this potential difference. So if we touch the analog pin with our body, it will have our body's potential difference. As body's potential difference is attributed by ECG signal which has periodicity, such voltage will vary between high to low.

Also voltage is measured between a point and the ground. Body's voltage that is acquired by the pins will have earth as ground which is not common to microcontroller's ground. Thus there will be huge potential difference. Our program must take this into consideration.

So let's put the wire on the analog pin 5 and write our program to obtain analog voltage at pin 5.

Image 11

Figure 2.10: Wire Connected to Analog Pin 5 

void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

void loop() 
{int n=0;
  n=analogRead(5);

  Serial.println(n);
  delay(500);

}

And your Serial Monitor output should look something like below.

Image 12

Figure 2.11: Serial Monitor Output of Reading Analog Pin

You might have expected 0 in the output because you are not providing any voltage at the pin ( in other words the pin is open circuit). But because pull up register at the analog pin, garbage charge is collected which is displayed as analog output.

Now when you hold the wire  like 2.12

 

Image 13

Figure 2.12: Serial Monitor Output When Body Voltage is Provided to Arduino Analog Pin 5

If we plot the values in excell, you get following graph.

Image 14

Figure 2.13: Body Voltage Graph

In order to minimize the effect of variations, let us now take avaerage of 10 readings as our Sensor value.

C++
void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

int s=0;
int i=1;
int a=0;
void loop() 
{int n=0;
  n=analogRead(5);
  s=s+n;
  a=s/i;
  i++;
  if(i>10)
  {
    i=1;
    s=0;
  }
  Serial.println(a);
  delay(500);

}

Remember that our body voltage will differ and so will be the open port voltage of the devices, so you may not get exact result as given here, but you will get much similar result.

Image 15

Figure 2.14: Modified Graph of Body Voltage.

This is much more stable. We can see that either values are greater than 700 or less than 200. Now obtain a graph with averaging sensor values when you don't touch the pin as in 2.16.

Image 16

2.15: Open Circuit Analog Pin 5 Average Voltage Graph

This is fantastic. Because we now have "no touch" voltage between 250 and 500.

So can we now trigger an event when voltage exceeds 600 or falls below 200?  Yes we can and let us trigger the LED light on when we touch the pin and switch it off when we release the pin.

C++
// Touch Switch Program. Touch Pin 5 to Switch on LED, Release to Switch off
void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

int s=0;
int i=1;
int a=0;
void loop() 
{int n=0;
  n=analogRead(5);
  s=s+n;
  a=s/i;
  i++;
  if(i>10)
  {
    i=1;
    s=0;
  }
  Serial.println(a);
  if(a>650 || a<200)
  {
    digitalWrite(13,HIGH);
  }
  else
  {
    digitalWrite(13,LOW);
  }
  delay(500);

}

 

2.3.5 User Defined Functions

Just like any efficient programming environment, Arduino  also provide functions to keep the code clean and to be able to call it instead of repeating the code inline.  Arduino functions are just like normal C program functions and behaves exactly as subroutines in assembly level programs.

We have seen Serial, Switch and Sensor driven on and off of the LED. Let us put togather everything. Let us develop a program where we can switch on the LED by either three means and can switch off using only Serial command. 

C++
void SerialLogic()
{
  int n=0;
   if(Serial.available())
  {
    n=Serial.read();
   Serial.println(n);
   n=n-48;
   if(n!=10) // Code for Enter
   {
   if(n==0)
   {
     Serial.println("LED is OFF");
     digitalWrite(13,LOW);
   }
   else if(n==1)
   {
     Serial.println("LED is ON");
     digitalWrite(13,HIGH);
   }
   else
   {
     Serial.print("Only 0 and 1 is allowed as input ");
     Serial.println(n);
   }
   }
 
}
}
void SwitchLogic()
{
  int n=0;
  n=digitalRead(8);

    if(n==0)
   {

    // digitalWrite(13,LOW);
    // We are switching off only through Serial Command
   }
   else if(n==1)
   {

     digitalWrite(13,HIGH);
   }
}
int s=0;
int i=1;
int a=0;
void SensorLogic()
{
  int n=0;
  n=analogRead(5);
  s=s+n;
  a=s/i;
  i++;
  if(i>10)
  {
    // Print the value one in every 5 Second
    Serial.println(a);
    i=1;
    s=0;
  }
 // Serial.println(a);

  if(a>650 || a<200)
  {
    digitalWrite(13,HIGH);
  }
  else
  {
    //digitalWrite(13,LOW);
    // No Turning off here :)
  }
}
void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

void loop() 
{ 
 SerialLogic();
 SwitchLogic();
 SensorLogic();
 delay(500);
}

So you can see that we have actually made three functions: SerialLogic(),SensorLogic(),SwitchLogic() Which handles the On off through serial port, switching on through touch and switching on by connecting +3v to pin 12 respectively.

From loop() we are just calling these functions. You can also see that we are not printing every serial value here, rather putting only once in avery 5 sec( .5sec par reading, printing after 10 readings).

Technically we are expecting all three functions to execute "parallely" (simultaneously)isn't it? But due to nature of the C program, there will be executed serially, one after the other in the order of call. Correct? So we have no parallelism support here.

At this moment you can think of each function as a Task, where each task executes set of instructions.

Now let us add little spice. Let us also blink the LED for 5 times, once in every every 10 seconds, along with the existing logic.

So here is our new program with BlinkLogic()

 

C++
void SerialLogic()
{
  int n=0;
   if(Serial.available())
  {
    n=Serial.read();
   Serial.println(n);
   n=n-48;
   if(n!=10) // Code for Enter
   {
   if(n==0)
   {
     Serial.println("LED is OFF");
     digitalWrite(13,LOW);
   }
   else if(n==1)
   {
     Serial.println("LED is ON");
     digitalWrite(13,HIGH);
   }
   else
   {
     Serial.print("Only 0 and 1 is allowed as input ");
     Serial.println(n);
   }
   }
 
}
}
void SwitchLogic()
{
  int n=0;
  n=digitalRead(8);

    if(n==0)
   {

    // digitalWrite(13,LOW);
    // We are switching off only through Serial Command
   }
   else if(n==1)
   {

     digitalWrite(13,HIGH);
   }
}
int s=0;
int i=1;
int a=0;
void SensorLogic()
{
  int n=0;
  n=analogRead(5);
  s=s+n;
  a=s/i;
  i++;
  if(i>10)
  {
    // Print the value one in every 5 Second
    Serial.println(a);
    i=1;
    s=0;
  }
 // Serial.println(a);

  if(a>650 || a<200)
  {
    digitalWrite(13,HIGH);
  }
  else
  {
    //digitalWrite(13,LOW);
    // No Turning off here :)
  }
}
void BlinkLogic()
{
  Serial.println("Blinking");
  for(int j=0;j<5;j++)
  {
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
   delay(500);
  }
  Serial.println("Blinking Ends");
}
void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(19200);
  Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
}

int t=0;
void loop() 
{ 
 SerialLogic();
 SwitchLogic();
 SensorLogic();
 t++;
 if(t>=10)
 {
 BlinkLogic();
 t=0;
 }
 delay(500);
}

Have a look at the output of this program:

Image 17

Figure 2.16: Result of Blink, Sensor, Switch, Serial logic put in same code

The output really exposes the problem of conventional Arduino programs. We can see that not only tasks are not executed cocurrently also that they are blocking in nature. 

One more observation you can make while executing the program is that, though you want a 5 sec blink after every 10 seconds, they do not appear after exact 10 seconds as the logic has to bear the delays of other executing parts. Which means perfect scheduling is not possible with Arduino programs. Arduino has a timer library which makes scheduling better but multitasking is still a difficult task.

We will introduce ArdOS  in next section and show how to overcome these problems using the OS. 

3. ArdOS 

3.1 Installation

ArdOS is distributed as an Arduino Library.  First download ArdOS from here. Rename the Zip file as ArdOS.zip.  This is because Arduino does not support special characters like (-,SPACE) while installing a package/library. Now click on Sketch menu->Add Library and select the downloaded and renamed zipped package.

Image 18

Figure 3.1: Installing ArdOS 

Once done, you will see ArdOS in the list of libraries when you hover your mouse over import library option under sketch menu.

Image 19

Figure 3.2 Installation Success of ArdOS

3.2 Working With ArdOS

We will first write a simple example of ArdOS and then we will model our complex program developed in section 2 with ArdOS.

In a blank sketch import library->ArdOS. It will include following header files.

C++
#include <kernel.h>
#include <mutex.h>
#include <queue.h>
#include <sema.h>

You can easily make out that Kernel will be compiled along with your code. Mutex provides methods for mutual exclussion important for task parallelism, queue manages task queue, sema manages the semaphores. Semaphores are the locks used while a resource is shared between multiple tasks. kernel provides methods for scheduling, task management, resource sharing, interrupt handling and so on.

ArdOS programs must incorporate programming logic inside tasks with signature

void task(void * p)

From setup() method tasks must be initialized and put inside a queue. loop() must not contain any code as entire looping will be looked after by Kernel. As kernel handles tasks, tasks must implement infinite loops to allow the tasks to run infinitely.

C++
#include <kernel.h>
#include <queue.h>
#include <sema.h>

#define NUM_TASKS    2
void task1(void *p)
{
  char buffer[16];
  unsigned char sreg;
  int n=0;
  while(1)
  {

    sprintf(buffer, "Time: %lu ", OSticks());
    Serial.println(buffer);
    OSSleep(500);
  }
}

void task2(void *p)
{
  unsigned int pause=(unsigned int) p;
  char buffer[16];
  while(1)
  {
    digitalWrite(13, HIGH);
    sprintf(buffer, "==>Time: %lu ", OSticks());
    Serial.println(buffer);
    Serial.println("LED HIGH");
    
    OSSleep(pause);
    sprintf(buffer, "==>Time: %lu ", OSticks());
    Serial.println(buffer);
    digitalWrite(13, LOW);
    Serial.println("LED LOW");
    OSSleep(pause);
  }
}

void setup()
{
  OSInit(NUM_TASKS);
  
  Serial.begin(19200);
  pinMode(13, OUTPUT);
  
  OSCreateTask(0, task1, NULL);
  OSCreateTask(1, task2, (void *) 250);
  
  OSRun();
}

void loop()
{
  // Empty
}

Here we have used two tasks: task1 and task2. task1 just prints time in terms of ticks. task2 blinks LED at a specific value passed as parameter and also prints On and Off state with respective time. Both tasks are meant to run continuesly through while(1) Loop structure.  Instead of using delay we use OSSleep() function which handles the delay accurately without blocking other tasks. First a task queue is initialized with OSInit function. Tasks are put in task queue using OSCreateTask function. The first parameter is task id, second parameter is name of the task function and the third one is parameter passed to the task.

Digital pins and serial communication is initialized in ususal way before initialization of the tasks. Once done, program is executed through ArdOS multitasking environment using OSRun().

The output of the above program is as shown below.

Image 20

Figure 3.3: Result of Our First Multi Tasking ArdOS Program

However while working with Kernel and RTOS, you must be very particular about your design, amount of time a resource may take. If a resource needs to be shared by two tasks the care should be adopted.

To check how critical is ArdOS programming just add following two lines in your task1 and try to execute:

n=analogRead(5);
Serial.println(n);

Obviously you need to declare n before while. When you run you might not see the desired print result of analog pin value.

However if we increase the baud rate to 115200 and implement analog read in one more task, it would give us perfect result.

Check out the following code:

#include <kernel.h>
#include <queue.h>
#include <sema.h>

#define NUM_TASKS  4
void task1(void *p)
{
  char buffer[16];
  unsigned char sreg;
  int n=0;
  while(1)
  {
   
    sprintf(buffer, "Time: %lu  ", OSticks());
    Serial.println(buffer);
    OSSleep(1000);
  }
}

void task2(void *p)
{
  unsigned int pause=(unsigned int) p;
  char buffer[16];
  while(1)
  {
    digitalWrite(13, HIGH);
//    sprintf(buffer, "==>Time: %lu ", OSticks());
  // Serial.println(buffer);
   //Serial.println("LED HIGH");
    
    OSSleep(pause);
  // sprintf(buffer, "==>Time: %lu ", OSticks());
  // Serial.println(buffer);
    digitalWrite(13, LOW);
    //Serial.println("LED LOW");
    OSSleep(pause);
  }
}
void task3(void * p)
{
  char buff1[16];
  int n1=0;
  while(1)
  {
    n1=analogRead(5);
    n1=map(n1,0,1023,0,255);
   sprintf(buff1, "APV: %d ", n1);
   Serial.println(buff1);
    OSSleep(1000);
  }
}
void setup()
{
  OSInit(NUM_TASKS);
  
  Serial.begin(115200);
  pinMode(13, OUTPUT);

  OSCreateTask(0, task3, NULL);    
 OSCreateTask(1, task1, NULL);
  OSCreateTask(2, task2, (void *) 1000);
  OSCreateTask(3, task1, NULL);

  OSRun();
}

void loop()
{
  // Empty
}

The result of this program will give you important insight of why the earlier function did not work. Many tasks may parallely try to write on the Serial port.

Image 21

Figure 3.4: Conflict of Resources between Tasks

This provides an important understanding of how the code must be designed. It should be carefully written in order to minimize holding any shared resource for longer time by any single task.

As the software is in beta stage there are still some bugs. Do not declare local variables with same name in diffrent functions. Means don't declare a variable by name n in task1 and task2. Tasks do not recognize the state of global variables. So if you declare a global variable and expect that to be used from your tasks then that is not what happens here. In fact tasks are unable to recognize global variables. There are few more issues, but you can carefully code to overcome those.

Here is our implementation of the code we developed in section 3 rewritten with ArdOS. Needless to say that the code performs far better in terms of parallelism, event trigger and scheduling.

C++
#include <kernel.h>
#include <mutex.h>
#include <queue.h>
#include <sema.h>

void SerialLogic(void *p)
{
  int n2=0;
  while(1)
  {
   if(Serial.available())
  {
    n2=Serial.read();
   Serial.println(n2);
   n2=n2-48;
   if(n2!=10) // Code for Enter
   {
   if(n2==0)
   {
     Serial.println("SERIAL OFF");
     digitalWrite(13,LOW);
   }
   else if(n2==1)
   {
     Serial.println("SERIAL ON");
     digitalWrite(13,HIGH);
   }
  
   }
 
  }
  OSSleep(500);
  }
}
void SwitchLogic(void *p)
{
  int n3=0;
  while(1)
  {
  n3=digitalRead(8);
  

    if(n3==0)
   {

    // digitalWrite(13,LOW);
    // We are switching off only through Serial Command
   }
   else if(n3==1)
   {
    Serial.println("SWITCH ON");
     digitalWrite(13,HIGH);
   }
   OSSleep(500);
  }
}
int s=0;
int i;
int a=0;
void SensorLogic(void *p)
{
  int n4=0;
  while(1)
  {
  n4=analogRead(5);
  
  if(n4>750 || n4<50)
  {
    Serial.println("SENSOR ON");
    digitalWrite(13,HIGH);
  }
  else
  {
    //digitalWrite(13,LOW);
    // No Turning off here :)
  }
  OSSleep(1000);
  }
}
void BlinkLogic(void * p)
{
 
  while(1)
  {
  Serial.println("Blinking");
  
  for(int j=0;j<2;j++)
  {
    digitalWrite(13,HIGH);
    OSSleep(500);
    digitalWrite(13,LOW);
   OSSleep(500);
  }
  Serial.println("Blinking Ends");
  OSSleep(20000);
  }
}
void setup() 
{
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,HIGH);
  pinMode(8,INPUT);
  // put your setup code here, to run once:
  Serial.begin(115200);
 // Serial.println("Enter 1 for Turning LED on and 0 for Turning it OFF");
  
  OSInit(4);
  
  
  OSCreateTask(0, SerialLogic, NULL);    
 OSCreateTask(1, SwitchLogic, NULL);
  OSCreateTask(2, SensorLogic, NULL);
  OSCreateTask(3, BlinkLogic, NULL);

  OSRun();
}

int t=0;
void loop() 
{ 
 
}

I had to rewrite SensorLogic because of the OS's inability to handle global variable.  Following is the output

Image 22

Figure 3.5:  Sensor, Switch, Blink and Serial logic in ArdOS

There you see that ArdOS makes it easy to efficiently and accurately scheduling the events and  multitasking different tasks. You can play around with other logic and see how ArdOS works.

4. Conclusion

In this article we have learnt how to get started with your Arduino boards and coding. We learnt the important aspects of the board namely Sensing, Controlling and Serial communication. We then learnt how to use ArdOS and migrated a solution from conventional Arduino Sketch to ArdOS. ArdOS not only provides good multitasking features but at the same time it also provides good resource sharing using Mutex and Semaphores. As the aim of this tutorial was to guide you an easy migration from sketch programs to ArdOS programs, I have considered multitasking features of OS. You can check out the semaphore and mutex examples and can play around with the examples. ArdOS is still in beta stage and I have listed few drawbacks. However those drawbacks do not make the OS unstable. Little care in coding can overcome the shortcomings of the OS. Also the aim of the tutorial was to cover most of the aspects of Arduino programming without using any external hardware. Once you are confident of Arduino environment, you can get some inexpensive hardware like LM35, LDR, Relays, BC548, 5V Motors, LEDs and can try out different logics.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)