Arduino Real Time EggBox
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.
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...
[code lang="cpp"]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 [/code]
Il codice è disponibile su github, ma lo replichiamo qui:
[code lang="cpp"]
/** 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()
[/code]