Arduino Real Time EggBox

This entry is part 3 of 3 in the series Arduino the Egg Box Lamp

In questo secondo capitolo della serie egg box per arduino (una lampada a led costruita con 2 scatole di plastica delle uova), riscriveremo il codice in modo da utilizzare un sistema operativo real time chiamato NilRTOS e disponibile su Github; in particolare mostrerò un semplice sketch che è in grado di produrre un piccolo accordo ed accedere tre luci rispetto ad un ritmo, mentre una quarta, di colore rosso, si accenderà rispetto ad una cadenza diversa.

NilRTOS è stato sviluppato da Giovanni Di Sirio, ed è una versione ridotta all’osso di un sistema operativo real time:

Un sistema operativo real-time o in tempo reale (abbreviato in RTOS) è un sistema operativo specializzato per il supporto di applicazioni software real-time. Questi sistemi vengono utilizzati tipicamente in ambito industriale […] o comunque dove sia necessario ottenere una risposta dal sistema entro un tempo prefissato.

Un sistema operativo real-time non deve essere necessariamente veloce […] l’importante è che risponda entro un tempo massimo pre-determinato. […]

In pratica un sistema real-time deve garantire che una elaborazione (o task) termini entro un dato vincolo temporale o scadenza (detta in gergo deadline). Per garantire questo è richiesto che la schedulazione delle operazioni sia fattibile. Il concetto di fattibilità di schedulazione è alla base della teoria dei sistemi real-time ed è quello che ci permette di dire se un insieme di task sia eseguibile o meno in funzione dei vincoli temporali dati.

Da Wikipedia

Per tali ragioni i sistemi  operativi real time si adattano bene in un contesto in cui dovete garantire l’esecuzione di alcune operazioni a fronte delle letture fatte da alcuni sensori.

Doverosa premessa Data la scarsa memoria di Arduino Uno (2KB), il nostro consiglio è di servirsi di un ArduinoMega (che ha 8KB di RAM) se desiderate sviluppare applicazioni minimamente complesse in NilRTOS.
Inoltre NilRTOS contiene solo un sottoinsieme delle feature normalmente disponibili in un sistema realtime: in particolare è preemptive ed  ha  semafori e  code FIFO con cui è possibile implementare le zone critiche.

 

Per farlo definiremo tre processi NilRTOS, a differenti priorità.

NB: I pin PWM 3 ed 11  non possono essere usati perché vanno in conflitto con la funzione “tone()”:

Use of the tone() function will interfere with PWM output on pins 3 and 11 (on boards other than the Mega).

come descritto dalla documentazione.

Inoltre i pin 2,3 sono usati per gestire gli “external” interrupts, per cui, in generale, suggeriamo di evitarli.

Rispetto al progetto precedente, avremo un pin in più per pilotare un led rosso con un thread aggiuntivo…

const int speakerOut=A5;
/*** FADER**/
int yellowLed = 9; // the pin that the LED is attached to
int greenLed=6; // Reverse pin
int blueLed=5; //ledgroup 3
int redLed=10; //extra pin
const int DelayTime=56; // 44 is good

Il codice è disponibile su github, ma lo replichiamo qui:


/** EGG BOX WITH 4 Independent PWM light and one powerful speaker.
  * Red Led rocks a lot
  */
#include <pitches_it.h>
#include <NilRTOS.h>

#define DEBUG yeppa

const int speakerOut=A5;

/*** FADER**/
int yellowLed = 9;           // the pin that the LED is attached to
int greenLed=6;      // Reverse pin
int blueLed=5;  //ledgroup 3
int redLed=10;  //PWM 3 get conflict with sound
const int DelayTime=44; // 44 is good

const float  durationBase=750;
const float D1_3= 1/3.0;
const float HALF= 0.5;
const float Q   = 0.25;

// Two pair: note and duration
const float music[]  ={ NOTE_FA4,1,
                      NOTE_LAS4 /*BEMOLLE*/ ,1, 

                      NOTE_FA4,  D1_3,
                      NOTE_FA4,  D1_3,
                      NOTE_LAS4, D1_3, /*SI BEM*/

                      NOTE_FA4,  1/2.0,
                      NOTE_RES4, 1/2.0, /* MI BEM*/

                      // Second
                      NOTE_FA4,1,
                      NOTE_DO5,1,

                      NOTE_FA4,D1_3,
                      NOTE_FA4,D1_3,
                      NOTE_DOS5,D1_3,

                      NOTE_DOS5,HALF, // INEXACT....
                      NOTE_LAS4,HALF, // LAB

                      //THIRD
                      NOTE_FA4,Q,
                      NOTE_DO5,Q,
                      NOTE_FA5,Q,
                      NOTE_FA4,Q,

                      NOTE_RES4,D1_3,     //MI bem
                      NOTE_MI4,D1_3,
                      NOTE_DO4,D1_3,

                      NOTE_SOL4,HALF,
                      NOTE_FA4,HALF,
                      NOTE_FA4,2,
                      //+ PAUSA
                      -1,-1,-1,-1

                  };

const float noteSwifter=1 ; // Default 1
// Boot Music
void taDa(){

  #ifdef DEBUG
    Serial.print("MUSIC STARTS. Total Data:");
  #endif

  for (int i=0; music[i] != -1;) {
    noTone(speakerOut);
    int note=(int)( ((float)music[i])  *noteSwifter);
    int duration=((int) (music[i+1]*durationBase))+1;
    #ifdef DEBUG
    Serial.print(i); Serial.print(F(" - Note:"));
    Serial.print(note); Serial.print(F(" Dur:"));
    Serial.println(duration);
    #endif
    tone(speakerOut,note);
    nilThdSleepMilliseconds(duration);
    noTone(speakerOut);
    i+=2;

  }
  noTone(speakerOut);
  #ifdef DEBUG
  Serial.println("MUSIC ENDS");
  #endif

}

void setup(){
 pinMode(13, OUTPUT);
 pinMode(speakerOut, OUTPUT);
 pinMode(yellowLed, OUTPUT);
 pinMode(greenLed,OUTPUT);
 pinMode(redLed,OUTPUT);

 Serial.begin(9600);
 Serial.println(F("The 4EggBox v2.1 RTOS"));
 Serial.println();
 nilSysBegin();
}

unsigned long lastToggyellowLedBlinker;

void fadeOut(int pin, int fadeAmount){
  int brightness = 255;    // how bright the LED is
  while(brightness >0 ){
    brightness = brightness + fadeAmount;
    if(brightness<0) { brightness=0;};
    analogWrite(pin, brightness);
    nilThdSleepMilliseconds(DelayTime);
  }
}

void fadeOut(int pin){
  fadeOut(pin, -5);
}

// Very slow, very nice
void fadeOut3(int pin1,int pin2, int pin3){
  int brightness = 255;    // how bright the LED is
  int fadeAmount = -2;    // how many points to fade the LED by

  while(brightness >2 ){
    brightness = brightness + fadeAmount;
    analogWrite(pin1, brightness);
    analogWrite(pin2, brightness);
    analogWrite(pin3, brightness);
    //nilThdSleepMilliseconds(DelayTime+(255-brightness));
    nilThdSleepMilliseconds(DelayTime/4);
  }
}

void fadeIn(int pin){
  int brightness = 0;    // how bright the LED is
  int fadeAmount = 5;    // how many points to fade the LED by

  while(brightness <255 ){
    brightness = brightness + fadeAmount;
    analogWrite(pin, brightness);
    nilThdSleepMilliseconds(DelayTime);
  }
}

#ifdef DEBUG
  // Use tiny unbuffered NilRTOS NilSerial library.
  #include <NilSerial.h>
  // Macro to redefine Serial as NilSerial to save RAM.
  // Remove definition to use standard Arduino Serial.
  #define Serial NilSerial

#endif
void loop()
{
    // Used only on DEBUG:
    #ifdef DEBUG
      nilPrintStackSizes(&Serial);
      nilPrintUnusedStack(&Serial);
      Serial.println();

      // Delay for one second.
      // Must not sleep in loop so use nilThdDelayMilliseconds().
      // Arduino delay() can also be used in loop().
      nilThdDelayMilliseconds(1000);
    #endif
}

// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waMusic, 128);

NIL_THREAD(Music, arg) {
  taDa();
  while(true){

    // Sleep for 10 sec
    nilThdSleepMilliseconds(60000);
  }
}

NIL_WORKING_AREA(waBlinkingLights, 64);
NIL_THREAD(BlinkingLights,arg){
  //fadeIn(redLed);
  while(true){

    /*** FADER PART */

    fadeIn(yellowLed);
    fadeIn(blueLed);
    fadeOut(yellowLed);
    fadeIn(greenLed);

    //fadeOut(blueLed);
    fadeIn(yellowLed);
    //fadeIn(blueLed);
    nilThdSleepMilliseconds(2500);
    fadeOut3(blueLed,greenLed,yellowLed);
    // turn off all
    analogWrite(greenLed,0);
    analogWrite(yellowLed,0);
    analogWrite(blueLed,0);
    // Give some time
    nilThdSleepMilliseconds(900);

    //nilThdSleepMilliseconds(DelayTime);
  }
}

// Very tiny stack for this red alerter: we economize on the rest
NIL_WORKING_AREA(waBlinkingRed, 8);
NIL_THREAD(BlinkingRed,arg){
  const int minBright=50;
  const int maxBright=255;
  while(true){
    int pin=redLed;
    int brightness = minBright;    // how bright the LED is
    int fadeAmount = 5;    // how many points to fade the LED by

    analogWrite(pin, brightness);
    // fade in,,,,
    while(brightness <maxBright ){
      brightness = brightness + fadeAmount;
      analogWrite(pin, brightness);
      nilThdSleepMilliseconds(DelayTime);
    }

    // fad out...
    while(brightness > minBright ){
      brightness = brightness - fadeAmount;
      analogWrite(pin, brightness);
      nilThdSleepMilliseconds(DelayTime);
    }

  }
}

/** Thread static table
  A thread's priority is
  determined by its position in the table with highest priority first.
*/
NIL_THREADS_TABLE_BEGIN()
NIL_THREADS_TABLE_ENTRY(NULL /*TH NAME*/, Music,          NULL, waMusic,          sizeof(waMusic))
NIL_THREADS_TABLE_ENTRY(NULL            , BlinkingLights, NULL, waBlinkingLights, sizeof(waBlinkingLights))
NIL_THREADS_TABLE_ENTRY(NULL            , BlinkingRed, NULL, waBlinkingRed, sizeof(waBlinkingRed))
NIL_THREADS_TABLE_END()

 

Series NavigationArduino EggBoxLamp

Leave a Reply