Home
Accesso / Registrazione
 di 

Come scrivere una libreria per Arduino

Scrivere una libreria per Arduino

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:

  1. create un nuova cartella "LIBLED" nella sottocartella "libraries" del vostro ambiente di sviluppo per Arduino
  2. copiate i file LIBLED.h e LIBLED.cpp nella nuova cartella LEDLIB
  3. 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:

  1. permette di testare la libreria: questa infatti non viene compilata fino al momento in cui è inclusa da un'applicazione
  2. 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

 

 

Scrivi un commento all'articolo esprimendo la tua opinione sul tema, chiedendo eventuali spiegazioni e/o approfondimenti e contribuendo allo sviluppo dell'argomento proposto. Verranno accettati solo commenti a tema con l'argomento dell'articolo stesso. Commenti NON a tema dovranno essere necessariamente inseriti nel Forum creando un "nuovo argomento di discussione". Per commentare devi accedere al Blog
ritratto di arrigo

libreria arduino

Grazie per l'articolo, mi serviva e devo dire che sarebbe una grande opportunità poter scrivere librerie a più mani e poi pubblicarle.
Suggerimenti?
saluti, Arrigo

ritratto di slovati

ciao, se hai già qualche idea

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.

ritratto di Paulchen

una auito

Ciao slovati
Peccato che hai dimenticato di dire che la libreria da Te creata funziona solo con IDE1.0.
Per farla funzionare con tutte le versioni del IDe bastava scrivere nel LEDLIB.h al posto di
"#include "Arduino.h"
quello che suggerisce http://arduino.cc/en/Main/ReleaseNotes
"#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif "
Ciao Paulchen

ritratto di slovati

L'articolo è stato scritto

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.

ritratto di MarcoT

Caro Slovati, seguendo le sue

Caro Slovati, seguendo le sue istruzioni ho provato a creare una libreria per gestire i vari input possibili, leggendo gli ingressi con funzioni contenute nella libreria, ma già ai primi passi ho incontrato delle difficoltà (sono un ingegnere meccanico, per cui di informatica non ho molte nozioni). L'ambiente di Arduino in fase di compilazione mi dice che le funzioni definite nella libreria non sono visibili. Le allego il file header e il cpp:

.cpp

#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);

}

.h

#ifndef INPUTLIB_H
#define INPUTLIB_H

#include "Arduino.h"
#include
#include

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

ritratto di slovati

Buonasera MarcoT, anzitutto

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

ritratto di MarcoT

Molte grazie, è stato

Molte grazie, è stato gentilissimo ed estremamente chiaro. Mi è rimasto solo un dubbio riguardante la seconda linea del file esempio (INPUTLIB inputLib;): che scopo ha? E' necessaria per il corretto funzionamento della libreria?

Mille grazie

ritratto di slovati

Sì, quella riga è necessaria,

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.

 

 

Login   
 Twitter Facebook LinkedIn Youtube Google RSS

Chi è online

Ci sono attualmente 2 utenti e 22 visitatori collegati.

Ultimi Commenti