Chi ha già provato a scrivere uno sketch per Arduino sa che lo sviluppo per questo tipo di piattaforma è relativamente semplice, grazie a un linguaggio intuitivo, un ambiente di sviluppo integrato, e numerosi esempi già pronti per l'uso. Se a questo punto volessimo condividere il codice che abbiamo sviluppato con dei nostri amici o con degli appassionati sparsi in giro per il mondo, il passo da seguire è quello di preparare una libreria. Vediamo come.
Come esempio, creeremo una libreria molto semplice. Del resto, quello che ci interessa maggiormente ora è apprendere i passi da seguire per la creazione di una nuova libreria; questi passi potranno poi essere applicati anche al caso di librerie per applicazioni maggiormente complesse.
Quali sono i principali vantaggi che derivano dall'utilizzo di una libreria? I vantaggi offerti da una libreria possono essere sintetizzati nel modo seguente:
- semplifica l'utilizzo e l'organizzazione del codice. Infatti, una libreria ben scritta e testata, mette a disposizione dell'utilizzare delle funzioni pronte per l'uso: l'utente non deve preoccuparsi di come una particolare funzione è stata implementata, è sufficiente sapere come deve essere utilizzata
- migliora la leggibilità del codice: il codice dell'applicazione si snellisce ed è più semplice da comprendere
- decentralizza la logica: lo sketch può infatti focalizzarsi su uno specifico processing, trascurando (o meglio "nascondendo") gli aspetti implementativi gestiti all'interno della libreria. Si usa spesso dire che una libreria deve specificare cosa, quando, e dove, ma non come
- permette di mantenere aggiornato il codice: se una stessa libreria viene utilizzata da più utenti, quando viene rilasciato un aggiornamento (in seguito ad esempio a del bug-fixing, oppure a seguito dell'aggiunta di nuove funzionalità), la stessa libreria può essere importata dagli utenti, che potranno così disporre immediatamente della nuova versione (con minime o addirittura senza alcuna modifica al proprio codice)
Supponiamo ora di voler scrivere una libreria che permetta di controllare lo stato di un led collegato a una uscita di Arduino. Possiamo pensare alle seguenti funzioni da includere nella nostra libreria:
- initialize: è la funzione di inizializzazione, quindi verrà chiamata una sola volta allo startup del sistema. Riceve come parametro il numero del segnale (pin) della scheda Arduino che si intende utilizzare per comandare il led. Il codice di questa procedura provvederà automaticamente a configurare questo pin come pin di uscita. Non è previsto alcun valore di ritorno dalla funzione. Il suo prototipo sarà perciò di questo tipo:
void initialize (byte pinLed);
- on: forza l'accensione del led. Non è richiesto alcun parametro e non esiste valore di ritorno. Il suo prototipo sarà perciò il seguente:
void on(void);
- off: forza lo spegnimento del led. Non è richiesto alcun parametro e non esiste valore di ritorno. Il suo prototipo sarà perciò il seguente:
void off(void);
- blink: causa il lampeggio del led. Come parametro viene passato il periodo desiderato per il lampeggio. Se ad esempio si vuole ottenere un lampeggio alla frequenza di 20 Hz, occorrerà passerà come parametro il valore 50 (l'unità di misura sono i millisecondi). Per questo parametro è stata prevista una variabile di tipo unsigned short in modo tale da poter gestire periodi anche superiori a 255 millisecondi. Non è previsto alcun valore di ritorno, per cui il prototipo di questa funzione è il seguente:
void blink (unsigned short periodMs);
Possiamo a questo punto vedere l'implementazione completa di queste funzioni; il passo successivo sarà quello di "calarle" all'interno di una libreria:
void initialize (byte pinLed) { pinGlob = pinLed; pinMode(pinGlob, OUTPUT); }
void on(void) { digitalWrite(pinGlob, HIGH); }
void off(void) { digitalWrite(pinGlob, LOW); }
void blink (unsigned short periodMs) { on(); delay(periodMs/2); off(); delay(periodMs/2); }
Abbiamo a questo punto visto una possibile implementazione per le funzioni che vogliamo includere nella nostra libreria. Prima di procedere, è però utile spendere qualche parola sulla convenzione adottata nel mondo Arduino per scegliere i nomi delle funzioni e delle variabili. Questa convenzione, che seguiremo nella scrittura della nostra libreria, prevede che i nomi della libreria siano in UppercaseUppercase (MaiuscoloMaiuscolo), mentre quelli delle funzioni in lowercaseUppercase (minuscoloMaiuscolo). Ciò significa che la nostra libreria dovrà avere un nome tutto maiuscolo (scegliamo ad esempio il nome LEDLIB), mentre i nomi delle funzioni e delle variabili saranno tutti in minuscolo, con maiuscolo solo la prima lettera di ogni parola (eccetto la prima parola, che è tutta in minuscolo). Nel mondo Arduino, le librerie corrispondono a delle classi (si segue la stessa sintassi del C++), cioè a dei particolari tipi di strutture. Ogni classe si suddivide poi in due file:
- un file header (estensione .h), contenente la dichiarazione della classe, oltre alla definizione di eventuali tipi e costanti. Nel nostro caso, questo file si chiamerà LEDLIB.h
- un file con il codice sorgente (estensione .cpp), contenente l'implementazione della classe, cioè il codice relativo a tutte le sue funzioni (esportate o locali). Le funzioni esportate da una classe corrispondono a quelle esportate dalla libreria, e vengono anche chiamate metodi. Nel nostro caso, il file sorgente della libreria si chiamerà LEDLIB.cpp
Il listato relativo al file LEDLIB.h sarà perciò il seguente:
#ifndef LEDLIB_H #define LEDLIB_H #include "Arduino.h" class LEDLIB { private: byte pinGlob; // pin utilizzato per pilotare il LED public: void initialize (byte pinLed); void on(void); void off(void); void blink (unsigned short periodMs); }; #endif
E' importante notare come tutte le funzioni della libreria siano "public", quindi visibili all'esterno, e quindi richiamabili da un'applicazione. E' invece dichiarata come "private" la variabile pinGlob in quanto questa è locale alla classe e non deve essere visibile al suo esterno. Questo è invece il codice relativo all'implementazione della classe (LIBLED.cpp):
/* LEDLIB.cpp - Libreria di esempio per gestire l'accensione, lo spegnimento, e il lampeggio di un LED. */ #include "LEDLIB.h" // dichiarazione della classe /* funzione di inizializzazione */ void LEDLIB::initialize (byte pinLed) { pinGlob = pinLed; pinMode(pinGlob, OUTPUT); } /* funzione di accensione del led */ void LEDLIB::on(void) { digitalWrite(pinGlob, HIGH); } /* funzione di spegnimento del led */ void LEDLIB::off(void) { digitalWrite(pinGlob, LOW); } /* funzione di lampeggio del led */ void LEDLIB::blink (unsigned short periodMs) { on(); delay(periodMs/2); off(); delay(periodMs/2); }
Siamo ora a buon punto, dobbiamo ancora vedere come "installare" la libreria (non preoccupatevi, è un'operazione molto semplice), e scrivere un piccolo esempio di utilizzo della stessa. I passi da seguire sono i seguenti:
- create un nuova cartella "LIBLED" nella sottocartella "libraries" del vostro ambiente di sviluppo per Arduino
- copiate i file LIBLED.h e LIBLED.cpp nella nuova cartella LEDLIB
- lanciate l'ambiente di sviluppo Arduino e selezionate l'opzione Sketch->Import Library dal menu principale: magia! Se avete eseguito correttamente i passi precedenti, sarà visibile nell'elenco la nuova libreria LEDLIB. Non selezionate comunque l'opzione di importazione della libreria, questa serve solo per includere l'header della libreria stessa nello sketch corrente
Il codice dell'esempio è il seguente:
#include <LEDLIB.h> LEDLIB led; void setup() { led.initialize(13); } void loop() { led.blink(1000); }
La creazione di almeno un file di esempio è molto importante per due motivi:
- permette di testare la libreria: questa infatti non viene compilata fino al momento in cui è inclusa da un'applicazione
- la "filosofia" di Arduino prevede di allegare sempre, insieme alla libreria, almeno un file di esempio. Se provate infatti a guardare nella cartella "libraries" della vostra installazione Arduino, potrete trovare la sottocartella "examples" in corrispondenza di ogni libreria presente (ad esempio per la libreria EEPROM)
Se avete provato a caricare l'esempio di cui sopra, avrete potuto notare come l'ambiente di sviluppo di Arduino non abbia riconosciuto nessuno dei nuovi nomi introdotti (la classe e i suoi metodi). Ciò è normale, bisogna infatti "istruire" Arduino su quali sono questi nuovi simboli, in modo tale che l'IDE possa applicare ad essi l'appropriata colorazione. Per fare questo è sufficiente creare un file chiamato "keywords.txt" che andrà copiato nella stessa cartella in cui sono presenti i file della libreria. Nel nostro caso, il contenuto del file sarà il seguente:
####################################### # Syntax Coloring Map For LEDLIB ####################################### LEDLIB KEYWORD1 initialize KEYWORD2 on KEYWORD2 off KEYWORD2 blink KEYWORD2
Vengono in pratica elencati i nuovi simboli e il tipo di colorazione da applicare agli stessi (per separare il nome del simbolo e il tipo di keyword, occorre utilizzare un singolo carattere TAB). Questo è l'ultimo passo necessario alla creazione di una libreria. Potete a questo punto distribuire a chi interessato le librerie da voi stessi create: sarà sufficiente, per praticità, comprimere tutti i file della cartella relativa alla libreria e rilasciare il file compresso.
Arduino è disponibile da Farnell


ciao,
se hai già qualche idea in merito, potresti renderla pubblica nell’aposita sezione Forum. Magari c’è qualche lettore che avrebbe bisogno di interfacciare Arduino con qualche dispositivo elettronico particolare ma non ha e non sa come scrivere il relativo codice. Creando una libreria ad hoc si potrebbe risolvere un’esigenza comune a più progettisti.
L’articolo è stato scritto come una semplice guida per chi sta muovendo, o ha intezione di farlo, i primi passi nel mondo Arduino. La tua osservazione è corretta, ma resta comunque il fatto che Arduino 1.0 è, alla data di stesura dell’articolo e a tutt’oggi, l’unica versione ufficiale del sistema di sviluppo per Arduino che è possibile scaricare (http://arduino.cc/hu/Main/Software). Non solo, nella stessa pagina viene indicato a chiare lettere che le precedenti versioni di IDE non sono più supportate dal team di sviluppatori. La mancata “compatibilità” all’indietro dell’esempio illustrato nell’articolo non è una dimenticanza ma un incitamento ad utilizzare il sistema di sviluppo più aggiornato e l’unico attualmente supportato.
Buonasera MarcoT,
anzitutto mi complimento con lei, che pur essendo un ingegnere meccanico dimostra una buona dimistichezza con la programmazione dei microcontrollori.
Per quanto riguarda il suo problema, le consiglio di operare nel modo seguente (occorre fare anche qualche commento ai due file .h e .cpp della sua libreria, ma ne parleremo più avanti).
1. si posizioni con il file manager di Windows sulla cartella dove è presente l’eseguibile di Arduino, e poi entri nella cartella libraries.
Qui deve creare una nuova cartella per la sua libreria, che chiameremo INPUTLIB
2. prenda i due file sorgente della libreria (assumo che li abbia chiamati INPUTLIB.h e INPUTLIB.cpp) e li copi all’interno della cartella INPUTLIB. A questo punto la sua nuova libreria è “installata” all’interno dell’ambiente di sviluppo Arduino. Per verificarlo, lanci Arduino, poi selezioni dal menu principale Sketch->Importa libreria…->INPUTLIB. Arduino dovrebbe automaticamente includere la riga #include <INPUTLIB.h>, a dimostrazione che ha riconosciuto la nuova libreria.
3. può partire da questa semplice riga iniziale per scrivere uno sketch di esempio. Questo ha una duplice funzione: a. permette di compilare il codice della libreria verificandone la correttezza sintattica; b. permette di testare su una board Arduino il funzionamento della libreria. Io mi sono limitato a includere due chiamate ai due metodi (procedure) da lei definiti nella libreria, e il compilatore ha generato senza errori lo sketch da scaricare su Arduino.
Il codice del programmino di esempio è il seguente:
#include <INPUTLIB.h>
INPUTLIB inputLib;
void setup()
{
}
void loop()
{
inputLib.encoder(3);
inputLib.button(10);
}
Detto questo, volevo fare due commenti al codice della libreria. Anzitutto occorre rimuovere le due istruzioni “#include” più o meno all’inizio del .h (penso si tratti di un copia e incolla, comunque vanno eliminate). Poi ho visto che ha dichiarato delle variabili locali alla classe, però poi non le usa. In realtà usa delle variabili con lo stesso nome, ma all’interno dei due metodi (funzioni) che ha definito nella classe. Quindi le variabili dichiarate private nella classe (e non utilizzate) andrebbero rimosse (non generano errori di compilazione ma sono “pericolose” e creano confusione).
Al posto delle variabili statiche definite nei suo due metodi, lei potrebbe in alternativa utilizzare le variabili private già definite. Queste andrebbero inizializzate tramite un apposito metodo chiamato poi dalla funzione setup dello sketch. Il vantaggio di usare variabili private anzichè statiche ai metodi è che con il primo approccio le variabili sono visibili a tutti i metodi della classe (lei potrebbe aggiungerne altri in futuro), mentre con il secondo le variabili sono visibili solo dal metodo in cui sono dichiarate.
Comunque ecco i due file nella versione con variabili statiche (compilati correttamente).
==========
INPUTLIB.h
==========
#ifndef INPUTLIB_H
#define INPUTLIB_H
#include “Arduino.h”
class INPUTLIB
{
public:
int8_t encoder(uint8_t ENC_PORT);
bool button (uint8_t BUTTONPIN);
private:
//bool puls;
//bool oldpuls;
//bool state;
//int8_t enc_states[];
//uint8_t old_AB;
};
#endif
============
INPUTLIB.cpp (invariato rispetto alla sua versione)
============
#include “INPUTLIB.h” // dichiarazione della classe
/* Encoder. Restituisce 0,-1 o +1 a seconda della rotazione e del senso di rotazione dell’encoder */
int8_t INPUTLIB::encoder(uint8_t ENC_PORT)
{
static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint8_t old_AB = 0;
old_AB <<= 2; old_AB |= ( ENC_PORT & 0x03 ); return ( enc_states[( old_AB & 0x0f )]); } /* Button. Legge gli input di un bottone. */ bool INPUTLIB::button (uint8_t BUTTONPIN) { static bool puls; static bool oldpuls; static bool state = 0; puls = digitalRead (BUTTONPIN); //leggo il valore del pulsante if ((puls == HIGH)&&(oldpuls == LOW)) { state = 1 - state; delay(10); } oldpuls = puls; return (state); } Saluti
Sì, quella riga è necessaria, e serve a creare un “oggetto” per la classe INPUTLIB. La creazione di questo oggetto (inputLib) corrisponde in pratica alla creazione di una variabile, o struttura dati (un pò come quando si dichiara una variabile ‘i’ di tipo intero con l’istruzione “int i;”). Per creare un altro oggetto, basterà aggiungerlo: INPUTLIB inputLib1, inputLib2; e così via.
L’oggetto (o istanza) della classe permette di accedere alle sue funzioni (dette metodi) e alle sue variabili.
Ciao Stefano,
ho trovato molto utile questa tua semplice guida. Nonostante uso da un po’ arduino, non mi son mai cimentato nello scrivere librerie.
Ho una domanda : in una libreria posso avere piu’ classi ? in altri ambienti questo e’ normale.
Mi chiedo se c’e’ il limite una libreria = una classe, per una questione di nomi, oppure posso includere anche diverse classi. Grazie.