Processing - Homepage | DidatticaWEB

39
Elettronica In ~ Settembre 2011 147 Introduzione a Processing Corso P rocessing rocessing è un linguaggio di program- mazione sviluppato con il preciso scopo di rendere molto semplice l’interazione con elementi grafici; più esattamente, è finaliz- zato alla realizzazione di immagini, anima- zioni e interazioni. Ottenere questo con altri linguaggi, come ad esempio C++ o Java, risulta complesso e richiede un grado molto elevato di conoscenze. Gli ideatori e svilup- patori di Processing sono Casey Reas e Ben Fry: il primo è professore al dipartimento di Design And Media Arts alla UCLA, mentre Ben Fry è un designer e programmatore di Cambridge. Processing è stato sviluppato dal 2001, con la finalità di ottenere un lin- guaggio semplice per gli studenti di arte e design e per quelli ad indirizzo tecnico. Processing è stato notevolmente sviluppato Conosciamo il linguaggio di programmazione open-source creato per realizzare immagini e animazioni, le cui librerie ed il cui ambiente sono compatibili con l’ambiente di sviluppo di Arduino. Prima puntata. di MATTEO FIORAVANTI P

Transcript of Processing - Homepage | DidatticaWEB

Page 1: Processing - Homepage | DidatticaWEB

Elettronica In ~ Settembre 2011 147

Introduzione a Processing

Cors

o Pr

oces

sing

rocessing è un linguaggio di program-mazione sviluppato con il preciso scopo

di rendere molto semplice l’interazione con elementi grafi ci; più esattamente, è fi naliz-zato alla realizzazione di immagini, anima-zioni e interazioni. Ottenere questo con altri linguaggi, come ad esempio C++ o Java, risulta complesso e richiede un grado molto elevato di conoscenze. Gli ideatori e svilup-

patori di Processing sono Casey Reas e Ben Fry: il primo è professore al dipartimento di Design And Media Arts alla UCLA, mentre Ben Fry è un designer e programmatore di Cambridge. Processing è stato sviluppato dal 2001, con la fi nalità di ottenere un lin-guaggio semplice per gli studenti di arte e design e per quelli ad indirizzo tecnico.Processing è stato notevolmente sviluppato

Conosciamo il linguaggio di programmazione open-source creato per realizzare immagini e animazioni, le cui librerie ed il cui ambiente sono compatibili con l’ambiente di sviluppo di Arduino. Prima puntata.

di MATTEO FIORAVANTI

P

Page 2: Processing - Homepage | DidatticaWEB

148 Settembre 2011 ~ Elettronica In

Corso Processing

in questi anni, come pure le sue numerose librerie, che ad oggi coprono varie funzio-nalità: grafi ca 3D, comunicazione seriale, interfacciamento con Arduino, audio.Processing e le sue librerie sono comple-tamente liberi, e come per gran parte di tutti i software open-source, la comunità di utilizzatori, appassionati, programmatori, hobbisti è molto ampia.Generalmente quando si studia un linguag-gio di programmazione si parte sostanzial-mente dalla struttura, dai concetti teorici, dagli algoritmi e dai vari metodi di pro-grammazione, relegando alla fi ne l’intera-zione con la grafi ca e magari le animazioni.Utilizzando Processing, si parte direttamen-te da grafi ca e animazioni, fermo restando che devono essere compresi le strutture di programmazione e i vari costrutti.La piena compatibilità con Arduino è un altro aspetto che rende Processing estrema-mente interessante, senza contare che gli ambienti di programmazione sono pratica-mente identici. Con Arduino e Processing abbiamo la possibilità di realizzare delle applicazioni complete, in cui i dispositivi hardware vengono realizzati utilizzando Arduino e gli applicativi software sono scritti in Processing. Chi già conosce Ardui-no, troverà sicuramente familiare l’ambien-te di Processing, dato che è praticamente lo stesso. Chi invece non conosce Arduino e volesse cominciare il suo percorso di apprendimento direttamente da Processing, non incontrerà alcuna diffi coltà e potrà inte-grare successivamente i concetti di Arduino, se mai ne avesse necessità.

Il corso che presentiamo ha lo scopo di for-nire a studenti ed hobbisti le nozioni base per utilizzare Processing e per realizzare dei programmi che si interfaccino con Arduino.Alla fi ne di questo corso si avranno le no-zioni base per realizzare una GUI (Graphic User Interface) completa con la quale sarà possibile controllare Arduino.Il sito internet di riferimento è http://pro-cessing.org, da cui è possibile scaricare Processing e trovare tutte le informazioni del caso, esempi, riferimenti, librerie, libri consigliati; rimandiamo ad esso per qual-siasi approfondimento relativamente agli argomenti trattati in questo corso.

SCARICARE ED INSTALLARE PROCESSINGSi comincia visitando il sito http://proces-sing.org/download e selezionando la versio-ne relativa al sistema operativo che si sta utilizzando: Windows, Mac, Linux. Proces-sing è indipendente dal sistema operativo e il codice creato con esso può essere traspor-tato da un sistema all’altro senza alcuna modifi ca.L’ultima versione di Processing disponibile nel momento in cui scriviamo questo corso è la Processing-1.2.1; in ogni caso, nel sito sono sempre disponibili le versioni prece-denti.Per quanto riguarda la versione Windows, ci sono due modalità: 1) Windows; 2) Windows (Without Java).

La prima modalità è quella standard, men-tre la seconda presuppone l’installazione da soli di JDK (Java Development Kit), che è diverso dal comune JRE (Java Run Time Engine). Sostanzialmente, Processing si fonda su Java ed in un certo senso ne è una derivazione, pertanto per funzionare cor-rettamente necessita di alcune componenti, che nella modalità standard vengono instal-late automaticamente, mentre nella seconda modalità prevista bisogna farlo da soli. La seconda modalità di installazione deve essere seguita soltanto se si ha dimestichez-za con Java e con tutto il Development Kit (ovviamente se per altri scopi abbiamo già installato JDK, seguiremo questa modalità).Una volta scaricato, Processing si presente-Fig. 1

Page 3: Processing - Homepage | DidatticaWEB

Elettronica In ~ Settembre 2011 149

Cors

o Pr

oces

sing

rà come un fi le .zip, quindi occorre estrarlo e posizionare la cartella in una locazione dell’HD; a questo punto basta fare do ppio clic su processing.exe per cominciare.

PROCESSING DEVELOPMENTENVIRONMENT (PDE)Una volta avviato il Processing Develop-ment Environment, si presenterà un’area chiamata Text Editor, dove verranno scritti i programmi; appena sopra quest’area c’è una fi la di pulsanti denominata Toolbar. Sotto al Text Editor c’è la Message Area e sotto a questa la Console. La Message Area è usata per inviare da Processing i messaggi di erro-re, mentre la Console serve per trasmettere, ad esempio, dei messaggi dal programma in esecuzione. La Fig. 1 mostra il PDE.I programmi creati con Processing vengono chiamati sketch, termine familiare a chi ha seguìto il nostro corso su Arduino.La Toolbar è composta dai seguenti pulsanti:1. RUN – avvia il programma;2. STOP – ferma il programma;3. NEW – apre un altro editor;4. OPEN – apre un altro programma;5. SAVE – salva il lavoro corrente; il forma-

to di un fi le in Processing è .pde;6. EXPORT – crea una cartella unica con

tutto il lavoro realizzato, che può essere installata su un web-server ed è compo-sta da un fi le .pde che è il codice sorgen-te; il fi le .jar sarà il programma, mentre il fi le .html corrisponde alla pagina web.

In EXPORT, facendo doppio clic sul fi le in-dex.html si avvierà il browser web, mostran-do lo sketch creato.Nel menù File si trova anche il comando Export to Application, che crea un’applica-zione per il sistema operativo che vogliamo: Mac, Windows, Linux. L’applicazione creata sarà quindi trasportabile ed installabile su altri PC che non hanno Processing.Scrivere codice coinvolge spesso lo studio e la vera e propria esplorazione di esempi già fatti; sotto questo aspetto, Processing già di suo ha tantissimi esempi molto ben struttu-rati in argomenti e categorie: basta imparti-re il comando di menu File –> Examples ed eseguire i vari codici sorgente disponibili. Un altro strumento indispensabile per

iniziare a programmare con Processing è il Processing Reference, cioè la lista di tutte le primitive del linguaggio con tanto di spiegazione e breve codice di esempio. Per accedere al Refence basta andare sul menu Help ed impartire il comando Reference, allorché si aprirà la pagina html con la lista di tutte le API del linguaggio Processing; basta fare clic su una di esse e si aprirà la relativa pagina con la descrizione ed il codi-ce di esempio.

CODICEI programmi di esempio che proporremo in questo corso sono i seguenti:1. LEZIONE 1_1 FORME BASE; questo

programma mostra come disegnare linee, rettangoli, cerchi, quadrilateri sull’area di disegno;

2. LEZIONE 1_2 FORME BASE 2; rispetto al programma precedente, aggiunge le funzionalità legate al tratto di disegno ed al riempimento di colore delle fi gure;

3. LEZIONE 1_3 COLORI BASE; illustra le funzionalità legate a colori, riempimenti e trasparenze;

4. LEZIONE 1_4 ELETTRINO 1; questo programma permette di disegnare un robot con le funzionalità descritte nei programmi precedenti;

5. LEZIONE 1_5 STRUTTURA BASE DI UN PROGRAMMA; in esso viene mo-strata la struttura base di un programma scritto in Processing;

6. LEZIONE 1_6 CONTROLLO DI FLUS-SO_RIPETIZIONI; questo programma descrive l’uso dei cicli FOR;

7. LEZIONE 1_7 CONTROLLO DI FLUS-SO_IF; questo programma mostra l’uti-lizzo del costrutto condizionale IF;

8. LEZIONE 1_8 USO DEL TESTO; aggiun-giamo del testo al programma dell’eser-cizio precedente;

9. LEZIONE 1_9 - ANIMAZIONE; realiz-ziamo una semplice animazione.

Partiamo dunque dal primo programma di esempio.

LEZIONE 1_1 FORME BASE Iniziamo ora a scrivere il nostro primo programma, che consiste semplicemente

Page 4: Processing - Homepage | DidatticaWEB

150 Settembre 2011 ~ Elettronica In

Corso Processing

nel rappresentare sullo schermo delle fi gure geometriche utilizzando le funzioni native di Processing.La dimensione dell’area in cui andremo a disegnare le nostre forme è defi nita dalla funzione: size(), le dimensioni dell’area di disegno sono defi nite in pixel e i parame-tri da passare alla funzione sono width ed eight, ovvero larghezza ed altezza.L’origine 0,0 del piano di disegno è posta nello spigolo in alto a sinistra, la direzione di incremento dell’asse X è verso destra, mentre la direzione di incremento dell’asse Y è verso il basso.La funzione background() permette di defi nire il colore di sfondo del piano di disegno; inserendo un solo valore che va da 0 a 255, Processing riconoscerà che stiamo lavorando in scala di grigi. Successivamente

affronteremo il tema dei colori.Il comando smooth() evita la spigolosità delle varie forme disegnate, mentre stro-keWeight() defi nisce lo spessore del tratto in pixel. Le linee vengono disegnate con il comando line(), che si aspetta le coordinate del punto di partenza (X1,Y1) e di quello di arrivo (X2,Y2). In maniera del tutto similare triangle() disegna dei triangoli e dobbia-mo defi nire le coordinate dei tre vertici (X1,Y1,X2,Y2,X3,Y3). Per disegnare dei qua-drilateri si usa il comando quad(), inseren-do le coordinate dei quattro vertici. Diverso è il caso per i rettangoli, che sono disegnati con il comando rect(), i cui parametri sono le coordinate X,Y dello spigolo in alto a sinistra, la larghezza del rettangolo e la sua altezza. In maniera del tutto simile a come vengono disegnati i rettangoli, si tracciano

Fig. 2 Fig. 3 Fig. 4

Listato 1size(260,400); //dimensioni X,Y dell’area di disegno background(205); //sfondo dell’area di disegno, in gradazione di grigio

smooth(); //forme disegnate meno spigolose

strokeWeight(2); //spessore del tratto di disegno pin px

line(60,60,200,20); //linea X1,Y1, X2,Y2 triangle(200,60,200,100,60,100); //triangolo X1,Y1,X2,Y2,X3,Y3 quad(100,120,180,140,160,160,60,160); //quadrilatero X1,Y1,X2,Y2,X3,Y3,X4,Y4

rect(80,200,100,40); //rettangolo X,Y, larghezza, altezza

ellipse(120,280,50,50); //ellisse X,Y, larghezza, altezza

arc(120, 350, 70, 70, 0, PI+HALF_PI); //arco X,Y, largh, Altezza, ang inizio, ang fine

Page 5: Processing - Homepage | DidatticaWEB

Elettronica In ~ Settembre 2011 151

Cors

o Pr

oces

sing

le ellissi; il comando corrispondente è ellip-se(), i cui parametri sono le coordinate X,Y del centro la larghezza e l’altezza (ovvia-mente se altezza e larghezza sono identiche otterremo un cerchio). L’ultimo comando che esaminiamo è arc(), il cui funzionamento è identico a ellipse(), ma ha due parametri in più, che sono l’angolo di inizio e l’angolo di fi ne (espressi in ra-dianti e con verso positivo di crescita quello antiorario).Il codice corrispondente è quello visibile nel Listato 1.

LEZIONE 1_2 FORME BASE 2 Il codice di questo secondo programma aggiunge due particolari funzionalità a quello precedente, o meglio, prima di ogni primitiva di disegno defi nisce fi ll() e stro-keWeight(). Ne consegue che ogni forma disegnata avrà il suo colore di riempimento

ed il suo spessore del tratto di disegno. La Fig. 2 mostra le forme base del programma precedente, mentre la Fig. 3 illustra le forme base con le nuove funzionalità appena descritte.

LEZIONE 1_3 COLORI BASEIl codice di questo terzo programma mostra come utilizzare i colori e le funzionalità di Processing relativamente alle trasparenze; con esso vengono disegnate due combina-

Fig. 5

Listato 2size(280,420); //dimensioni X,Y dell’area di disegno //l’origine è situata nello spigolo in alto a sinistra, //X incrementa da sinistra a destra 0 ---> X //Y incrementa dall’alto verso il basso 0 | // | // V // Y

//i colori sono R - G - B //i valori che possono assumere vanno da 0 --> 255 //nella funzione fill() possiamo mettere un solo parametro, quindi stiamo lavorando in gradazioni di grigio //nella funzione fill() possiamo mettere tre parametri, quindi lavoreremo in RGB

background(0,0,0);//sfondo dell’area di disegno nero R=0, G=0, B=0

smooth(); //questa funzione permette di rendere le forme disegnate meno spigolose

noStroke(); //con questa funzione le forme disegnate sono prive di contorno

fill(255, 0, 0); //riempimento di colore rosso ellipse(80,80,140,140); //prima circonferenza fill(0, 255, 0); //riempimento di colore verde ellipse(200,80,140,140); //seconda circonferenza fill(0, 0, 255); //riempimento di colore blu ellipse(140,140,140,140); //terza circonferenza

//oltre ai colori si può impostare la trasparenza //i valori vanno da 0 --> 255 //nella funzione fill() possiamo mettere quattro parametri: R,G,B, trasparenza

fill(255, 0, 0,100); //riempimento di colore rosso e trasparenza 100 ellipse(80,280,140,140); //prima circonferenza fill(0, 255, 0, 100); //riempimento di colore verde e trasparenza 100 ellipse(200,280,140,140); //seconda circonferenza fill(0, 0, 255, 100); //riempimento di colore blu e trasparenza 100 ellipse(140,340,140,140); //terza circonferenza

Page 6: Processing - Homepage | DidatticaWEB

152 Settembre 2011 ~ Elettronica In

Corso Processing

zioni di tre cerchi con i colori base. Nella prima sequenza i colori sono pieni e non ci sono trasparenze, mentre nella seconda vengono utilizzate le trasparenze Fig. 4. Trovate il codice corrispondente a questo esempio nel Listato 2.

LEZIONE 1_4 ELETTRINO 1A questo punto mettiamo as-sieme tutte le funzioni descrit-te ed incominciamo a realizza-re un disegno.Il codice di questo quarto pro-gramma permette di combinare le forme base per disegnare un piccolo robot (Fig. 5) cui diamo il nome di Elettrino e che richiameremo più avanti in successivi programmi, nei quali saranno aggiunte altre funzionalità.

LEZIONE 1_5 STRUTTURA BASEDI UN PROGRAMMAIl quinto programma esegue un ciclo in cui nel terminale viene scritto continuamente

un numero che viene incrementato ad ogni ciclo (Fig. 6). La struttura di un programma scritto in Processing si compone di tre parti fondamentali:• la dichiarazione delle variabili;• void setup(){} che è una parte di codice

eseguito una sola volta;

Fig. 6

Listato 3int cont; //dichiarazione della variabile cont come intero

//setup viene eseguito una sola volta void setup(){ cont=0; //inizializzazione della variabile cont println(“SETUP - eseguito una sola volta”); //scrittura a terminale }

//draw è un ciclo che viene eseguito continuamente void draw(){ println(“ciclo continuo - in esecuzione”); //scrittura a terminale println(cont); //scrittura a terminale del valore di cont delay(1000); //attesa di 1 secondo, il valore di delay() è espresso in ms cont +=1; //incremento di cont di 1 ad ogni ciclo }

Listato 4size(480,120);smooth();background(0,0,0); // sfondo nerostrokeWeight(1);

//ciclo forfor (int i = 40; i<480; i +=40){ //variabile di conteggio i, che parte da 40 fino a 280, con incrementi di 40 per ogni ciclo fill(50+i/2, 90+i/10, 200); //riempimento, i colori variano in modo proporzionale con l’incremento di i ellipse (i, 60, 60, 60); //vengono disegnati cerchi, la cui posizione X è i }

Page 7: Processing - Homepage | DidatticaWEB

Elettronica In ~ Settembre 2011 153

Cors

o Pr

oces

sing

• void draw(){} che invece è la parte di codice eseguita continuamente.

Nel programma appena descritto (Listato 3) è stata utilizzata la funzione delay(1000); che permette di creare un ritardo di 1 secon-do; in pratica l’argomento della funzione è un numero che esprime il ritardo in ms.La scrittura cont +=1; signifi ca che la varia-bile cont viene incrementata di un’unità ad ogni ciclo.

LEZIONE 1_6 CONTROLLO DI FLUSSO_RIPETIZIONIQuesto sesto programma introduce l’uti-lizzo dei controlli di fl usso. Il codice (Li-stato 4) serve a disegnare una ripetizione di cerchi di colore diverso, come mostrato nella Fig. 7; questa funzionalità è ottenuta attraverso un ciclo FOR. Quest’ultimo è de-scritto dalla variabile che viene incrementa-ta ad ogni ripetizione, dalla condizione per cui il ciclo viene ripetuto (in pratica, fi nché la condizione è vera il ciclo viene ripetuto, mentre quando diventa falsa il programma esce dal ciclo) ed infi ne dall’incremento della variabile. Nel caso del programma in questione, la variabile che viene incrementata è i, defi ni-ta come intero for(int i ….La condizione per cui il ciclo è ripetuto è: “fi ntanto che i è minore di 480” for (int i; i< 480....Invece l’incremento della variabile i ad ogni ciclo è 40: for (int i; i< 480; i +=40).

LEZIONE 1_7 CONTROLLO DI FLUSSO_IFIl settimo programma della prima lezione permette di disegnare un quadrato con all’interno quattro quadranti; spostando il cursore del mouse sopra un quadrante, questo cambia di colore (Fig. 8).Lo scopo principale di questo programma è verifi care la posizione del cursore del mouse; se questa è entro i limiti defi niti di un quadrante viene cambiato il colore

dell’area. Defi nite le dimensioni dell’area di lavoro (con size(200,200)), la posizione X ed Y del mouse può essere ottenuta sempli-cemente con i comandi mouseX e mouseY, i quali ci restituiranno, nell’istante in cui sono richiamati, il valore X ed Y del cursore del mouse nell’area di disegno.La condizione di verifi ca del primo qua-drante è la seguente:

if ((mouseX > x1) && (mouseX < x2) && (mouseY > y1) && (mouseY < y3)){..

In pratica si tratta di una condizione con quattro AND logici (&&) che verifi ca se la posizione X del mouse è tra X1 e X2 e se la posizione Y del mouse è tra Y1 e Y2; se tutte e quattro le condizioni sono verifi cate, la condizione complessiva è vera e di conse-guenza viene colorata l’area (Listato 5).

LEZIONE 1_8 USO DEL TESTOQuesto programma aggiunge, rispetto al precedente, del testo per identifi care i quadranti (Fig. 9); il comando qui utilizza-to è: text(“testo da scrivere”, posizione X,

Fig. 7

Fig. 8 Fig. 9

Fig. 10

Page 8: Processing - Homepage | DidatticaWEB

154 Settembre 2011 ~ Elettronica In

Corso Processing

posizioneY). Il Listato 6 mostra l’utilizzo di tale comando nell’applicazione oggetto di questo esercizio.

LEZIONE 1_9 - ANIMAZIONERealizziamo ora una semplice animazione consistente in un’ellisse che oscilla in verti-cale tra due posizioni, come mostra la Fig. 10.Il programma si basa sul concetto fondamen-tale in Processing, secondo cui void draw(){} viene ripetuto continuamente; per la preci-sione, tale funzione è richiamata, di regola, 60 volte al secondo. Pertanto sfruttando questo principio possiamo realizzare delle animazioni. La tecnica base è quella di creare una fi gura in una certa posizione, quindi cancellarla nel frame successivo e ridisegnarla in una posizione differente dalla prima.L’oscillazione sull’asse verticale della po-sizione dell’ellisse è ottenuta mediante la formula:

fl oat y1 = offset + (sin(angolo))*scala;

La posizione è defi nita da y1, angolo è una variabile che viene incrementata ad ogni ci-clo, mentre la funzione sin() (funzione seno di un angolo espresso in radianti) restituisce un numero compreso tra -1 e 1. La variabile scala moltiplica sostanzialmente il valore della funzione sin(), mentre offset rappre-senta la posizione iniziale attorno alla quale avviene l’oscillazione.La variabile di angolo verrà incrementata indefi nitamente (perlomeno fi no alla sua to-tale occupazione del campo di memoria e poi ricomincerà), ma la funzione seno è periodi-ca di 2pi, quindi il numero che ne consegue dal continuo incremento di angolo sarà un multiplo di 2pi ed il risultato della funzione seno sarà sempre compreso tra -1 e 1.Variando le costanti iniziali, ossia angolo,

Fig. 11

Listato 5int x1 = 0; int y1 = 0;

int x2 = 100; int y2 = 0;

int x3 = 100; int y3 = 100;

int x4 = 0; int y4 =100;

int h = 100; int l = 100;

void setup(){ size(200,200); } void draw(){ //costruzione rettangolo QUADRANTE 1 if ((mouseX > x1) && (mouseX < x2) && (mouseY > y1) _ && (mouseY < y3)) { //condizione di verifica se il mouse è nel QUADRANTE 1 fill(0); } else { fill(255); } rect(x1,y1,l,h); //costruzione rettangolo QUADRANTE 2 if ((mouseX > x2) && (mouseX < width) && _ (mouseY > y2) && (mouseY < y4)) { //condizione di verifica se il mouse è nel QUADRANTE 2 fill(0); } else { fill(255); } rect(x2,y1,l,h);

//costruzione rettangolo QUADRANTE 3 if ((mouseX > x2) && (mouseX < width) && _ (mouseY > y3) && (mouseY < height)) { //condizione di verifica se il mouse è nel QUADRANTE 3 fill(0); } else { fill(255); } rect(x3,y3,l,h);

//costruzione rettangolo QUADRANTE 4 if ((mouseX > x4) && (mouseX < x3) && _ (mouseY > y4) && (mouseY < height)) { //condizione di verifica se il mouse è nel QUADRANTE 4 fill(0); } else { fill(255); } rect(x4,y4,l,h); }

Listato 6 ... text(“x1,y1”,15+x1,15+y1); text(“x2,y2”,15+x2,15+y2); text(“x3,y3”,15+x3,15+y3); text(“x4,y4”,15+x4,15+y4); ...

Page 9: Processing - Homepage | DidatticaWEB

Elettronica In ~ Settembre 2011 155

Cors

o Pr

oces

sing

offset, scala e velocità, si può sperimentare cosa accade. Il Listato 7 mostra il codice cor-rispondente a questo esempio.Bene, con gli esercizi abbiamo terminato. In questa prima puntata del corso su Processing abbiamo brevemente descritto come instal-lare questo linguaggio di programmazione e dei brevi programmi di esempio; nella prossima incominceremo a realizzare delle applicazioni con Arduino. Scriveremo quin-di dei semplici programmi che tratteranno l’interfacciamento di un software scritto in Processing con un piccolo sistema basato su Arduino. Fondamentalmente, realizzeremo su Processing un’interfaccia grafi ca e faremo accendere un LED su Arduino; l’applicazio-

ne sarà molto semplice, ma alla base ci sarà la costruzione di una struttura master/slave e di un protocollo di comunicazione tra i due sistemi.

EXTRA – PLASMA FADINGLa fi nalità di Processing è, come già det-to, costituire un linguaggio semplice per applicazioni di grafi ca e design; il codice plasma_fadig.pde allegato ai programmi di esempio di questo corso si riferisce proprio a ciò. Si tratta di un’applicazione in cui viene disegnato un raggio colorato che ruota a tutto schermo (Fig. 11).Siete invitati a variare i parametri del codice per sperimentare cosa accade in pratica.

Via Val Sillaro 38 • 00141 Roma • Tel: 06-8104753

Distributore autorizzato:

Il tuo punto

di riferimento

per l’elettronica

a ROMA

M Elett onica Forni ture per hobbist i , scuole e industr ia

M Elett M Elett M Elett onica

Listato 7float angolo = 0.0; //angolofloat offset = 200; //posizione iniziale dell’ellissefloat scala = 60; //ampiezza dell’oscillazionefloat velocita = 0.02; //velocità dell’oscillazione

void setup(){ size(560,400); smooth(); strokeWeight(1); background(0); ellipseMode(RADIUS);}

void draw(){

fill(0,0,0); //riempimento di colore nero rect(0,0,560,400); //disegno di un rettangolo di colore nero di dimensioni pari all’area di disegno

float y1 = offset + (sin(angolo))*scala; //calcolo della nuova posizione Y dell’ellisse fill(0,220,200); // colore di riempimento dell’ellisse

ellipse(280, y1, 30, 30); //disegno dell’ellisse

angolo += velocita; //incremento ad ogni ciclo della variabile angolo

}

Page 10: Processing - Homepage | DidatticaWEB

Elettronica In ~ Ottobre 2011 145

Introduzione a Processing

Cors

o Pr

oces

sing

rocessing e Arduino sono due realtà molto simili, infatti il loro editor (o

IDE) è praticamente lo stesso e la sintas-si da utilizzare per programmare cambia davvero poco tra i due. L’unica differenza significativa è che il loro campo di appli-cazione è differente, in quanto Proces-sing serve per scrivere software da PC, mentre l’IDE di Arduino ci consente di

programmare il microcontrollore che si trova a bordo proprio dei moduli Ardui-no. Qualunque sia il punto di partenza, è comunque molto semplice passare da un ambiente all’altro: da Arduino a Proces-sing o viceversa. Questo corso riguarda Processing, quindi rimandiamo chi voles-se approfondire la conoscenza di Arduino all’apposito corso; tuttavia in questa lezio-

Prosegue il nostro corso su Processing: in questa puntata vedremo come costruire un’interfaccia grafica e in che modo collegarla con Arduino. Seconda puntata.

di MATTEO FIORAVANTI

P

Page 11: Processing - Homepage | DidatticaWEB

146 Ottobre 2011 ~ Elettronica In

Corso Processing

ne coinvolgeremo la piattaforma Arduino perché intendiamo utilizzarla per testare i programmi d’esempio relativi agli esercizi che vi proponiamo.Per questa ragione riportiamo in queste pagine anche il codice per Arduino ed il circuito di test corrispondente.

COMUNICAZIONE SERIALEL’anello di congiunzione tra un programma scritto in Processing ed uno che gira su Ar-duino è la comunicazione seriale, la quale fi sicamente è realizzata attraverso il cavo USB tramite cui Arduino è collegato al PC.A livello logico, nella comunicazione seriale vengono scambiati byte; ricordiamo che un byte è un numero binario di 8 bit, perciò la rappresentazione di un byte può essere un numero intero che va da 0 a 255 oppure la rappresentazione di caratteri che si trovano nella tabella ASCII. Che sia un numero, oppure un carattere, tutto dipende da come interpretiamo i dati; la sostanza comunque non cambia, perché si tratta sempre di un pacchetto di 8 bit che viene scambiato. Concettualmente una comuni-cazione è defi nita quando conosciamo il supporto fi sico (nel nostro caso è il colle-gamento USB), il livello logico (cioè come sono interpretati i bit) ed infi ne, quando è stabilito il protocollo di comunicazione. Nel caso di questo corso, saremo noi a defi nire il protocollo, cioè attribuiremo a determinati simboli e numeri delle funzionalità, sia verso Processing che verso Arduino.Una comunicazione deve essere opportu-namente gestita nelle sue fasi; solitamente viene defi nito “master” il sistema che invia comandi con una certa cadenza temporale oppure a seguito di determinati eventi, mentre è chiamato “slave” il sistema che risponde alle interrogazioni del master. In questa lezione stabiliremo che il software da PC scritto in Processing sarà il master della comunicazione, mentre Arduino sarà lo slave. Possiamo impostare tutto il sistema anche con Arduino che funziona da master,

ma lasceremo ciò come esercizio. A questo livello non ci sono particolari differenze, né vantaggi ad utilizzare una soluzione anzi-ché l’altra.Richiamati questi brevi concetti base, an-dremo adesso ad analizzare i codici oggetto di questa puntata del corso.

IL CODICEI programmi di esempio che vogliamo pro-porvi stavolta sono i seguenti.1. LEZIONE 2_1 BUTTON – Questo

programma mostra come disegnare un pulsante, che si evidenzia quando il puntatore è sopra di esso e si accende se viene fatto clic.

2. LEZIONE 2_2 BUTTON & LED – Rispet-to al programma precedente, aggiunge un LED che lampeggia in maniera inter-mittente ogni secondo.

3. LEZIONE 2_3 BUTTON & LED + TRA-SMISSIONE + ARDUINO – A questo punto utilizziamo Arduino; l’interfaccia è quella del programma precedente, ma ad ogni secondo viene trasmesso un comando ad Arduino, che accenderà o meno un LED secondo lo stato del pul-sante grafi co.

4. LEZIONE 2_4 BUTTON & LED + TRA-SMISSIONE/RICEZIONE + ARDUI-NO – Aggiungiamo ora la possibilità di leggere lo stato di ingresso di Arduino; in pratica ad ogni secondo viene invia-to, da parte di Processing, lo stato del pulsante grafi co ed Arduino risponde comunicando lo stato del suo pulsante reale. Processing accenderà un ulteriore LED sull’interfaccia grafi ca.

LEZIONE 2_1 BUTTON Iniziamo ora a scrivere il nostro primo pro-gramma di questa lezione, cioè l’implemen-tazione di un pulsante che si evidenzia se il mouse ci passa sopra e si illumina se viene fatto clic sopra di esso, quindi si spegne se facciamo nuovamente clic.La strategia è relativamente semplice:

Fig. 1

Fig. 2

Page 12: Processing - Homepage | DidatticaWEB

Elettronica In ~ Ottobre 2011 147

Cors

o Pr

oces

sing

innanzitutto un pulsan-te può essere rappresen-tato come un quadrato riempito di colore nero, perciò evidenziare il rettangolo signifi ca ridisegnare il quadra-to con il bordo giallo, illuminare il pulsante equivale a ridisegna-re il quadrato con il riempimento diverso da nero (magari un azzurro molto luminoso) come rappresentato in Fig. 1. Abbiamo quindi la necessità di gestire le primitive grafi che rect(), fi ll(), stroke(); inoltre ci serve una funzione che chiameremo overRect(), la quale ci restituirà un valore logico TRUE se il puntatore del mouse sarà sopra il rettangolo identifi cato come pul-sante, secondo quanto schematizzato in Fig. 2.Infi ne ci occorre una piccola astuzia: defi -niamo innanzitutto la variabile button come intero; questa variabile sarà rappresentativa dello stato del pulsante. Poi scriveremo una fun-zione di riempimento in questo modo:

fi ll(60*button,130*button,240*button).

Quando button vale 0, avremo questa fun-zione di riempimento: fi ll(0,0,0), che signifi ca riempimento di colore nero. Se button = 1, al-lora si ha fi ll(60,130,240), che in questo caso corri-sponde al riempimento con un colore RGB

Listato 1int button; //definizione della variabile button come intero

void setup(){ button = 0; //inizializzazione di button

//definizione delle primitive grafiche color(RGB); //tipo di colore strokeWeight(1); //spessore linee strokeJoin(SQUARE); //tipo di spigolo: quadrato

fill(30); //grigio di sfondo smooth(); size(300,160); //area dell’applicazione

fill(220,220,220); //grigio dell’area pulsante rect(10,10,280,140); //area pulsante

textSize(30); //dimensione del testo fill(0); }

void draw(){ fill(60*button,130*button,240*button); //il colore di riempimento del pul-sante dipende dalla variabile button, ovvero dallo stato di button, //se button=0 ---> fill(0,0,0) quindi colore nero //Se button=1 ---> fill(60,130,240) quindi colore blu rect(20,40,40,40); //rettangolo che rappresenta il pulsante text(“button”, 20, 120); //etichetta

strokeWeight(3); //spessore del bordo if (overRect(20,40,40,40)) //la funzione overRect restituisce TRUE se il mouse è sopra il rettangolo che rappresenta il pulsante, altrimenti FALSE

stroke(241, 250, 18); //definizione di una linea di colore giallo rect(20,40,40,40); //disegno di un rettangolo di colore giallo in corrispondenza del pulsante, questa tecnica serve ad evidenziare il pulsante } else { stroke(0, 0, 0); //se il mouse non si trova sopra il pulsante, il bordo del pulsante è di colore nero rect(20,40,40,40); }

//cambio dello stato del pulsante se tasto mouse premuto if ((overRect(20,40,40,40)) && mousePressed) //se contemporaneamente il mouse è sopra l’area del pulsante ed il tasto sinistro è premuto viene cambiato il valore di button in 0 se era 1, oppure in 1 se precedentemente era 0 { delay(300); //questo ciclo di ritardo funziona da debounce del tasto del mouse if (button == 0) { button = 1; } else { button = 0; } }

boolean overRect(int x, int y, int w, int h) //funzione overRect, vengono passati i parametri di posizione X e Y, e di larghezza W e altezza H del pul-sante { if (mouseX >= x && mouseX <= x+w && //overRect assume valore TRUE se il cursore del mouse è dentro il rettangolo posizionato in X,Y e di dimensioni W,H mouseY >= y && mouseY <= y+h) { return true; } else { return false; }

Page 13: Processing - Homepage | DidatticaWEB

148 Ottobre 2011 ~ Elettronica In

Corso Processing

defi nito, nello specifi co, dai valori 60, 130, 240 delle componenti cromatiche, rispetti-vamente, R, G, B.Lo stesso principio descritto per fi ll() vale anche per stroke(), cioè per il bordo del rettangolo. Quindi, attraverso la funzione overRect() identifi chiamo se il puntatore è sopra il pulsante; se, contemporaneamente, il tasto del mouse è premuto, attraverso la

primitiva di Proces-sing chiamata mouse-Pressed cambiamo il valore di button da 0 a 1 e con esso il colo-re di riempimento. Il codice corrisponden-te all’applicazione qui descritta è con-tenuto nel Listato 1. Analizziamo adesso, più approfondita-mente ,la funzione overRect(). Diciamo innanzitutto che le funzioni si scrivono in fondo al program-ma principale; in

pratica fuori da void draw(){}. La funzione in questione è defi nita in questo modo:

boolean overRect(int x, int y, int w, int h){}

Ciò signifi ca che overRect restituisce un valore logico che può essere TRUE o FALSE e si aspetta, come parametri di ingresso, quattro valori interi x, y, w, e h.In sostanza, quando all’interno del pro-gramma richiamiamo questa funzione, dob-biamo passare la posizione x, y del rettan-golo e le sue dimensioni w, h; la funzione restituirà TRUE o FALSE.La verifi ca della posizione del puntatore del mouse, all’interno di overRect, viene svolta in questo modo:Fig. 3

Fig. 4

Fig. 5 Fig. 6 Fig. 7

Listato 2int button; //definizione della variabile button come interoint LED; //definizione della variabile LED come interofloat lastUpdate;

void setup(){ button = 0; //inizializzazione di button LED = 0; //inizializzazione di LED lastUpdate =0; //inizializzazione di lastUpdate //definizione delle primitive grafiche color(RGB); //tipo di colore strokeWeight(1); //spessore linee strokeJoin(SQUARE); //tipo di spigolo: quadrato

fill(30); //grigio di sfondo smooth(); size(300,360); //area dell’applicazione

fill(220,220,220); //grigio dell’area pulsante rect(10,10,280,140); //area pulsante rect(10,160,280,140); //area LED

textSize(30); //dimensione del testo fill(0);}

Page 14: Processing - Homepage | DidatticaWEB

Elettronica In ~ Ottobre 2011 149

Cors

o Pr

oces

sing

if (mouseX >= x && mouseX <= x+w && mouseY >= y && mouseY <= y+h) { return true;}else{ return false;}

Viene, cioè, verifi cata una condizione con quattro AND logiche; il valore X del punta-tore deve essere maggiore della posizione x del rettangolo e minore della posizione x+w (larghezza del rettangolo), mentre il valore Y del puntatore deve essere maggiore della posizione y del rettangolo e minore di y+h (altezza del rettangolo). Il risultato fi nale di questa prima interfaccia grafi ca è rappresen-tato nella Fig. 3, dove il pulsante è spento, in Fig. 4, che mostra il pulsante evidenziato e nella Fig. 5, dove il pulsante è acceso.

LEZIONE 2_2 BUTTON & LED

Aggiungiamo al programma precedente il comando di un LED (Listato 2). Anche in questo caso rappresentiamo il LED con un rettangolo colorato di nero se il LED è spen-to, ovvero di giallo se il LED è acceso.Innanzitutto dobbiamo cambiare l’inter-faccia grafi ca del programma precedente, estendendo l’area del programma ed ag-giungendo un nuovo rettangolo, con sotto l’etichetta ‘TX’; il risultato si vede nella Fig. 6.Oltre a dover ridisegnare l’area dell’inter-faccia, abbiamo bisogno di una nuova varia-bile che chiameremo LED; inoltre ci servirà ancora un’altra variabile che chiameremo lastUpdate e della quale analizzeremo tra poco la funzionalità. Anche per il LED useremo la stessa tecnica sviluppata per il pulsante, ovvero agiremo sulla funzione di riempimento fi ll() e la renderemo dipen-dente dalla variabile LED:

Fig. 8

Listato 3void draw(){…

//disegno del Led strokeWeight(3); stroke(0, 0, 0); fill(241*LED,250*LED,18*LED); //il colore di riempimento del Led dipende dalla variabile LED, ovvero dal dal suo stato //se LED=0 ---> fill(0,0,0) quindi colore nero //se LED=1 ---> fill(241,250, 18) quindi colore giallo rect(20,200,40,40); //rettangolo che rappresenta il Led text(“TX”, 20, 280); //etichetta

}

Page 15: Processing - Homepage | DidatticaWEB

150 Ottobre 2011 ~ Elettronica In

Corso Processing

fi ll(241*LED, 250*LED, 18*LED); se LED = 0, allora avremo fi ll(0,0,0) cioè riempimento nero, mentre se in-vece LED = 1 avre-mo fi ll(241,250,18) che rappresenta un giallo acceso. Il tutto è mostrato nel codice conte-

nuto nel Listato 3.Supponiamo ora di voler far lampeggiare il LED in maniera intermittente ogni secondo.La prima cosa che ci viene in mente potreb-be essere inserire un banale ciclo di ritardo all’interno della funzione principale void draw(){}, ma questa tecnica avrebbe un grave effetto collaterale: il pulsante non risponderebbe più durante il ciclo di ritar-do, perché il programma sarebbe bloccato dentro questo ciclo e non potrebbe gestire le funzionalità del pulsante.Il nostro obiettivo è, dunque, far lampeg-giare il LED, mantenendo contemporanea-mente tutte le funzionalità del pulsante.Una tecnica relativamente semplice per fare ciò consiste nello sfruttare la funzione millis() di Processing, soprattutto perché essa funzione restituisce il tempo, espresso in millisecondi, trascorso dall’istante in cui l’applicazione è stata avviata.

Quindi millis() ci restituisce un valore che cresce continuamente. Siccome vogliamo far accadere qualcosa (un evento) ogni secondo (1.000 ms), basterà salvare il valore di millis() in una variabile temporanea e confrontare continuamente la differenza tra il valore assoluto di millis() ed il valore precedentemente salvato. Se questa differenza è maggiore di 1.000, allora verrà scatenata la relativa azione e sarà nuo-vamente salvato il valore di millis() nella variabile temporanea.Il ciclo è rappresentato nella Fig. 8. La funzione millis() cresce indefi nitamente, mentre invece la variabile temporanea lastUpdate è inizializzata a 0 ed ogni volta che la differenza tra millis() ed essa è mag-giore di 1.000, lastUpdate viene aggiornata all’attuale valore di millis(). Inoltre viene avviata l’azione prevista.La porzione di codice che svolge quanto descritto è quella contenuta nel Listato 4.La Fig. 9 e la Fig. 7 mostrano come appare il programma defi nitivo, nel quale il ciclo di lampeggio del LED non interferisce con la funzionalità del pulsante.

LEZIONE 2_3 BUTTON & LED + TRA-SMISSIONE + ARDUINOQuesto terzo esempio trasforma il program-ma appena descritto in una vera interfaccia grafi ca capace di gestire delle funzionalità su Arduino. In pratica vengono aggiunte al

Fig. 9

Listato 4void draw(){…

//ciclo di lampeggio del Led//la funzione millis() restituisce il tempo trascorso dall’avvio del programma, in millisecondi//se la differenza tra millis() e lastUpdate è > 1000, è trascorso 1 secondo e viene cambiato lo stato di LED

if ((millis() - lastUpdate) > 1000){ println(“Trasmissione”); if (LED ==1){ LED=0; } else{ LED=1; } lastUpdate = millis(); //salvataggio nella variabile lastUpdate del valore di millis() }

}

Page 16: Processing - Homepage | DidatticaWEB

Elettronica In ~ Ottobre 2011 151

Cors

o Pr

oces

sing

programma della lezione 2_2 le primitive di Processing utili per la comunicazione seriale. Allo stesso tempo dovremo anche analizzare il relativo codice di Arduino ed il circuito applicativo.Per accedere alle funzionalità della comuni-cazione seriale, dobbiamo far ricorso all’ap-posita libreria di Processing. Il nostro terzo programma di esempio comincia infatti in questo modo:

import processing.serial.*.

Ciò signifi ca che inglobiamo nel program-ma la relativa libreria di comunicazione seriale; dal momento che Processing già dispone di questa libreria non dovremo installare nulla, ma semplicemente richia-marla prima di tutto nel programma.Un’altra operazione fondamentale è nomi-nare la porta seriale:

Serial myPort;

Serial è una classe e myPort è la defi nizio-

ne di un oggetto appartenente alla classe Serial, quindi la nostra porta di comuni-cazione seriale si chiamerà myPort e nel programma si farà riferimento ad essa.Ancora, però, la porta seriale non è funzio-nante, perché è stata defi nita unicamente a livello astratto ma non a livello fi sico; dobbiamo infatti specifi care a quale COM è associata ed a che velocità comunica. Queste defi nizioni sono fatte dentro void setup(){}. Il Listato 5 mostra come appaiono nel programma.La riga di programma seguente: myPort = new Serial(this, “COM9”, 9600);

signifi ca che myPort (la nostra porta seriale) appartiene alla classe Serial, si chiama ‘COM9’ e trasmette a 9.600 baud.Il nome della porta COM che andremo a scrivere (“COM9”, “COM6”...) deve essere lo stesso della porta usata da Arduino; ov-viamente la velocità di comunicazione deve essere la stessa sia per Arduino che per Processing (nel nostro caso appunto 9600);

Listato 5import processing.serial.*; //libreria serialeSerial myPort; //oggetto myPort costruito dalla classe Serial

int button; //definizione della variabile button come interoint LED; //definizione della variabile LED come interofloat lastUpdate;

void setup(){ myPort = new Serial(this, “COM9”, 9600); //configurazione della porta seriale, in questo caso è usata COM9, deve essere la stessa di Arduino

}

Listato 6//ciclo di lampeggio del Led if ((millis() - lastUpdate) > 1000){ println(“Trasmissione”); if (LED ==1){ LED=0; } else{ LED=1; } if (button ==1){ //se lo stato di button è 1 myPort.write(‘H’); //trasmissione sulla seriale del carattere ‘H’ } else{ myPort.write(‘L’); //se button non è 1, sulla seriale è trasmesso il carattere ‘L’ } lastUpdate = millis(); //salvataggio nella variabile lastUpdate del valore di millis() }

Page 17: Processing - Homepage | DidatticaWEB

152 Ottobre 2011 ~ Elettronica In

Corso Processing

se il nome della porta e la velocità non sono rispettati, il sistema non può funzionare.Defi niamo ora un semplice protocollo: stabiliamo che se il pulsante è acceso, viene trasmesso verso Arduino il carattere ASCII ‘H’, altrimenti se il pulsante è spento trasmettiamo il carattere ‘L’. Il master della trasmissione è il programma scritto in Pro-cessing e la trasmissione del comando deve avvenire ogni secondo.In pratica la struttura è già fatta: infatti sfrutteremo il ciclo di lampeggio del LED descritto nel programma precedente ed aggiungeremo la comunicazione seriale del carattere ‘H’ oppure ‘L’; questa comunica-

zione ovviamente avverrà ogni secondo.Il codice modifi cato è quello che appare nel Listato 6.Dentro il ciclo di lampeggio è stata aggiun-ta una condizione di verifi ca del valore di button ed a seconda di questo viene scritto in myPort il carattere ‘H’ oppure ‘L’, sfrut-tando la funzione myPort.write().Ora che il programma master è comple-to dobbiamo impostare il sistema slave con Arduino. Innanzitutto realizziamo il circuito di Fig. 10 il cui schema elettrico è riportato in Fig. 11; si tratta di collegare due LED con le rispettive resistenze serie ed un pulsante con la sua resistenza di pull-up, ai

Listato 7char val; //dato ricevuto dalla porta serialeint i;

int LEDPin[6] = { 5,6,10,11,12,13}; //setup dei pin di uscita int inPin[6] = { 2,3,4,7,8,9}; //setup dei pin usati come ingresso

void setup() { for (i = 0; i < 6; i++) { // configurazione dei pin di output pinMode(LEDPin[i], OUTPUT); digitalWrite(LEDPin[i], LOW); // inizializzo l’uscita a 0 }

for (i = 0; i < 6; i++) { //configurazione dei pin di input pinMode(inPin[i], INPUT); } Serial.begin(9600); // attivazione della seriale}

void loop() {

//stato di lettura e scrittura della seriale

if (Serial.available()) { // se è disponibile un dato sul buffer seriale digitalWrite(LEDPin[0], HIGH); //accendi il LED 0, identificativo dello stato della comunicazione val = Serial.read(); //il valore del buffer è letto e salvato su val, una volta letto il buffer è svuotato if(digitalRead(inPin[0]) == HIGH){ //lettura dello sato del pin usato come ingresso, se lo stato è alto Serial.write(1); //trasmissione sulla seriale del valore 1 }else{ // altrimenti Serial.write(2); //trasmissione del valore 2 } } //stato di decodifica del valore seriale ricevuto if (val == ‘H’) { // se il valore di val è il carattere ‘H’ digitalWrite(LEDPin[1], HIGH); // viene acceso il LED 1 } else { digitalWrite(LEDPin[1], LOW); // altrimenti LED 1 viene spento } delay(100); // ciclo di attesa di 100 ms

}

Carletto
Text Box
Serial.println('1');
Carletto
Text Box
Serial.println('2');
Page 18: Processing - Homepage | DidatticaWEB

Elettronica In ~ Ottobre 2011 153

Cors

o Pr

oces

sing

pin digitali di Arduino che saranno oppor-tunamente confi gurati come uscite per i LED e ingresso per il pulsante.I due LED di Arduino saranno chiamati LED_0 e LED_1; il primo servirà per mostra-re che la comunicazione seriale è stabilita, quindi sarà acceso se comunque un carat-tere è ricevuto, mentre LED_1 si accenderà se il carattere ricevuto è ‘H’ e si spegnerà in corrispondenza del carattere ‘L’. Il funzio-namento è rappresentato dalle Fig. 12 e Fig. 13. Riportiamo, nel Listato 7, il codice di Arduino.Per quanto possibile, proviamo a spiegare in questa sede il programma per Arduino:il codice è strutturato in una prima fase in cui sono defi niti e confi gurati i pin di ingresso/uscita e la porta seriale di Arduino, poi il programma entra nel loop principale. Nel loop viene continuamente verifi cato se ci sono dati nel buffer seriale: in caso affer-mativo, viene acceso LED_0, quindi letto e salvato il dato del buffer; poi, a seconda dello stato del pulsante, viene trasmesso tramite la porta seriale un valore(1 o 2) verso il PC. Il valore letto dal buffer seriale viene decodifi cato; se è ‘H’ viene acceso LED_1, altrimenti lo stesso viene tenuto spento.Il fl ow-chart del programma è rappresen-tato nella Fig. 14. Il programma descritto ha una funzionalità in più rispetto a quella necessaria per il codice di Processing qui descritto, in quanto il programma di Ardu-ino trasmette anche lo stato del pulsante. Questa funzionalità sarà usata dal program-ma dell’esercizio seguente.

LEZIONE 2_4 BUTTON & LED + TRA-SMISSIONE/RICEZIONE + ARDUINOIn quest’ultimo programma aggiungiamo

Fig. 10

Fig. 12

Fig. 13

Fig. 14

Fig. 11

Page 19: Processing - Homepage | DidatticaWEB

154 Ottobre 2011 ~ Elettronica In

Corso Processing

Fig. 15 Fig. 16

sull’interfaccia grafi ca un ulteriore LED che rappresenta lo stato del pulsante di Ardui-no. Ora che sappiamo come disegnare un LED ed accenderlo, rimane solo da defi nire a livello di programma la lettura della porta seriale da parte di Processing.Innanzitutto defi niamo un nuovo LED che chiameremo LEDConn; questo, ovviamente, deve essere anche disegnato, perciò proce-deremo sempre come per il LED precedente: si tratterà di un rettangolo in cui cambiere-mo il colore di riempimento a seconda del valore di LEDConn. Nel ciclo di trasmis-sione del master verso Arduino, inseriamo anche la lettura del buffer seriale del PC. Allo scopo, la prima funzione che richia-miamo è myPort.availabe(), la quale restitu-isce TRUE se c’è qualcosa nel buffer, ovvero FALSE se non c’è nulla. Una volta verifi cato che il buffer seriale è pieno, questo viene letto ed il valore me-morizzato in una variabile temporanea val

= myPort.read(); dopo aver letto questo va-lore, il buffer è ripulito attraverso myPort.clear(). Verifi cato che c’è un valore, questo viene letto, poi il buffer è ripulito. Rimane soltanto da decodifi care il valore di val: se è 1, LEDConn sarà posto ad 1, altrimenti LE-DConn sarà posto a 0. La porzione di codice da aggiungere al programma precedente è illustrata nel Listato 8.Il funzionamento del sistema completo è rappresentato in Fig. 15 e Fig. 16.

NELLE PROSSIME LEZIONI...Bene, per il momento abbiamo concluso. Nella prossima puntata, seguendo la stessa linea seguita fi n qui, descriveremo come avviene la lettura di un valore analogico e spiegheremo il funzionamento della libreria fi rmdata, importante perché ci consentirà di controllare direttamente Arduino da Pro-cessing, senza passare per la defi nizione di alcun protocollo.

Listato 8//disegno del LedConn strokeWeight(3); stroke(0, 0, 0); fill(247*LEDConn,5*LEDConn,5*LEDConn); //il colore di riempimento del Led dipende dalla variabile LEDConn, ovvero dal dal suo stato //se LEDConn=0 ---> fill(0,0,0) quindi colore nero //se LEDConn=1 ---> fill(247,5, 5) quindi colore rosso rect(20,350,40,40); //rettangolo che rappresenta il Led text(“RX”, 20, 430); //etichetta if (myPort.available()>0) { // se è presente un dato sul buffer seriale, val = myPort.read(); //il dato viene letto e memorizzato su val myPort.clear(); //il buffer è ripulito if (val == 1){ //se val = 1 LEDConn = 1; //cambia il valore di LEDConn in 1 } else{ //altrimenti LEDConn = 0; //il valore di LEDConn è 0 } }

Page 20: Processing - Homepage | DidatticaWEB

Elettronica In ~ Novembre 2011 147

Introduzione a Processing

Cors

o Pr

oces

sing

bbiamo conosciuto Processing parten-do dalle basi, scoprendo che cos’è e a

quali ambiti si applica; dopo le prime due puntate, la prima delle quali propedeutica all’apprendimento dell’uso di questo am-biente di sviluppo per programmi da PC e la seconda dedicata all’interazione con Arduino ed allo sviluppo di programmi di test utilizzabili su quest’ultimo, siamo

giunti alla terza lezione. Qui affronteremo tre temi fondamentali, ossia: 1) la rappresentazione di un valore analo-

gico, letto sempre attraverso Arduino;2) la costruzione di interfacce grafiche

usando delle immagini e delle piccole animazioni;

3) l’utilizzo della libreria “arduino” di Processing, che ci consentirà di control-

Scopriamo come rappresentare un valore analogico acquisito con Arduino, realizzare interfacce grafiche e impariamo ad utilizzare la libreria “arduino”.Terza puntata.

di MATTEO FIORAVANTI

A

Page 21: Processing - Homepage | DidatticaWEB

148 Novembre 2011 ~ Elettronica In

Corso Processing

lare direttamente i pin e le funzionalità di Arduino, senza passare per la defini-zione di un protocollo.

I programmi di esempio che vedremo in questa terza puntata del nostro corso sono elencati qui di seguito.1. LEZIONE 3_1 PULSANTE + LED +

INDICATORE ANALOGICO – Que-sto codice è la naturale prosecuzione della lezione della seconda puntata; in essa, all’interfaccia con un pulsante ed un LED viene aggiunta una barra che rappresenta il valore analogico letto da Arduino. Il codice che deve essere cari-cato su Arduino è:

arduino_codice_TXRX_ANALOG.pde.

2. LEZIONE 3_2 PULSANTE GRAFI-CO – Qui impariamo a costruire delle interfacce grafiche usando delle imma-gini; la tecnica adottata è molto simile a quella descritta nelle scorse lezioni, ma la differenza sostanziale è che non disegneremo da codice i pulsanti, i LED e gli indicatori, ma sfrutteremo delle immagini precedentemente costruite. Questo codice mostra come costruire un interruttore, che si evidenzia se ci portiamo sopra il puntatore del mouse e che commuta se vi facciamo clic.

3. LEZIONE 3_3 INDICATORE ANALO-GICO GRAFICO – Questo codice serve a costruire un indicatore, simile ad un manometro, in cui c’è un’immagine di sfondo che rappresenta una scala gra-duata ed una lancetta che ruota. Tutto è realizzato mediante immagini prece-dentemente costruite e la lancetta ruota in funzione del valore che vogliamo visualizzare.

4. LEZIONE 3_4 MANOPOLA – In questo programma combiniamo le due tecniche delle lezioni precedenti per realizzare una manopola, che si evidenzia quando il mouse la punta e che se vi facciamo clic e ruotiamo il mouse, essa ruota con-seguentemente.

5. LEZIONE 3_5 INTERFACCIA COM-PLETA – In questo codice mettiamo insieme tutto quello che abbiamo ap-

preso finora realizzando un’interfaccia completa di: interruttore, LED, mano-pola, indicatore analogico. L’interfaccia controllerà direttamente Arduino, nel quale dovrà essere caricato il codice:

arduino_codice_TXRX_ANALOG_IN_OUT.pde.

6. LEZIONE 3_6 INTERFACCIA COM-PLETA + FIRMDATA – Utilizzando la stessa interfaccia grafica del codice pre-cedente, andiamo ad usare la libreria arduino di Processing e carichiamo su Arduino il codice firmdata. Il program-ma mostra come controllare direttamen-te Arduino da Processing.

LEZIONE 3_1Questo programma è la diretta evoluzione del codice spiegato nella seconda puntata nella LEZIONE 2_4. Richiamiamo breve-mente cosa accadeva in quel programma: Arduino comunicava verso il programma master lo stato di un suo pulsante e faceva questo inviando un byte seriale in cui c’era il valore 1 o 0; nel caso fosse trasmes-so il valore 1 il codice di Processing accen-deva il secondo LED dell’interfaccia. Partendo da questa architettura, modifi-chiamo il codice e facciamo trasmettere ad Arduino non lo stato del pulsante, ma il valore analogico letto su ANALOG IN 0; dal lato Processing modifichiamo il codice in modo da decodificare il valore analo-gico ricevuto e rappresentarlo attraverso una barra. Il codice Arduino da utilizzare in questa lezione è:

arduino_codice_TXRX_ANALOG.pde,

un file che potete scaricare dal nostro sito www.elettronicain.it insieme a tutti i co-dici di esempio descritti. Ci soffermiamo, quindi, unicamente sulle parti di codice che ci interessano. Innanzitutto definia-mo l’ingresso analogico 0, chiamandolo inAnalogico. Nel ciclo di risposta alle interrogazioni del master, trasmettiamo il valore analo-gico letto sull’ingresso 0, quindi leggiamo il valore attraverso la funzione analogRead(), e lo dividiamo per 4, in quanto Arduino

Page 22: Processing - Homepage | DidatticaWEB

Elettronica In ~ Novembre 2011 149

Cors

o Pr

oces

sing

utilizzeremo in questa lezione è rappre-sentato in Fig. 1; il relativo schema elettri-co si trova nella Fig. 2. Dal punto di vista di Processing, il codice non differisce di molto da quello della LEZIONE 2_4; in-fatti le modifiche sono minime. Quando è presente un dato nel buffer seriale, questo è letto e caricato su val:

val = myPort.read();

La barra analogica è costruita mediante un rettangolo, di colore rosso, la cui dimen-sione “x” varia in funzione del valore di val:

rect(20,350,val,40);

La porzione di codice di Processing è riportata nel Listato 2; le Fig. 3 e Fig. 4, ci mostrano come si presenta l’interfaccia completa.Occorre notare una cosa molto importante,

Fig. 1

Fig. 1

dispone di convertitori analogico digitali a 10 bit e pertanto un’escursione della tensione d’ingresso tra 0 e 5 V corrisponde a una rappresentazione numerica intera 0–1024 (210 = 1024). Siccome un byte è com-posto da 8 bit, il valore numerico massimo che possiamo trasmettere sulla seriale è 256 (28 = 256) ecco che dobbiamo compri-mere la scala dividendo tutto per quattro.

anVal = analogRead(inAnalogico) / 4;

Il valore letto e scalato viene quindi invia-to via seriale attraverso l’istruzione:

Serial.write(anVal);

Qualora avessimo voluto trasmettere il valore non scalato, ma rappresentativo di tutti e 10 i bit di conversione, avremmo dovuto spezzarlo trasmettendolo con due byte. Riportiamo nel Listato 1 le parti di codice di Arduino, mentre il circuito che

Listato 1int inAnalogico = 0;…

void loop() {

//stato di lettura e scrittura della seriale

if (Serial.available()) { // se è disponibile un dato sul buffer seriale digitalWrite(LEDPin[0], HIGH); //accendi il LED 0, identificativo dello stato della comunicazione val = Serial.read(); //il valore del buffer è letto e salvato su val, una volta letto il buffer è svuotato anVal = analogRead(inAnalogico) / 4; // lettura dell’ingresso analogico e divisione per 4, (1024/4 = 256) Serial.write(anVal); // trasmissione del valore via seriale }

Carletto
Text Box
Serial.println(anVal);
Carletto
Text Box
Serial.println(anVal);
Page 23: Processing - Homepage | DidatticaWEB

150 Novembre 2011 ~ Elettronica In

Corso Processing

ossia che una volta letto, val subisce una trasformazione attraverso la funzione map(value,low1,high1,low2,high2):

val = map(val, 0, 255, 0, 260);

in pratica map() trasforma il valore va-lue (che deve essere compreso tra i valori low1 e high1) in un corrispondente valore proporzionato che sarà compreso tra i va-lori low2 e high2, come ci mostra la Fig. 5. Questa funzione è molto importante e sarà ampiamente usata nelle successive lezioni; in questo caso val, che è per sua natura compreso tra 0 e 255, viene impo-stato tra 0 e 260. La motivazione di ciò è unicamente di tipo grafico, in quanto abbiamo definito la larghezza della barra analogica 260 pi-xel, mentre se avessimo definito una barra più larga (magari 500 pixel) avremmo dovuto impostare la funzione nel modo seguente:

val = map(val, 0, 255, 0, 500);

così facendo quando val = 0, la barra è tut-ta a 0, mentre quando val = 255, la barra

occuperà tutti i 500 pixel.Il funzionamento del sistema è riassunto in Fig. 6 e Fig. 7, il pulsante dell’interfac-cia controlla il LED1 di Arduino, mentre LED0 si accende in ogni caso se è presente qualcosa nel buffer seriale. Il trimmer funziona da partitore di tensione ed il va-lore letto dall’ingresso analogico 0 di Ar-duino viene ritrasmesso via seriale verso l’interfaccia e qui rappresentato mediante la barra.

LEZIONE 3_2Abbandoniamo per un po’ l’interfaccia-mento e la comunicazione con Arduino, per concentrarci sulla costruzione di in-terfacce grafiche. L’obiettivo è realizzare pulsanti, LED, indicatori, attraverso delle immagini.Nel mese scorso abbiamo costruito delle interfacce sfruttando le primitive grafiche di Processing, che sono essenzialmente quadrati, rettangoli, colori e riempimenti.Ora realizzeremo delle interfacce combi-nando quanto abbiamo finora appreso, con la possibilità di caricare immagini e sovrapporle.Il codice di questa lezione si riferisce alla

Listato 2… if (myPort.available()>0) { // se è presente un dato sul buffer seriale, val = myPort.read(); //il dato viene letto e memorizzato su val myPort.clear(); //il buffer è ripulito val = map(val, 0, 255, 0, 260); //il valore di ingresso varia 0-->255, la barra ha una dimensione 0-->260, //la funzione map svolge automaticamente la proporzione }

fill(0,0,0); //colore di sfondo della barra, nero rect(20,350,260,40); //sfondo della barra fill(240,0,0); //colore di riempimento della barra, rosso rect(20,350,val,40); //la barra analogica è un rettangolo la cui larghezza è proporzionale al valore analo-gico rappresentato text(“Analog”, 20, 430); //etichetta

Listato 3…void draw(){ if (button == 0){ //se il valore di button = 0 viene visualizzata l’immagine del pulsante off image(imgBt1Off, 20,30); //visualizzazione del pulsante off } else{ //altrimenti image(imgBt1On, 20,30); //viene visualizzata, nella stessa posizione, l’immagine del pulsaten on } text(“button”, 180, 130); //etichetta…

Page 24: Processing - Homepage | DidatticaWEB

Elettronica In ~ Novembre 2011 151

Cors

o Pr

oces

sing

costruzione di un pulsante e la Fig. 8 ci mostra come si presenta l’interfaccia; in essa c’è un interruttore con relativo LED. Quando il puntatore del mouse passa so-pra l’interruttore, l’area corrispondente si evidenzia e quando facciamo clic l’inter-ruttore si abbassa ed il LED si accende.Questa funzionalità è ottenuta sempli-cemente con due immagini: in una c’è rappresentato l’interruttore in posizione off con a fianco il LED spento, mentre nell’altra immagine si trova l’interruttore in posizione on con il LED acceso.Il rettangolo che viene evidenziato è stato già descritto: sostanzialmente esso definisce l’area attiva in cui se facciamo clic cambieremo lo stato di una variabile e conseguentemente forzeremo il cari-camento dell’immagine corrispondente (interruttore on oppure interruttore off).Per gestire le immagini in Processing abbiamo bisogno delle seguenti primitive:- PImage imgBt1On; questo è il tipo di dato per le immagini, in pratica imgB-t1On, è una variabile definita del tipo im-magine; Processing può gestire immagini

.png, .gif, .jpg, .tga. In questo caso, oltre a definire le variabili tipo int, float, defini-remo anche quelle Pimage;- imgBt1On=loadImage(“button2On.png”); questa istruzione, come si può fa-cilmente intuire, costituisce il caricamento vero e proprio dell’immagine nella va-riabile imgBt1On che abbiamo preceden-temente definito; l’immagine da caricare deve essere contenuta nella stessa cartella di Processing dove è contenuto il codice;- image(imgBt1On, 0, 0); questa istruzione costituisce la visualizzazione dell’immagine (in questo caso nella po-sizione X0 e Y0); ovviamente, cambiando questi valori possiamo spostare l’immagi-ne nell’area del programma.

Capito come si definiscono, caricano e vi-sualizzano le immagini, la parte di codice del programma che riportiamo in questa puntata del corso è quella visibile nel Listato 3.A cambiare il valore della variabile button sarà il fatto che avremo fatto clic nell’area attiva; questo evento sarà gestito dalla

Fig. 3 Fig. 4

Fig. 5

Fig. 6 Fig. 7

Page 25: Processing - Homepage | DidatticaWEB

152 Novembre 2011 ~ Elettronica In

Corso Processing

funzione overRect(). Ciò che è interessan-te notare è che, ad ogni ciclo, possiamo rappresentare un’immagine o l’altra e che l’effetto dell’interruttore abbassato e del LED acceso è dato semplicemente dalla sovrapposizione di due immagini. La costruzione delle immagini grafiche per le interfacce può essere fatta con qual-siasi programma di disegno: quasi banal-mente con paint, oppure con Inkscape; addirittura potremmo caricare delle foto o combinare foto e disegni.

LEZIONE 3_3Estendiamo ora l’applicazione delle im-magini per realizzare un indicatore analo-gico, tipo manometro, rappresentato nella Fig. 9. L’indicatore è ottenuto per mezzo della sovrapposizione di due immagi-ni, una delle quali rappresenta la scala graduata e sarà tenuta fissa, mentre l’altra rappresenta la lancetta e sarà fatta ruotare. Le immagini che ci servono sono gauge.png (la scala) e arrow.png (la lancetta). In queste applicazioni è conveniente usare le immagini .png, in quanto, pur essendo del tutto analoghe alle bitmap, hanno il vantaggio di poter definire il colore tra-sparente, come evidenziato nella Fig. 10. In questa stessa figura vediamo che le nostre immagini hanno dimensioni di 300x300 pixel per la base e 120x120 pixel per la lancetta. Nel programma definiremo le variabili immagine, che sono le seguenti:

PImage gauge;

PImage arrow;

Poi caricheremo le immagini effettive::

gauge = loadImage(“gauge2.png”);

arrow = loadImage(“arrow2.png”)

A questo punto vediamo che se andassimo a visualizzarle in questo modo:

image(gauge, 0, 0);

image(arrow, 0, 0);

otterremmo quanto rappresentato nella Fig. 11. Tuttavia vogliamo assicurarci che i centri della scala e della lancetta coin-cidano e che la lancetta possa essere fatta ruotare.Per ottenere queste funzionalità ci ser-vono ancora due primitive molto impor-tanti di Processing, che sono translate() e rotate(); la prima effettua una traslazione, mentre la seconda opera una rotazione. Dobbiamo però prestare molta attenzione al fatto che non si riferiscono alle immagi-ni, ma al sistema di riferimento.In Processing, ciò che viene traslato e ruo-tato è sempre il sistema di riferimento.La Fig. 12 illustra come avviene questo processo, che possiamo spiegare così: supponiamo di voler porre l’immagine della lancetta al centro e ruotata di 30°; innanzitutto trasliamo il sistema di rife-rimento alla coordinata 150,150. Questo punto ora sarà la nuova origine 0,0. Ora, se ruotiamo tutto di 30° ruoterà l’intero si-stema di riferimento. Se rappresentassimo

Fig. 8

Fig. 9

Fig. 10

Page 26: Processing - Homepage | DidatticaWEB

Elettronica In ~ Novembre 2011 153

Cors

o Pr

oces

sing

ora l’immagine della lancetta:

image(arrow, 0, 0)

otterremmo quanto rappresentato dalla figura centrale e cioè la lancetta ruotata di 30°. Ma rispetto alla sua origine (0,0;) per centrare la lancetta rispetto al centro della scala dobbiamo posizionarla al punto definito da x=-60 e y=-100. Se faremo in modo che l’angolo di rotazione sia rap-

presentativo del valore da visualizzare, avremo com-pletato il nostro indicatore.Riportiamo, nel Listato 4, il relativo codice di Proces-

sing. Cambiando il valore di val, tra 0 e 255, si nota che la lancetta percorre tutta la rotazione dal valore iniziale della scala a quello finale.Ad agire sulla rotazione della lancetta non è direttamente val, ma valm, cioè la tradu-zione di val che va da 0 a 255 in un valore che invece varia tra -160 e +160. Perciò quando val è 0, allora valm=-160, mentre quando val=128 → valm = 0 (lancetta ver-ticale); ancora, quando val = 255 → valm = 160 (fondo scala). Questa conversione è ottenuta per mezzo della funzione map() precedentemente descritta.La variabile valm rappresenta un valore espresso in °, ma invece la funzione rota-te() agisce su un valore angolare espres-so in radianti; il problema viene risolto semplicemente attraverso la funzione di conversione radians().

LEZIONE 3_4Avendo definito un pulsante ed un indi-catore grafico, realizziamo ora un altro strumento di input, cioè una manopola. Si tratta sostanzialmente di un controller ro-tativo funzionante così: quando il punta-

Fig. 11

Fig. 12

Listato 4PImage gauge; //definizione dell’immagine della base dell’indicatorePImage arrow; //definizione dell’immagine della lancetta

int val; //definizione della variabile che definisce la rotazione e che varierà tra 0 - 255float valm; //definizione della variabile che definisce la rotazione e che varierà tra -160 e +160

void setup(){ color(RGB); //tipo di colore strokeWeight(1); //spessore linee strokeJoin(SQUARE); //tipo di spigolo: quadrato

fill(30); //grigio di sfondo smooth(); size(300,300); gauge = loadImage(“gauge2.png”); //caricamento dell’immagine relativa alla base dell’indicatore arrow = loadImage(“arrow2.png”); //caricamento dell’immagine relativa alla lancetta}

void draw(){ image(gauge,0,0); //visualizzazione dell’immagine della base dell’indicatore translate(150,150); //traslazione dell’origine del sistema di riferimento da X0,Y0 a X150,Y150 val=250; //attribuiamo a val un valore fisso, val 0-->255 valm = map(val, 0, 255, -160, 160); //valm è la traduzione di val in un range che va da -160 a +160 //quando val = 0 valm = -160, quando val = 255 valm = +160

rotate(radians(valm)); //rotazione del sistema di riferimento, il valore di valm (espresso in °) viene tra-dotto in radianti image(arrow,-60,-100); //nel sistema di riferimento ruotato e traslato viene rappresentata l’immagine della lancetta

}

Page 27: Processing - Homepage | DidatticaWEB

154 Novembre 2011 ~ Elettronica In

Corso Processing

tore del mouse gli passa sopra, si eviden-zia, mentre se vi facciamo clic muovendo contemporaneamente il mouse, la mano-pola ruota. La funzionalità corrispondente è illustrata nella Fig. 13. La tecnica che fa evidenziare il controller è sempre la stes-sa, ovvero usiamo la funzione overRect(). Per quanto riguarda la rotazione della manopola, invece, il principio è analogo alla rotazione dell’immagine della lancet-ta nell’indicatore analogico, ma l’angolo di rotazione deve dipendere dallo sposta-mento del mouse. Riportiamo, nel Listato 5, la porzione di codice che permette di realizzare le funzionalità descritte. Se il mouse è sopra l’area attiva ed il tasto sinistro è premuto (mousePressed) allora si verifica:

knobRot = int(map(mouseX,10, 110, -130,130))

Alla variabile knobRot, viene assegnato un valore che va da -130 a +130 e questo valore è proporzionato rispetto a 10÷100, che è l’escursione del mouse nell’area attiva (la posizione “x” del puntatore del mouse viene restituita dal parametro mouseX). Quando il cursore del mouse si trova nell’area attiva, definita nell’asse x da 10 a 100, ed il tasto sinistro è premuto, muovendo il mouse il parametro mouseX varierà tra 10 e 100.La variabile knobRot rappresenterà un angolo di rotazione che varierà e tra -130° e +130°. Questo angolo è rappresentati-vo di mouseX (che, come abbiamo detto, varia tra 10 e 100), ma varierà nel’ambito compreso fra -130 e +130. LEZIONE 3_5Sfruttando tutte le tecniche di visualizzazione, di rappresentazione e controllo fin qui descritte, è possibile Fig. 13

Listato 5…

if ((overRect(10,10,100,100)) && mousePressed){ //se contemporaneamente il mouse è sopra l’area della manopola ed il tasto sinistro è premuto knobRot = int(map(mouseX,10, 110, -130,130)); //rotazione, mouseX varia tra 10 e 110, map fa la proporzione tra -130° e +130° valTx = int(map(mouseX,10,110,0,255)); //valTx potrebbe essere valore trasmesso via seriale, mouseX varia tra 10 e 110, viene scalato tra 0 e 255 println(valTx); //trasmissione a terminale di valTx }

rotate(radians(knobRot)); //rotazione del sistema di riferimento in base al valore della variabile knobRot image(knob, -40,-40); //visualizzazione della manopola ruotata...

Listato 6… //stato di decodifica del valore seriale ricevuto if (val == ‘H’) { // se il valore di val è il carattere ‘H’ digitalWrite(LEDPin[1], HIGH); // viene acceso il LED 1 } if (val == ‘L’){ digitalWrite(LEDPin[1], LOW); } if ((val >0) && (val<255)){ // se il valore seriale ricevuto è compreso tra 0 e 255 analogWrite(LEDPin[2], val); // sul terzo pin della lista, quindi pin 10 viene rappresentato val in PWM }

delay(1); // ciclo di attesa di 1 ms …

Page 28: Processing - Homepage | DidatticaWEB

Elettronica In ~ Novembre 2011 155

Cors

o Pr

oces

sing

re la libreria, perché dovremo anche far girare su Arduino un apposito programma chiamato firmdata.Ma procediamo con ordine: per prima cosa visitiamo la pagina web www.ardu-ino.cc/playground/Interfacing/Processing, dalla quale scarichiamo la libreria proces-sing-arduino-0017.zip; questa deve essere estratta e la cartella arduino va copiata nella cartella libraries di Processing.A questo punto bisogna aprire l’editor di Arduino e, come mostrato dalla Fig. 17, accedere al percorso Examples>Firmdata>StandardFirmadata, quindi fare verify e l’upload su Arduino.A questo punto i programmi in Processing dovranno essere strutturati considerando che innanzitutto bisognerà importare la libreria arduino e la libreria seriale:

import processing.serial.*;

import cc.arduino.*;

Poi occorrerà definire l’oggetto arduino della classe Arduino (questi aspetti sa-ranno meglio approfonditi nelle prossime lezioni):

Arduino arduino;

Ora, dentro Setup definiremo le carat-teristiche di comunicazione di Arduino, analogamente a quanto si faceva per la porta seriale:

void setup()

{

arduino = new Arduino(this, Arduino.list()[0], 57600);

}

Descriviamo, qui di seguito, le primitive

realizzare un’interfaccia utente com-pleta. L’inter-faccia, ripor-tata in Fig. 14 e Fig. 15, è sostanzial-mente la stes-sa descritta nella seconda

puntata, ma in questo caso il pulsante è stato sostituito da due immagini grafiche e lo stesso vale per il LED; inoltre sono stati aggiunti la manopola e l’indicatore analogico grafico. Il pulsante controlla il LED 1 di Arduino, mentre la posizio-ne della manopola controlla il PWM che Arduino genererà sul LED 2; l’indicatore analogico rappresenta il valore letto da Arduino su Analog In 0, mentre il LED che si accende ogni secondo rappresenta il momento in cui il programma comunica con Arduino.Il codice Processing dell’interfaccia è dunque la somma dei codici qui descritti, solamente che le posizioni geometriche dei vari elementi e delle aree attive sono diverse.La Fig. 16 mostra il sistema al completo.Il codice per Arduino che deve essere uti-lizzato in questa lezione pratica è conte-nuto nel file arduino_codice_TXRX_ANA-LOG_IN_OUT.pde. Questo differisce dal precedente codice soltanto per il fatto che ha in più la deco-difica del valore PWM da rappresentare sull’uscita. Il Listato 6 contiene il codice per Arduino.

LEZIONE 3_6Rivediamo ora lo stesso programma della Lezione 3_5, utilizzando però una nuova tecnica di controllo di Arduino: in pratica sfruttiamo un’apposita libreria di Proces-sing che ci offre la comodissima opportu-nità di controllare direttamente i pin di Arduino da Processing, come se Arduino fosse una naturale estensione del PC. Così realizziamo un’interfaccia completa con firmdata.Ovviamente non basta soltanto richiama-

Fig. 15Fig. 14

Fig. 16

Page 29: Processing - Homepage | DidatticaWEB

156 Novembre 2011 ~ Elettronica In

Corso Processing

che abbiamo a disposizione usando la libreria arduino.

pinMode(pin, mode): setta un pin digita-le come ingresso o come uscita (Arduino.INPUT o Arduino.OUTPUT). Esempio:arduino.pinMode(6, Arduino.OUTPUT); il pin 6 di Arduino è

settato come uscita

arduino.pinMode(7, Arduino.INPUT); il pin 7 di Arduino è setta-

to come ingresso

digitalRead(pin): legge lo stato di un pin digitale, precedentemente settato come ingresso, e restituisce il valore (Arduino.LOW o Arduino.HIGH).Esempio:arduino.digitalRead(7); legge lo stato del pin 7

digitalWrite(pin, value): scrive Arduino.LOW o Arduino.HIGH su di un pin digita-le settato come uscita. Esempio:arduino.digitalWrite(5, Arduino.LOW); setta come alto il pin

5

arduino.digitalWrite(5, Arduino.HIGH); setta come basso il

pin 5

analogRead(pin): restituisce il valore di un ingresso analogico (0 – 1023), i pin ana-logici di Arduino sono: 0,1,2,3,4.Esempio:

val=arduino.analogRead(0); legge il valore dell’ingresso ana-

logico 0

analogWrite(pin, value): genera un se-gnale PWM sul pin assegnato. I pin di Arduino che supportano la generazione del segnale PWM sono: 3, 5, 6, 9, 10, 11. Il campo value varia tra 0 (PWM 0%) e 255 (PWM 100%).Esempio:arduino.analogWrite(10, valTx); scrive sul pin 10 un valore PWM

che è dato dalla variabile valTx

Nel Listato 7 riportiamo la porzione di codice relativa alla visualizzazione della condizione dell’ingresso analogico.Vediamo che, in pratica, per leggere il valore basta semplicemente richiamare l’opportuna funzione ed indirizzare la lettura nel pin di Arduino che ci interessa. Questa tecnica è estremamente comoda e versatile, ma ha lo svantaggio che Ardui-no deve essere programmato con firmdata. Se vogliamo far svolgere ad Arduino delle funzioni in maniera autonoma, è necessa-rio prevedere un’architettura master/slave, nella quale Arduino svolge le sue ope-razioni e quando interpellato dal master risponde; per questa ragione Arduino sarà programmato con un codice che prevede, oltre alle operazioni da svolgere, anche il protocollo di comunicazione con il master.Se invece vogliamo considerare Arduino come una periferica e tutto il programma risiede nel PC, allora conviene sfruttare la tecnica qui descritta.

PROSSIME LEZIONINella prossima puntata introdurremo la programmazione ad oggetti, che ci per-metterà di costruire gli elementi dell’in-terfaccia quali pulsanti, LED, indicatori ecc., in una maniera molto più versatile e ci consentirà di costruire interfacce com-plesse in modo abbastanza semplice.

Fig. 17

Listato 7translate(150,480); val=arduino.analogRead(0); //il dato viene letto e memorizzato su val valm = map(val, 0, 1024, -160, 160); //valm è la traduzione di val in un range che va da -160 a +160 //quando val = 0 valm = -160, quando val = 1024 valm = +160rotate(radians(valm)); //rotazione del sistema di riferimento, il valore di valm (espresso in °) viene tra-dotto in radianti

image(arrow,-60,-100); //nel sistema di riferiemento ruotato e traslato viene rappresentata l’immagine della lancetta

Page 30: Processing - Homepage | DidatticaWEB

Elettronica In ~ Dicembre 2011 / Gennaio 2012 147

Introduzione a Processing

Cors

o Pr

oces

sing

ella scorsa puntata abbiamo imparato a disegnare dei controlli grafici: indi-

catori, pulsanti, LED, manopole. Per vede-re all’opera tali elementi, abbiamo costrui-to una semplice interfaccia grafica per un programma che controlla Arduino. In que-sta quarta puntata risolveremo la proble-matica di dover costruire interfacce gra-fiche complesse che richiedano, magari,

l’uso di molti controlli e indicatori. Infatti l’interfaccia costruita nella terza puntata funzionava come un unico programma, cioè il codice relativo alla gestione dei controlli e indicatori era fuso all’interno del codice principale del programma, il quale controllava completamente l’inter-faccia. In questa sede risolveremo altresì il problema di richiamare all’interno del

Scopriamo l’Object Oriented Programming, che rappresenta un modo più semplice di costruire le nostre interfacce. Quarta puntata.

di MATTEO FIORAVANTI

N

Page 31: Processing - Homepage | DidatticaWEB

148 Dicembre 2011 / Gennaio 2012 ~ Elettronica In

Corso Processing

programma principale i veri LED, inter-ruttori, manopole, nelle quantità che ci occorrono, senza preoccuparci del codice occorrente ad essi, ma interessandoci uni-camente al loro comportamento in termini di ingressi ed uscite (come del resto acca-drebbe con i controlli reali).Conosciamo già l’uso delle funzioni in Processing; ricordiamo che una funzione è definita all’interno del programma, viene richiamata a seconda dei casi e ne gestia-mo ingressi ed uscite. Per il problema che affrontiamo, ci serve qualcosa di simile.Supponiamo di dover inserire nella nostra interfaccia tre LED, ognuno dei quali avrà un suo significato e sarà collegato a certe variabili, proprio come accadrebbe in un sistema reale. Alla luce di quanto spiegato nella scor-sa puntata, la prima cosa che ci viene in mente è piazzare tre immagini dei LED e modificarle a seconda delle variabili che dovrebbero rappresentare.L’interfaccia siffatta sarà certamente funzionante, ma il relativo codice potreb-be diventare ingestibile nel momento in cui ci trovassimo a dover aggiungere altri LED ai precedenti, oppure laddove doves-simo essere costretti a complicare l’archi-tettura del programma. Pensiamo invece a quanto sarebbe più comodo se potessimo richiamare una specie di costruttore di LED, che ci per-mettesse di costruire i componenti led1, led2 led3 (cioè i nostri tre LED virtuali) e così via, e se ogni diodo luminoso potesse essere gestito in completa autonomia, cioè avesse un codice a sè stante e direttamente controllabile.Potremmo, infatti, attribuire lo stato di Accesso a led1, mentre cambiamo led2 da Acceso a Spento e così via. Se avessimo questa possibilità, ci baste-rebbe concentrarci unicamente sul pro-gramma principale ed ogni LED si com-

porterebbe proprio come se fosse reale. Fare questo è possibile, ma dobbiamo introdurre una tecnica di programmazione nuova e che risolva in maniera estrema-mente semplice questi casi; tale tecnica si chiama programmazione orientata agli oggetti, ovvero, abbreviando, OOP (acro-nimo di Object Oriented Programming).Processing permette di implementare que-sta tecnica un modo del tutto naturale.Per spiegare l’OOP partiremo introdu-cendo il concetto di Classe e di Oggetto, poi trasformeremo i controlli descritti nella lezione precedente a loro volta in Classi ed Oggetti e, con quanto appreso, scriveremo il codice di un’interfaccia che simulerà un sistema reale.

IL CODICE DI QUESTA PUNTATAQui di seguito elenchiamo i programmi di esempio di questa puntata del corso.1. LEZIONE 4_1 - INTRODUZIONE ALLA

PROGRAMMAZIONE AD OGGETTI. Questo codice permette di costruire un’interfaccia con tre oggetti apparte-nenti alla Classe Led; serve, inoltre, per familiarizzare con i concetti di Classe e di Oggetto.

2. LEZIONE 4_2 - INTERFACCIA GRAFI-CA COMPLESSA. Questo codice per-mette di costruire un’interfaccia grafica complessa, nella quale tutti i controlli ed indicatori della terza lezione sono diventati Classi ed Oggetti.

Fig. 1

Fig. 2

Page 32: Processing - Homepage | DidatticaWEB

Elettronica In ~ Dicembre 2011 / Gennaio 2012 149

Cors

o Pr

oces

sing

Se lo volessimo spegnere, opereremmo sempre attraverso lo stesso metodo, ma scrivendo:

Led_1.Acceso(false).

Volendo inserire nel nostro ipotetico pro-gramma un secondo LED, basterà definire un Oggetto che, verosimilmente, chia-meremo Led_2, sempre appartenente alla Classe Led, ma avente attributi differenti, una diversa posizione X ed Y (110,10) ed una differente Etichetta (Circuito2). Quin-di abbiamo creato, o meglio Processing ha costruito, un nuovo Oggetto chiama-to Led_2, diverso da Led_1, ma avente comunque gli stessi Metodi. Infatti, se volessimo accendere Led_2 opereremmo in questo modo:

Led_2.Acceso(true).

Supponiamo, ora, di avere all’interno del programma la situazione rappresentata in Fig. 2, ossia Led_1 spento e Led_2 acceso. Operando in termini di Metodi, possiamo accendere Led_1 e passare alla situazione di Fig. 3, scrivendo:

Led_1.Acceso(true)

Chiaramente quanto riportato fin qui è un esempio che dovrebbe aiutarci a capire il concetto di Classe ed Oggetto.Scendiamo ora sempre più sul caso prati-co, per vedere come tutto questo si appli-ca. Consideriamo il codice di esempio (Le-zione_4_1.pde): prima di tutto gli Oggetti devono essere dichiarati, proprio come succede con le variabili; infatti nel nostro codice abbiamo bisogno di tre LED (ld1, ld2, ld3), quindi dichiareremo tre oggetti “led” appartenenti alla Classe Led, come riportato in Fig. 4. Poi, dentro void setup(){} andremo a fare ciò che prende il nome di istanza degli oggetti precedentemente dichiarati; in pratica, ciò si fa attraverso il comando new. Perciò, la nuova istanza dell’oggetto ld1 assumerà questo aspetto:

ld1 = new Led(10, 10, “Led 1”);

LEZIONE 4_1 - INTRODUZIONE ALLA PROGRAMMAZIONE AD OGGETTIPartiamo ora da una minima astrazione, definendo il LED come una Classe; ciò significa innanzitutto che alla Classe dobbiamo dare un Nome, quindi bisogna definire dei Campi e dei Metodi. Tutto questo è rappresentato in Fig. 1.Il Nome della Classe è quello che identifi-cherà l’Oggetto da essa costruito, mentre i Campi sono gli attributi (ad esempio la posizione X ed Y dell’oggetto e l’etichetta) mentre i Metodi sono i comportamenti che possono essere eseguiti. Nel caso del LED, abbiamo definito come metodo Acceso(): in pratica, Acceso(true) accenderà il diodo luminoso, mentre Acceso(false) lo spe-gnerà. Quindi, i Campi di una Classe sono degli attributi, mentre i Metodi sono le azioni, ovvero i comportamenti che posso-no essere intrapresi. La Classe è un insieme generico; di essa si definiscono i singoli Oggetti, come ci mostra la Fig. 2. Infatti, se definiamo come Nome Led_1, come Posizione 10,10 e per Etichetta Circuito1, avremo definito un Oggetto che si chiama Led_1, appartie-ne alla Classe Led, si trova nella posizione 10,10 e mostra come etichetta “Circuito1”. Ora, se volessimo accendere questo og-getto, dovremmo intervenire sul metodo Acceso, scrivendo quanto segue:

Led_1.Acceso(true),

Fig. 3

Page 33: Processing - Homepage | DidatticaWEB

150 Dicembre 2011 / Gennaio 2012 ~ Elettronica In

Corso Processing

Ciò significa che il nuovo oggetto ld1 della Classe Led deve essere costruito con i seguenti campi: posizione X =10, posizio-ne Y =10, Etichetta = “Led 1”.Procediamo allo stesso modo per gli altri Oggetti, come riportato in Fig. 4.Dentro il ciclo principale void draw(){}, si andrà ad agire sui Metodi di Led.Per la Classe Led abbiamo definito due Metodi: .display() e .update(). Il primo fa letteralmente apparire il LED (ciò signi-fica che se non viene richiamato questo metodo, il diodo luminoso non compare nell’interfaccia) anche se è stato definito ed istanziato, mentre il secondo consente

di aggiornare dinamicamente lo stato del LED, facendolo passare da acceso a spento e viceversa. Il codice del programma corrispondente è riportato di seguito nel Listato 1 ed è anche visibile in Fig. 4.A questo punto abbiamo visto come si opera con le Classi e con gli Oggetti; rima-ne da capire come questi si programmano, ed è quanto faremo ora. Scrivere il codice per definire una Classe non è per nulla differente dallo scrivere codice per un programma o per una funzione; cambiano solo alcuni dettagli.Prima di tutto la Classe (che ad esempio

Fig. 4

Listato 1Led ld1, ld2, ld3; //dichiarazione degli oggetti ld1, ld2, ld3 appartenenti alla Classe Led

void setup(){ //inizializzazione background(200); //sfondo size(200,400); //dimensione dell’area grafica del programma fill(0);

ld1 = new Led(10, 10, “Led 1”); //istanza dell’oggetto ld1 e sua costruzione ld2 = new Led(10, 120, “Led 2”); //istanza dell’oggetto ld2 e sua costruzione ld3 = new Led(10, 230, “Alarm”); //istanza dell’oggetto ld3 e sua costruzione

} void draw(){ ld1.display(); //richiamo del metodo .display() dell’oggetto ld1, in pratica ld1 viene visualizzato ld2.display(); //identica cosa per ld2 ld3.display(); //identica cosa per ld3 ld1.update(true); //richiamo del metodo .update() per ld1, mettendolo a “true” si accende il led ld1, //mettendolo a “false” si spegne ld2.update(false); //ld2 viene spento ld3.update(true); //ld3 viene acceso}

Page 34: Processing - Homepage | DidatticaWEB

Elettronica In ~ Dicembre 2011 / Gennaio 2012 151

Cors

o Pr

oces

sing

può essere Led) è definita all’interno di due parentesi graffe:

class Led{}

Questo significa che tutto quanto serve alla Classe Led per funzionare è contenuto all’interno di tali parentesi.Ci saranno delle variabili locali e inoltre verranno definiti il costruttore dell’inter-faccia e i metodi. Nel Listato 2 è riportato il codice commentato per la Classe Led e lo stesso è visibile nella Fig. 5, che mostra come questo codice interagisce con il pro-gramma principale.Un aspetto importante su cui porre l’at-tenzione è che la classe, per funzionare, potrebbe necessitare di sue variabili interne; tali variabili sono definite come in qualsiasi altro programma di Proces-sing e soprattutto sono delle variabili locali. Quando viene istanziato un nuovo oggetto, abbiamo visto come questo debba essere definito nei suoi Campi; questi ulti-mi saranno poi passati alle variabili locali della Classe, così come mostrato dalla Fig.

5, attraverso quello che viene definito il Costruttore dell’Oggetto. Per la Classe in questione, quest’ultimo assume la seguen-te forma:

Led(int tempX, int tempY, String lable) {}

Il Costruttore è in definitiva il ponte che consente di trasferire i Campi, definiti quando l’Oggetto viene istanziato, alle va-riabili locali che servono poi al program-ma per costruire l’Oggetto.Riassumendo, i Campi vengono defini-ti nel programma principale quando si istanzia il nuovo Oggetto; nella Classe, i Campi sono trasferiti alle variabili locali attraverso il Costruttore dell’Oggetto.La Fig. 5 rappresenta anche, in maniera schematica, i due Metodi, i quali all’in-terno della Classe sono sostanzialmente delle funzioni; ad esempio:

void display() {}

Quindi il Metodo, in realtà, è una funzio-ne, che però viene richiamata dall’Oggetto

Fig. 5

Page 35: Processing - Homepage | DidatticaWEB

152 Dicembre 2011 / Gennaio 2012 ~ Elettronica In

Corso Processing

proprietario, nel programma principale:

...

ld1.display();

...

Nel Listato 2 (codice riguardante la clas-

se Led) la parte riguardante la Classe può essere scritta in fondo al programma principale, cioè praticamente fuori da void draw(){}, analogamente a quanto si fa per le funzioni. Però esiste un modo che ci consente di rendere il codice ancora più leggibile: si tratta di sfruttare una fun-zionalità dell’editor di Processing, che è quella che ci permette di aprire dei nuovi Tab di editor e che ancora non abbiamo affrontato. La Fig. 6 ci illustra come ciò viene fatto e comunque la procedura è la seguente: innanzi tutto occorre andare con

il cursore, nell’angolo in alto a destra dell’editor, sopra alla rappresentazione della freccia verso destra, e fare clic con il tasto sinistro del mouse. Allora

si aprirà un menu a tendina e bisogne-rà selezionare New Tab; a questo punto comparirà una barra gialla sotto l’editor, contenente la richiesta di inserimento del nome per il nuovo file “New name for file”. Nella casella di testo bisogna scri-vere il nome del nuovo Tab, che noi con “grande originalità” chiameremo Led. Di fianco al Tab indicante il nome del file, ne sarà comparso un altro con il nome di “Led”; facendovi clic, si aprirà una nuova pagina di editor. Ora non rimane altro da fare che copiare ed incollare tutto il codice relativo alla Classe Led, che avevamo scritto nel programma principale, ed in-collarla nel nuovo Tab Led (Fig. 7). A que-sto corrisponde effettivamente un nuovo file, che si trova nella stessa cartella del programma principale. Il codice, quindi, rimane semplicemente quello di Fig. 8, ma soprattutto, la Classe Led che abbia-mo creato possiamo trasportarla in altri programmi, spostando il file Led.pde nelle cartelle dei programmi che andremo a cre-are; inoltre, dentro ai programmi potremo creare tutti gli oggetti “led” che vorremo, ricordandoci di definirli all’inizio del programma, di istanziarli e di usarli con i relativi metodi. Questa tecnica consente di semplificare notevolmente il codice e di riurilizzarlo tutte le volte che vorremo nei programmi che andremo a creare. Dal punto di vista della creazione di interfacce grafiche, questo permette, una volta che

Fig. 6

Page 36: Processing - Homepage | DidatticaWEB

Elettronica In ~ Dicembre 2011 / Gennaio 2012 153

Cors

o Pr

oces

sing

Fig. 7

Listato 2class Led{ PImage imgLdOn; //definizione dell’immagine del pulsante on, nel programma sarà richiamata led1On PImage imgLdOff; //definizione dell’immagine del pulsante on, nel programma sarà richiamata led1Off //dichiarazioni delle variabili funzionali all’esecuzione della classe int x; int y; boolean active = false; String LedLable;

//costruttore Led(int tempX, int tempY, String lable){ //la Classe Led necessita di tre campi che devono essere definiti // quando si istanzia un nuovo oggetto //la posizione X, Y ed una stringa che definisce l’etichetta x = tempX; //il campo tempX viene passato alla variabile globale della Classe X y = tempY; //il campo tempY viene passato alla variabile globale della Classe Y

LedLable = lable; //il campo lable viene passato alla variabile globale della Classe LedLable imgLdOn = loadImage(“led1On.png”);//vengono caricate le immagini .png rappresentative del led acceso e spento imgLdOff = loadImage(“led1Off.png”); textSize(20); //viene definita dimensione del carattere textAlign(LEFT); //viene definito l’allineamento del testo, a sinistra //questi parametri servono per la rappresentazione dell’etichetta }

//METODO update() --------------------------- void update(boolean z){ //il metodo si aspetta che sia passato un parametro true/false active = z; //il valore del parametro è trasferito alla variabile globale “active”, della Classe }//-------------------------------------------

//METODO display() -------------------------- void display(){ text(LedLable, x+100,y+100); if (active){ // a seconda del valore della variabile “active” il metodo display() mostra un’immagine diversa del led image(imgLdOn, x,y); //immagine del led acceso, se active == true } else{ image(imgLdOff, x,y); //immagine del led spento, se active == false } }//-----------------------------------------

Page 37: Processing - Homepage | DidatticaWEB

154 Dicembre 2011 / Gennaio 2012 ~ Elettronica In

avremo creato i nostri controlli, di concen-trarci unicamente sul programma.

LEZIONE 4_2 - INTERFACCIA GRAFICA COMPLESSA Il secondo codice presentato in questa lezione ci serve per prendere dimesti-chezza con i controlli di tipo Led, Knob, Button ed Indicator, trasformati però in Classi e quindi in Oggetti. Supponiamo di voler realizzare l’interfaccia di Fig. 9, dove si trovano tre interruttori, tre LED, una manopola e un indicatore analogico. Stabiliamo a questo punto una funziona-lità puramente di esempio: l’interruttore denominato “Start” attiva un ipotetico sistema che incrementa la sua velocità e questa viene indicata dalla lancetta dell’indicatore analogico; il livello di questa velocità è settato tramite la ma-nopola “Level” e quando raggiunge una soglia, si deve accendere il LED deno-minato “Alarm”. Questa funzionalità è

rappresentata in Fig. 10. Aggiungiamo un’ulteriore complessità, cioè facciamo in modo che i due interruttori siglati “Circuit 1” e “Circuit 2” attivino, rispettivamente, “Led 1” e “Led 2” (Fig. 11). L’interfac-cia qui rappresentata è in definitiva un simulatore: non serve a nulla di reale, ma ci consente di apprendere una maniera più efficace di programmare questo genere di applicazioni. Se vogliamo che la nostra interfaccia controlli realmente qualcosa, possiamo riprendere le tecniche illustrate nella lezione precedente e magari collega-re il tutto ad Arduino. Il codice contenuto nella cartella Lezione_4_2 contiene sia il programma principale che simula l’inter-faccia, sia tutte le classi usate in questo esempio: Led, Button, Indicator e Knob. Il codice del programma è strutturato come nell’esempio precedente, quindi nella fase iniziale in cui si dichiarano gli oggetti, nella fase di istanza degli oggetti e nel vero e proprio ciclo principale del pro-gramma. Le figure 12, 13, 14 e 15 riassu-mono le Classi utilizzate, ciascuna con i Campi e Metodi caratteristici. Brevemente, tutti i Campi sono rappresen-ti dalle posizioni X, Y e dall’etichetta.Tutte le Classi qui usate hanno il me-todo .display(), che ne consente la visualizzazione.La Classe Led ha il Metodo .update(), il quale, come spiegato, se gli viene passato true accende il LED, mentre lo spegne se riceve false.La Classe Button ha il Metodo .active(), in-

Corso Processing

Fig. 9

Fig. 8

Fig. 10 Fig. 11

Page 38: Processing - Homepage | DidatticaWEB

Elettronica In ~ Dicembre 2011 / Gennaio 2012 155

vece, restituisce un booleano (true/false) a seconda della posizione dell’interruttore.La Classe Knob ha il Metodo .position(), il quale restituisce un valore intero compre-so tra 0 e 1024, proporzionale alla posi-zione della manopola. La Classe Indicator ha il Metodo .update() che si aspetta un valore intero compreso tra 0 e 1024; chiara-mente il valore 0 corrisponderà all’inizio della scala, mentre 1024 equivarrà alla

Fig. 12 Fig. 13 Fig. 14

Cors

o Pr

oces

sing

posizione di fondo scala. Il codice del programma di interfaccia è contenuto nel Listato 3. Non ci dilunghiamo, in questa lezione, sul codice specifico delle varie Classi qui presentate, in quanto è sostanzialmente lo stesso esposto nella terza puntata del nostro corso, ma scritto sotto forma di Classe. Quindi, a livello sostanziale non cambia nulla: è solo differente la forma-

Fig. 15

(Continua)

Listato 3//definizione degli OGGETTIButton bt1, bt2, bt3; //definizione degli oggetti della classe ButtonIndicator ind1; //definizione degli oggetti della classe IndicatorKnob knb1; //definizione degli oggetti della classe KnobLed ld1, ld2, ld3; //definizione degli oggetti della classe Led//-----------------------------------------------

//dichiarazione VARIABILIPImage griglia; //definizoione della variabile griglia di tipo PImageint i=0; //definizione della variabile i che servirà come contatore//----------------------------------------------

void setup(){ background(200); //colore di sfondo size(600,600); //dimensione dell’area di disegno bt1 = new Button(0,20,”Start”); //istanza dell’oggetto bt1 e sua costruzione bt2 = new Button(200,20,”Circuit 1”); //istanza dell’oggetto bt1 e sua costruzione bt3 = new Button(400,20,”Circuit 2”); //istanza dell’oggetto bt2 e sua costruzione ind1 = new Indicator(0,200,”Speed”); //istanza dell’oggetto bt3 e sua costruzione knb1 = new Knob(400,200, “Level”); //istanza dell’oggetto knb1 e sua costruzione ld1 = new Led(400, 300, “Led 1”); //istanza dell’oggetto ld1 e sua costruzione ld2 = new Led(400, 400, “Led 2”); //istanza dell’oggetto ld2 e sua costruzione ld3 = new Led(400, 500, “Alarm”); //istanza dell’oggetto ld3 e sua costruzione griglia = loadImage(“griglia_600_600.png”); //si carica l’immagine di una griglia per facilitare il posizio-namento degli elementi dell’interfaccia

}

void draw(){ image(griglia,0,0); //rappresentazione dell’immagine griglia bt1.display(); //rappresentazione di bt1 bt2.display(); //rappresentazione di bt2 bt3.display(); //rappresentazione di bt3 ind1.display(); //rappresentazione di ind1 knb1.display(); //rappresentazione di knb1 ld1.display(); //rappresentazione di ld1 ld2.display(); //rappresentazione di ld2

Page 39: Processing - Homepage | DidatticaWEB

156 Dicembre 2011 / Gennaio 2012 ~ Elettronica In

Corso Processing

lizzazione, come spiegato in riferimento al codice della Classe Led. Comunque vi invitiamo ad analizzare il codice delle varie Classi e ad apportare tutte le modifi che che ritenete opportune a seconda delle vostre necessità. Una piccola nota: la Fig. 16 mostra l’interfaccia completa e funzionante, ma dobbiamo notare che in essa è presente una griglia di sfondo, richiamata da:

griglia = loadImage(“griglia_600_600.png”);

Praticamente, abbiamo creato un’immagine rappresentante un quadrato di 600x600 pixel, con una griglia interna spaziata di 20 pixel, la quale ci consente di avere un riferimento quando piazziamo i controlli e gli indicatori sull’interfaccia, permettendoci in maniera Fig. 16

più rapida di allinearli. Una volta completata l’interfaccia, basterà eliminare (commentare) la riga di codice precedente, che richiama la griglia, per far sparire questa immagine.

Listato 3 - segue ld3.display(); //rappresentazione di ld3 //funzionamento dell’interfaccia

//se il bt1 “Start” è attivo //l’indicatore crescerà fino a raggiungere il valore impostato dalla manopola knb1 “Level” //se riduciamo il valore della manopola, il valore rappresentato dall’indicatore si ridurrà if(bt1.active()){ //se il l’interruttore bt1 “Start” è in posizione attiva println(knb1.position()); //sul terminale viene rappresentato il valore trasmesso dalla posizione della manopola knb1 if(i<knb1.position()){ //se la posizione della manopola è superiore alla posizione dell’indicatore i +=1; //incremento la variabile i ind1.update(i); // aggiorno la posizione dell’indicatore ad i } if(i>knb1.position()){ //se la posizione della manopola è inferiore a quella dell’indicatore i -=1; //decremento i ind1.update(i); //aggiorno la posizione dell’indicatore ad i } if(i>900){ //se la variabile i oltrepassa 900 ld3.update(true); //aggiorno lo stato del led3 “Alarm” ad acceso }else{ ld3.update(false); //altrimenti lo stato di led3 è spento } }

if(bt2.active()){ //se l’interruttore bt2 è acceso ld1.update(true); //aggiorno lo stato di ld1 a “true”, accendo il led } else{ ld1.update(false); //altrimenti lo stato del led è spento } if(bt3.active()){ //se l’interruttore bt3 è acceso ld2.update(true); //aggiorno lo stato di ld2 a “true”, accendo il led } else{ ld2.update(false); //altrimenti lo stato del led è spento }

}