DuinOS: Small and Simple OS based on FreeRTOS

Continuation of the original thread located here; http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1256745982

Well I've been making some progress with the FreeRTOS port for Arduino.

  • Ethernet Library works

Polling three sensors using bitbang, serial interface, and web server on my Mega - Serial and Ethernet are non interrupt mode at the moment. Pretty impressive what can be packed into an 8-bit MCU.

A few modifications are needed with the DuinOS files but basically it strips all of the Arduino friendly functions/macros used to call the native FreeRTOS functions. I am using the native FreeRTOS API functions for simplicity. It simply doesn't make sense to re-invent the wheel considering documentation already exists.

Here is a code snippet:

static void vNetSvcTask(void *pvParameters) {
  /* The parameters are not used. */
  ( void ) pvParameters;
  
  /* Initialize the web server library
     with the IP address and port you want to use 
     (port 80 is default for HTTP): */
  WebServer webserver(PREFIX, 80);
  
  /*  */
  portENTER_CRITICAL();
  {
    /* start the Ethernet connection and the server */
    Ethernet.begin(mac, ip);
    
    /* web server related */
    
    /* setup our default command that will be run when the user accesses
     * the root page on the server */
    webserver.setDefaultCommand(&vHomeCmd);
  
    /* setup our default command that will be run when the user accesses
     * a page NOT on the server */
    webserver.setFailureCommand(&vFailCmd);
  
    /* run the same command if you try to load /index.html, a common
     * default page name */
    webserver.addCommand("message.xml",  &vXmlCmd);
    webserver.addCommand("index.html",   &vHomeCmd);
    webserver.addCommand("setup.html",   &vSetupCmd);
    webserver.addCommand("units.html",   &vUnitsCmd);
    webserver.addCommand("serial.html",  &vSerialCmd);
    webserver.addCommand("network.html", &vNetworkCmd);
    webserver.addCommand("status.html",  &vStatusCmd);
  
    /* start the webserver */
    webserver.begin();
  }
  portEXIT_CRITICAL();

  /* Cycle for ever, delaying then checking all the other tasks are still
     operating without error. */
  for( ;; ) {
    char buff[64];
    int len = 64;
    
    /* process incoming connections one at a time forever */
    webserver.processConnection(buff, &len);
  }
}

int main(void) {

  /* init mcu */
  init();
  
  /* init net-sensor to defaults */
  vInitConfig();
  
  // Pin change interrupt control register - enables interrupt vectors
  // Bit 2 = enable PC vector 2 (PCINT23..16)
  // Bit 1 = enable PC vector 1 (PCINT14..8)
  // Bit 0 = enable PC vector 0 (PCINT7..0)
  //PCICR |= (1 << PCIE0);
    
  // Pin change mask registers decide which pins are enabled as triggers
  //PCMSK0 |= (1 << PCINT5);

  // enable interrupts
  //interrupts();

  
  /* create tasks */
  xTaskCreate(vShtReadTask, (signed portCHAR *) "ShtRead", 304, NULL, NORMAL_PRIORITY, &xShtReadHandle);
  xTaskCreate(vMsReadTask,  (signed portCHAR *) "MsRead",  304, NULL, NORMAL_PRIORITY, &xMsReadHandle);
  xTaskCreate(vNetSvcTask,  (signed portCHAR *) "NetSvc",  700, NULL, NORMAL_PRIORITY, &xNetSvcHandle);
  xTaskCreate(vPrintTask,   (signed portCHAR *) "SPrint",  208, NULL, NORMAL_PRIORITY, NULL);
  
  /* start scheduler */
  vTaskStartScheduler();
  
  /* will only get here if there was insufficient memory  */
  for( ;; );
  
  return 1;
}

DuinOS.h

#ifndef DuinOS__h
#define DuinOS__h

#ifndef FREERTOS_ARDUINO
  #define FREERTOS_ARDUINO 1
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdlib.h>
#include <string.h>

//#ifdef GCC_MEGA_AVR
	/* EEPROM routines used only with the WinAVR compiler. */
	#include <avr/eeprom.h>
//#endif

/* Scheduler include files. */
#include "DuinOS/FreeRTOS.h"
#include "DuinOS/task.h"

#ifdef __cplusplus
} // extern "C"
#endif

//extern unsigned portBASE_TYPE mainLoopPriority;

//In small devices, we use only 3 priorities:
#define LOW_PRIORITY		(tskIDLE_PRIORITY)
#define NORMAL_PRIORITY		(tskIDLE_PRIORITY + 1)
#define HIGH_PRIORITY		(tskIDLE_PRIORITY + 2)

// leave this for arduino libraries
#define delay(ticks) vTaskDelay(ticks)

#endif

main.cpp (just saves you from having to include header files but this file can be removed altogether)

#include <WProgram.h>

// add DuinOS support

#include "DuinOS.h"

Next is to get serial and Ethernet working using Interrupts to take full advantage of the RTOS.

Well that's all for now.

Hi,

I have just downloaded the DuinOS and was trying to get it working on the Arduino mega 2560 but with no success. has anyone had any success with the arduino 2560?

Thanks

Hi!

Has anyone managed to work with Arduino+ DuinOS+ Ethernet Library, using the Arduino IDE?
What needs to be modified in the Ethernet Library?

Thank you in advance!

rama.stefan the DuinOS files would need to modified to accept the Mega 2560. There isn't much of a difference between them.

Bima yes, the Ethernet library works "as is" provided no other SPI devices are being used. The Ethernet library needs to be initialized within the task declaration using the portENTER_CRITICAL() call as shown in my posted example (e.g. vNetSvcTask). Also, this was tested and is being used on a mega and not the Uno (limited RAM).

Eric

Hi LAVco,

I have changed the contents of boards.txt on the arduino mega 2560 to use the arduino.DuinOS core and also I have modified FreeRTOSConfig.h to include the AVR Atmega 2560 with the same settings as the 1280. Then I tried the simple blinking example, it would compile but the LED is not blinking when I upload it on the board.

Hi,

I'm new in the arduino community ! I'm working on a project and am trying to use DuinOS. The version i'm using is the v0.3(svn) with the "board-test.txt" file in order to use the OS with the arduino UNO board.
Until now, all seems to run fine : I'm running 4 tasks using mutex and it works like a charm. Yet, i have experienced some issues using the "Serial" feature. This problem is the same that the one I read in the old topic (can't use print/println with numerics, but just with strings).
I'd like to know if this is still a known bug or if I missed something ?

Thanks

EDIT :
Sorry, problem solved by replacing the configMINIMAL_STACK_SIZE to 200
I changed the following lines in the DuinOS.h :

#define createTaskLoop(name, priority)\
{\
	xTaskCreate(name##_Task, (signed portCHAR *) #name, configMINIMAL_STACK_SIZE, NULL, priority, &name);\
}

to

#define createTaskLoop(name, priority)\
{\
	xTaskCreate(name##_Task, (signed portCHAR *) #name, 200, NULL, priority, &name);\
}

Ok, so !

The solution above was not very smart actually. The "freeRTOSconfig.h" file has to be update for (at least) the arduino UNO board.
The configMINIMAL_STACK_SIZE is set at 85 for the AVR_ATmega328P, which is the MCU of the arduino UNO board. But this is also the case for every other MCU...
If I change the following lines in the "freeRTOSconfig.h" file:

#elif defined(__AVR_ATmega328P__)
...
	#define configMINIMAL_STACK_SIZE	( ( unsigned portSHORT ) 85 )

to

#elif defined(__AVR_ATmega328P__)
...
	#define configMINIMAL_STACK_SIZE	( ( unsigned portSHORT ) 200 )

this will do basicly the same thing. This is a workaround for the "Serial bug", but the thing is, you will be limited by a maximum amount of 4 tasks in my case !
It is smarter to use the " createTaskLoopWithStackSize(name, priority, ssize) " function to set a bigger stack size only for the concerned task.

In addition to this, I tried to set the smaller stack size possible (for the configMINIMAL_STACK_SIZE but also the stack size of the task that uses the serial transmition).
I reached a value of 63 for the configMINIMAL_STACK_SIZE and a value of 133 for the other.
Reducing these values will crash the OS.

With this solution, I can launch 6 tasks with no bug and not any difficulty. I didn't try to launch more tasks but I'll try later (maybe ... or not).

You need to modify your stack size within the config file using the default port because there is an initial task created using macros to mimic the arduino structure; e.g. loop() {}. Even if you don't have any code in the "loop" function a task is created regardless.

You can make the changes suggested in the initial posting as it alleviates this problem and gives you more control. Furthermore your using native FreeRTOS calls instead of regenerated wrappers/macros serving the same purpose e.g. createTaskLoopWithStackSize versus native xTaskCreate(vShtReadTask, (signed portCHAR *) "ShtRead", 304, NULL, NORMAL_PRIORITY, &xShtReadHandle).

Eric

Ok ! Thanks for the tips (especially the xTaskCreate function).

Another tip; you only require a mutex/semaphore if the resource is being shared with more than one tasks.

For example; if you are using i2c for one device a mutex isn't required per say.

static void vI2CTask(void *pvParameters) {

   /* The parameters are not used. */
   ( void ) pvParameters;

   portENTER_CRITICAL();
   {
        // init i2c
   }
   portEXIT_CRITICAL();

  /* Cycle for ever, delaying then checking all the other tasks are still
     operating without error. */
   for( ;; ) {
      // read i2c device
      // process data
      // sleep for 10 seconds
   }
}

The "critical" section halts background tasks and allows the code to execute atomically alleviating potential synch issues. Additional information is available in the FreeRTOS on-line documentation. If you have done C# threaded programming its similar to a "lock" call.

Hi There,

I am still working on getting the DuinOS working on the mega 2560. I have followed the codes available on:

http://code.google.com/p/duinos/source/detail?r=21

but with no success :frowning:

for some reason when I tested a simple serial print code, it will print the test when I place it in void setup()

but it does not work when I place it in void loop()

Any one has any idea on how to run duinOS on the 2560..

Thanks,

(Sorry I'm still really new with the duino :sweat_smile:)

Thanks for the help beforehand..

Maybe if you show your code ?

I used a simple serial print and blink code

void setup() {

Serial.begin(9600);
Serial.println("TEST1");
pinMode (52, OUTPUT);

}

void loop(){
digitalWrite (52, HIGH);
delay(300);
digitalWrite (52, LOW);
delay(300);
Serial.println("TEST2");
}

it will print the serial TEST1 but no blinking happens and no printing of TEST2

And when you remove the serial tests, it will blink ? If it does, it looks like you are facing my problem of stack size.
Try to create a new task without using loop() (empty but exist) and set a stack size bigger than the default one. I used 200...

taskLoop(task1)
{
          digitalWrite (52, HIGH);
          delay(300);
          digitalWrite (52, LOW);
          delay(300);
          Serial.println("TEST2");
}
void setup()
{
        Serial.begin(9600);
        Serial.println("TEST1");
        pinMode (52, OUTPUT);
        createTaskLoopWithStackSize(task1, LOW_PRIORITY, 200);
}
void loop()
{
}

Hey, i just started toying around with DuinOS. And i want to implement it into one of my projects...

I have an atmega328 with the optiboot boot loader (arduino uno) on a breadboard. I have that hooked up to an lcd and some buttons, ultimatley i want to hook it up to an mp3 decoder. I have a whole gui and a couple games built into one of the task loops, the other one is going to take care of talking to an sd card and sending the data to an mp3 decoder... The problem is when i try to load up the gui it doesn't come up.

Nothing happens... i think im getting a stack overflow but i am not sure... is there anyway to detect this? Or am i doing it wrong? I am leaving the loop empty and declaring a task loop that i start in the setup, should i start it with a stack and how big? thanks in advanced for the help.