Premessa:
Le librerie sono un modo molto efficace per creare del codice riutilizzabile e condivisibile; per questo rivestono in Arduino un ruolo molto importante. chiunque puo contribuire a implementare nuove librerie e a migliorare la qualità dei progetti vivendo questa collettività chiamata open source.
La realizzazione di una libreria non differisce molto dalla scrittura di uno sketch, tant’è che in vari casi le librerie sono proprio l’evoluzione di qualche applciazione in cui oltre alla procedura principale – il classico void loop – ci sono delle funzioni che vengono ripetutamente chiamate dal loop principale e proprio per questo sono state messe come routine. Oltre a essere un buon metodo per semplificare gli sketch, offre un significativo vantaggio nella manutenzione del codice, dato che aggiornando la libreria, gli aggiornamenti si propagano a tutti gli sketch che ne fanno uso – nel momento in cui si ricompilino. Useremo il codice di esempio Arduino come tutorial per la creazione di una libreria. Questo esempio parte da uno sketch che genera il classico SOS tre punti, tre linee e tre punti accendendo il solito led collegato al pin 13. Per rendere l’esempio un po più pratico e comprensibile riportiamo qui sotto lo sketch:
int pin =13; void setup(){ pinMode(pin, OUTPUT); } void loop(){ dot();dot();dot(); dash();dash();dash(); dot();dot();dot(); delay(3000); } void dot(){ digitalWrite(pin, HIGH); delay(250); digitalWrite(pin, LOW); delay(250); } void dash(){ digitalWrite(pin, HIGH); delay(1000); digitalWrite(pin, LOW); delay(250); }
Il pin scelto per il LED è il 13, ma una libreria dovrebbe offrire il massimo della flessibilità, quindi la definizione del pin dovrebbe essere una variabile da passare alla libreria, anche perchè una libreria può essere utilizzata in più instanze indipendenti e quindi poter definire pin diversi diventa essenziale. Scorrendo lo sketch, troviamo quindi la definizione del pin e della sua modalità con pinMode(), quindi il loop principale che chiama le funzioni dot() e dash() e poi due funzioni che accendono e spengono il led collegato al “pin” per 1000 o 250 millisecondi. Una libreria è composta da tre file: .h .cpp e keyword.txt che contengono rispettivamente l’intestazione, il sorgente e le parole chiave per l’IDE. L’intestazione o header ha le definizioni per la libreria e contiene l’elenco di quanto la libreria vera e propria contiene ed è nel file del codice .cpp. Perchè il tutto funzioni, va anche rispettata la struttura a cartelle e il nome dei file: cartella, .h e .cpp devono avere il medesimo nome – nel nostro caso Morse e devono tovarsi nella cartella libraries dell’IDE di Arduino. Nel caso si voglia inserire qualche esempio, dovremo creare dentro alla cartella Morse una cartella examples e posizionare gli sketch esemplificativi in esse. Iniziamo con il file Morse.h, l’header o intestazione e vediamo cosa scriverci:
#ifndef Morse_h #define Morse_h #include "Arduino.h" class Morse{ public: Morse(int pin); void dot(); void dash(); private: int _pin; }; #endif
La parte centrale che inizia con class Morse è l’intestazione vera e propria, mentre le due istruzioni precedute dal # all’inizio e quella in fondo servono a gestire il meccanismo dell’include nel codice oggetto:l’istruzione ifndef controlla se Morse_h è gia stato definito in precedenza e in caso affermativo salta tutto fino al endif di chiusura evitando di reinserire il codice della libreria. La seconda riga definisce proprio Morse_h ed innesca il meccanismo di rilevazione per una eventuale seconda istruzione di include della nostra libreria. Questo costrutto va applicato a tutte le librerie definendo il nome dell’oggetto da controllare in accordo con il nome della libreria stessa nel nostro caso Morse_h. La riga #include “arduino.h” include la libreria specifica di Arduino che contiene i tipi e le costanti specifiche; il suo include avviene automaticamente per gli sketch, ma non per le librerie e quindi è importante ricordarselo pernon incontrare errori di compilazione. Con class Morse viene definita la classe, che altro non è se non un contenitore dove in ogni riga viene descritta una variabile o una funzione della classe stessa, tenendo di fatto tutto assieme in un solo punto facilmente gestibile. Nella classe viene anche definito per ciascuna funzione e variabile se si tratta di qualcosa di pubblico, ovvero accessibile dall’esterno, o privato e quindi utilizzabile solo dall’interno della classe stessa. Le due Aree sono delimitate dalle due etichette pubblic: e private: .Sempre nella classe si trova la funzione costruttore che crea un’istanza della classe. il costruttore ha lo stesso nome della classe e non ha alcun valore di ritorno. Nel nostro caso il costruttore è Morse(int pin) e definisce il meccanismo di creazione dell’istanza della classe con anche il pinsu cui va gestito il lampeggio come varibile pubblica passata alla classe. Ora vediamo come realizzare il file Morse.cpp che contiene le funzioni con il codice preso dello sketch originario, ma con le dovute aggiunte.
#include "Arduino.h" #include "Morse.h" Morse::Morse(int pin){ pinMode(pin, OUTPUT); _pin = pin; } void Morse::dot(){ digitalWrite(_pin, HIGH); delay(250); digitalWrite(_pin, LOW); delay(250); } void Morse::dash(){ digitalWrite(_pin, HIGH); delay(1000); digitalWrite(_pin, LOW); delay(250); }
Arduino.h e Morse.h sono indispensabili per poter disporre sia dei tipi dati e costanti di aruino, sia per avere a disposizione le definizioni e le variabili definite nell’header. Con Morse::Morse(int pin) abbiamo il costruttore che descrive cosa succede quando viene creata un’istanza della classe. In questo caso abbiamo il pin che l’utente configura per il lampeggio del LED, utilizzato dal codice per definire come output il pin stesso e successivamente assegnato come valore alla variabile privata _pin. Al proposito, vale la pena spendere due parole sulle variabili private e i loro nomi: _pin avrebbe potuto essere definito con qualsiasi altro nome, ma dato che rappresenta la variabile pubblica pin passata alla libreria tramite le chiamate di creazione della classe, aggiungendo il _ si riesce facilmente a riconoscerla visivamente pur avendo un nome diverso dal punto di vista informatico. Questa variabile è anche definita nel file header ed è quindi importante che tutto sia allineato. Proseguendo nella nostra libreria troviamo le funzioni dot e dash che ricalcano molto da vicino il codice originale dello sketch, con le seguenti differenze: il nome della funzione è preceduto da quello della classe e dai ::, mentre la variabile utilizzata è quella privata _pin. A questo punto si può accedere con la creazione del terzo file della libreria keyword.txt nel quale inseriremo le parole chiave che servono all’IDE per sapere cosa evidenziare e in che colore.
Morse KEYWORD1 dash KEYWORD2 dot KEYWORD2
Le parole chiave e la loro tipologia vanno speparate con un TAB (tabulazione) e non con degli spazi; le distinzioni sono KEYWORD1 per le classi colorate in arancione KEYWORD2 per le funzioni colorate in marrone. Se non si crea questo file, la libreria è ugualmente funzionante e il compilatore non genera alcun errore, ma si perde l’evidenzazione. Con itre file Morse.h, Morse.cpp e KEYWORD.txt pronti, potete procedere con l’inserimento degli stessi nella cartella Morse, da posizionare all’interno di libraries la sottocartella che si trova nella cartella dell’installazione di Arduino. Ora vediamo come utilizzare la nostra libreria appena creata:
#include <Morse.h> Morse morse(13); Morse morse2(8); void setup(){ } void loop(){ morse.dot();morse.dot();morse.dot(); morse.dash();morse.dash();morse.dash(); morse.dot();morse.dot();morse.dot(); delay(3000); morse2.dot();morse2.dot();morse2.dot(); morse2.dash();morse2.dash();morse2.dash(); morse2.dot();morse2.dot();morse2.dot(); delay(3000); }
Come potete vedere nell’esempio la classe Morse può essere creata in più istanze semplicemente assegnando nomi diverse alle istanze.
Buon progetto.