La piattaforma hardware e software Arduino: parte IV Corso di autoapprendimento Tempo previsto: 3 – 4 ore Prof. Angelo Monfroglio In questa quarta parte parleremo della programmazione di Arduino nel linguaggio Processing, basato su Java. Ma prima di tutto, dell’interfacciamento con i display LCD. Generalità sul display LCD (Hitachi con controller HD44780) E’ fatto apposta per essere usato con un micro controller e costa circa 10 euro. Può visualizzare i normali caratteri alfanumerici, lettere greche, segni di interpunzione, simboli matematici e caratteri creati dall’utente, attraverso 3 memorie di cui dispone: -DDRAM (RAM per i dati) -CGRAM (RAM per generare caratteri personalizzati) -CGROM (ROM che contiene il generatore di caratteri interno). Il messaggio può essere spostato automaticamente a destra e sinistra, si può controllare la forma del cursore, e, come opzione, è disponibile la retroilluminazione. I piedini sono14, più 2 per la retroilluminazione a LED. Funzione Pin Nome Valore Descrizione Massa 1 VSS - 0V Alimentazione 2 VDD - +5V Contrasto regolabile 3 VEE - Da 0 a VDD Controllo 4 RS 0o1 0 = comando, 1 dato (pin D0 – D7) Controllo 5 R/W 0o1 0 scrive, 1 legge Controllo 6 E 0 o 1 o fronte di discesa 0 accesso disabilitato, 1 operativo, fronte = trasferimento a LCD Dato o comando Da 7 a 14 D0..D7 0o1 Dato o comando Lo schermo visualizza 2 righe di 16 caratteri con matrice di 5x8 pixel (oppure 5x11). E’ anche possibile regolare il contrasto: solitamente va regolato al massimo. La memoria DDRAM è usata per memorizzare i caratteri da visualizzare: 80. Di questi 32 sono visibili direttamente sul display. Si può configurare la DDRAM in modo che avvenga uno scorrimento automatico a destra e deve essere fissato il primo indirizzo, ad esempio 00h. Ecco la mappa. 00h 27h 40h 4F 67h Non vis. Visibile La memoria CGROM, non volatile, contiene il set di base dei caratteri visualizzabili, sotto forma di matrici di puntini (nel nostro caso 5 x 8). Gli indirizzi corrispondono in pratica ai caratteri ASCII. Occorre controllare l’indirizzo effettivo di ogni simbolo sulla mappa. Oltre ai simboli standard, l’utente può crearne di propri con la memoria CGRAM di 64 bytes. La matrice di punti è 5 x 8 che significa 8 righe di 5 colonne: * * * * significa acceso Gli indirizzi vanno da 00 a 3F e ovviamente ciascuna locazione usa solo 5 bit degli 8 perché ogni riga ha 5 colonne. 00 … 3K. Il modulo LCD può essere interfacciato con il microcontrollore usando 8 bit di dati o 4 per volta. Il vantaggio del primo modo è la facilità di gestione e la velocità, del secondo modo il risparmio di piedini usati per le porte del microcontrollore. Se si usa un PIC da 28 piedini quest’ultima soluzione può essere preferibile. Si può anche risparmiare un piedino mettendo a massa R/W e rinunciando a leggere da LCD, ma è sconsigliabile. Ogni comando dura circa 2 milli secondi per essere eseguito. Quando il circuito è alimentato LCD è azzerato., dopo 15 milli secondi, e posto nella condizione di default: DL = 1 comunicazione a 8 bit N=0 il dato su una linea F=0 il formato della griglia è 5 x 8 D=0 display off U=0 cursor off B=0 lampeggio off I/D = 0 gli indirizzi sono incrementati ogni volta automaticamente di 1 S=0 display shift off. I comandi Il valore sui pin D0-D7 può essere un comando o un dato. Se RS = 0 viene considerato un comando. Comando RS R/W D7 D6 D5 D4 D3 D2 D1 D0 Tempo Clear 0 0 0 0 0 0 0 0 0 1 1.64 mS Home 0 0 0 0 0 0 0 0 1 X 1.64 Mode 0 0 0 0 0 0 0 1 I/D S 40 Display ON/OFF 0 0 0 0 0 0 1 D U B 40 Shift 0 0 0 0 0 1 D/C R/L X X 40 Function 0 0 0 0 1 DL N F X X 40 Indirizzo CGRAM 0 0 0 1 Addr Addr Addr Addr Addr Addr 40 Indirizzo DDRAM 0 0 1 ADDr Addr Addr Addr Addr Addr Addr 40 Legge 0 1 BF Addr Addr Addr Addr Addr Addr Addr 40 flag busy Scrive in 1 CGRAM o in DDRAM 0 D7 D6 D5 D4 D3 D2 D1 D0 40 Legge 1 D7 D6 D5 D4 D3 D2 D1 D0 40 1 Con i significati: I/D = 1 incremento di 1, = 0 decremento S = 1 shift del display on, 0 off D = 1 display on, 0 off U = 1 cursor on, 0 cursor off B = 1 lampeggio, 0 non lampeggio R/L = 1 sposta a destra, 0 a sinistra DL = 1 interfaccia a 8 bit, 0 a 4 bit N = 1 display su 2 linee, 0 su una linea F = 1 matrice dei caratteri 5 x 10, = 0 5 x 7 D/C = 1 spostamento sul display, = 0 spostamento del cursore. Se siete spaventati da tutti questi comandi, buone notizie: con Arduino è tutto più facile. Il seguente esempio visualizza un messaggio sul display LCD e il tempo in secondi dall’ultimo reset della scheda Arduino. Nella prima parte del nostro corso abbiamo detto che con Arduino non ha senso scrivere Hello sul video come primo esempio. Ha senso invece farlo su un display esterno LCD, anzi è molto istruttivo. LiquidCrystal Library semplifica il commando del display. Non è necessario imparare le istruzioni a basso livello. Il modulo LCD Hitachi si può controllore in due modi: con 4 o 8 bit. Il modo a 4 bit richiede 7 piedini di Arduino; il modo a 8 bit 11. Il primo modo è sufficiente nella maggior parte dei casi. L’esempio che segue riguarda il modo a 4 bit e la visualizzazione su 2 linnee ognuna con 16 caratteri. H a r d w a r e r i c h i es t o Arduino Board LCD Screen (compatibile con Hitachi HD44780 driver) 10k Potenziometro breadboard cavetti C i r cu i t o Collegare così il display ad Arduino: LCD RS pin al digital pin 12 LCD Enable pin al digital pin 11 LCD D4 pin al digital pin 5 LCD D5 pin al digital pin 4 LCD D6 pin al digital pin 3 LCD D7 pin al digital pin 2 Inoltre, collegare il potenziometro da 10K pot a +5V e GND col il wiper (output) a LCD VO pin (pin3). Codice /* LiquidCrystal Library - Hello World Demonstrates the use a 16x2 LCD display. The LiquidCrystal library works with all LCD displays that are compatible with the Hitachi HD44780 driver. There are many of them out there, and you can usually tell them by the 16-pin interface. This sketch prints "Hello World!" to the LCD and shows the time. * * * * The circuit: LCD RS pin to digital pin LCD Enable pin to digital LCD D4 pin to digital pin LCD D5 pin to digital pin 12 pin 11 5 4 * * * * * * LCD D6 pin to digital pin 3 LCD D7 pin to digital pin 2 LCD R/W pin to ground 10K resistor: ends to +5V and ground wiper to LCD VO pin (pin 3) Library originally added 18 Apr 2008 by David A. Mellis library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net) example added 9 Jul 2009 by Tom Igoe modified 22 Nov 2010 by Tom Igoe This example code is in the public domain. http://www.arduino.cc/en/Tutorial/LiquidCrystal */ // include the library code: #include <LiquidCrystal.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 5, 4, 3, 2); void setup() { // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("hello, world!"); } void loop() { // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): lcd.setCursor(0, 1); // print the number of seconds since reset: lcd.print(millis()/1000); } Vedi anche: lcd.begin() lcd.print() lcd.setCursor() Liquid Crystal Library Blink: control of the block-style cursor. Cursor: control of the underscore-style cursor. Display: quickly blank the display without losing what's on it. TextDirection: control which way text flows from the cursor. Scroll: scroll text left and right. Serial input: accepts serial input, displays it. SetCursor: set the cursor position. Autoscroll: shift text right Display grafico LCD (Arduino shield) Display grafico LCD per Arduino Un programma esemplificativo #ifndef LCD4884_h #define LCD4884_h #ifdef PD1 #define LCD_RST PD6 #define SPI_CS PD5 #define SPI_MOSI PD3 #define SPI_SCK PD2 #define LCD_DC PD4 #define LCD_BL PD7 #else #define LCD_RST PORTD6 #define SPI_CS PORTD5 #define SPI_MOSI PORTD3 #define SPI_SCK PORTD2 #define LCD_DC PORTD4 #define LCD_BL PORTD7 #endif //display mode -- normal / highlight #define MENU_NORMAL 0 #define MENU_HIGHLIGHT 1 class LCD4884 { public: LCD4884(); void LCD_init(void); void LCD_backlight(unsigned char dat); void LCD_write_byte(unsigned char dat, unsigned char dat_type); void LCD_draw_bmp_pixel(unsigned char X,unsigned char Y,unsigned char *map,unsigned char Pix_x,unsigned char Pix_y); void LCD_write_string(unsigned char X,unsigned char Y,char *s, char mode); void LCD_write_chinese(unsigned char X, unsigned char Y,unsigned char *c,unsigned char ch_with,unsigned char num,unsigned char line,unsigned char row); void LCD_write_string_big ( unsigned char X,unsigned char Y, char *string, char mode ); void LCD_write_char_big (unsigned char X,unsigned char Y, unsigned char ch, char mode); void LCD_write_char(unsigned char c, char mode); void LCD_set_XY(unsigned char X, unsigned char Y); void LCD_clear(void); }; extern LCD4884 lcd; #endif //#include "LCD4884.h" #include "font_6x8.h" #include "font_big.h" extern "C" { #include <avr/pgmspace.h> #include <avr/io.h> #include "WConstants.h" } LCD4884::LCD4884() {}; LCD4884 lcd = LCD4884(); void LCD4884::LCD_backlight(unsigned char dat) { if(dat==1)PORTD |= (1<<LCD_BL); // turn on backlight else PORTD &= ~(1<<LCD_BL); } void LCD4884::LCD_init(void) { // LCD_RST = 0; DDRD |= (1<<SPI_CS)|(1<<LCD_DC)|(1<<LCD_RST)|(1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<LCD_BL); PORTD &= ~(1<<LCD_RST); delayMicroseconds(1); PORTD |= (1<<LCD_RST); PORTD &= ~(1<<SPI_CS); delayMicroseconds(1); PORTD |= (1<<SPI_CS); delayMicroseconds(1); PORTD |= (1<<LCD_BL); // turn on backlight LCD_write_byte(0x21, 0); LCD_write_byte(0xc0, 0); LCD_write_byte(0x06, 0); LCD_write_byte(0x13, 0); LCD_write_byte(0x20, 0); LCD_clear(); LCD_write_byte(0x0c, 0); PORTD &= ~(1<<SPI_CS); } void LCD4884::LCD_write_byte(unsigned char dat, unsigned char dat_type) { unsigned int i; PORTD &= ~(1<<SPI_CS); if (dat_type == 0) PORTD &= ~(1<<LCD_DC); else PORTD |= (1<<LCD_DC); for(i=0;i<8;i++) { if(dat&0x80) { PORTD |= (1<<SPI_MOSI); //SDIN = 1; } else { PORTD &= ~(1<<SPI_MOSI); //SDIN = 0; } PORTD &= ~(1<<SPI_SCK); //SCLK = 0; dat = dat << 1; PORTD |= (1<<SPI_SCK); //SCLK = 1; } PORTD |= (1<<SPI_CS); } void LCD4884::LCD_draw_bmp_pixel(unsigned char X,unsigned char Y,unsigned char *map, unsigned char Pix_x,unsigned char Pix_y) { unsigned int i,n; unsigned char row; if (Pix_y%8==0) row=Pix_y/8; else row=Pix_y/8+1; for (n=0;n<row;n++) { LCD_set_XY(X,Y); for(i=0; i<Pix_x; i++) { LCD_write_byte(map[i+n*Pix_x], 1); } Y++; } } void LCD4884::LCD_write_string(unsigned char X,unsigned char Y,char *s, char mode) { LCD_set_XY(X,Y); while (*s) { LCD_write_char(*s, mode); s++; } } void LCD4884::LCD_write_chinese(unsigned char X, unsigned char Y,unsigned char *c,unsigned char ch_with,unsigned char num,unsigned char line,unsigned char row) { unsigned char i,n; LCD_set_XY(X,Y); for (i=0;i<num;) { for (n=0; n<ch_with*2; n++) { if (n==ch_with) { if (i==0) LCD_set_XY(X,Y+1); else { LCD_set_XY((X+(ch_with+row)*i),Y+1); } } LCD_write_byte(c[(i*ch_with*2)+n],1); } i++; LCD_set_XY((X+(ch_with+row)*i),Y); } } void LCD4884::LCD_write_string_big ( unsigned char X,unsigned char Y, char *string, char mode ) { while ( *string ){ LCD_write_char_big( X, Y, *string , mode ); if(*string++ == '.') X += 5; else X += 12; } } /* write char in big font */ void LCD4884::LCD_write_char_big (unsigned char X,unsigned char Y, unsigned char ch, char mode) { unsigned char i, j; unsigned char *pFont; unsigned char ch_dat; pFont = (unsigned char *) big_number; if(ch == '.') ch = 10; else if (ch == '+') ch = 11; else if (ch == '-') ch = 12; else ch = ch & 0x0f; for(i=0;i<3;i++) { LCD_set_XY ( X, Y+i); for(j=0; j<16; j++){ ch_dat = pgm_read_byte(pFont+ch*48 + i*16 +j); LCD_write_byte( (mode == MENU_NORMAL)? ch_dat : (ch_dat^0xff), 1); } } } void LCD4884::LCD_write_char(unsigned char c, char mode) { unsigned char line; unsigned char *pFont; byte ch; pFont = (unsigned char *)font6_8; c -= 32; for (line=0; line<6; line++){ ch = pgm_read_byte(pFont+c*6+line); LCD_write_byte( (mode==MENU_NORMAL)? ch: (ch^ 0xff) , 1); } } void LCD4884::LCD_set_XY(unsigned char X, unsigned char Y) { LCD_write_byte(0x40 | Y, 0); LCD_write_byte(0x80 | X, 0); } void LCD4884::LCD_clear(void) { unsigned int i; LCD_write_byte(0x0c, 0); LCD_write_byte(0x80, 0); for (i=0; i<504; i++) // column // row LCD_write_byte(0, 1); }// big font #include <avr/pgmspace.h> //******* VERY LARGE FONTS ********** //used here for displaying temperature unsigned char big_number[13][3][16] PROGMEM = { 0,128,192,224,224,96,224,224, //'0' 192,128,0,0,0,0,0,0 , 112,255,255,1,0,0,0,0, 255,255,254,0,0,0,0,0 , 0,15,31,60,56,48,56,56, 31,15,3,0,0,0,0,0 , 0,0,0,0,128,224,224,0, 0,0,0,0,0,0,0,0 , 0,0,3,3,3,255,255,0, 0,0,0,0,0,0,0,0 , //'1' 0,0,56,56,56,63,63,56, 56,56,0,0,0,0,0,0 , 0,192,192,224,96,96,224,224, //'2' 192,128,0,0,0,0,0,0 , 0,1,0,0,128,192,224,249, 63,31,0,0,0,0,0,0 , 0,60,62,63,63,59,57,56, 56,56,56,0,0,0,0,0 , 0,192,224,224,96,96,224,224, //'3' 192,192,0,0,0,0,0,0 , 0,1,0,0,48,48,56,125, 239,207,0,0,0,0,0,0 , 0,28,56,56,48,48,56,60, 31,15,1,0,0,0,0,0 , 0,0,0,0,0,128,192,224, 224,0,0,0,0,0,0,0 , //'4' 224,240,248,222,207,199,193,255, 255,192,192,0,0,0,0,0 , 0,0,0,0,0,0,0,63, 63,0,0,0,0,0,0,0 , 0,224,224,224,224,224,224,224, //'5' 224,224,224,0,0,0,0,0 , 0,63,63,63,56,56,48,112, 240,224,0,0,0,0,0,0 , 0,28,56,56,48,48,56,60, 31,15,1,0,0,0,0,0 , 0,0,128,192,192,224,96,96, 224,224,0,0,0,0,0,0 , 224,254,255,55,57,24,24,56, 240,240,192,0,0,0,0,0 , 0,15,31,28,56,48,48,56, 31,15,7,0,0,0,0,0 , //'6' 0,224,224,224,224,224,224,224, //'7' 224,224,224,0,0,0,0,0 , 0,0,0,0,128,224,248,126, 31,7,1,0,0,0,0,0 , 0,0,56,62,31,7,1,0, 0,0,0,0,0,0,0,0 , 0,128,192,224,224,96,96,224, //'8' 192,192,0,0,0,0,0,0 , 0,207,255,127,56,48,112,112, 255,239,199,0,0,0,0,0 , 3,15,31,60,56,48,48,56, 31,31,15,0,0,0,0,0 , 0,128,192,224,224,96,224,224, 192,128,0,0,0,0,0,0 , 12,63,127,241,224,192,192,225, 255,255,254,0,0,0,0,0 , 0,0,56,48,48,56,56,30, //'9' 15,7,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0, //'.' 0,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 , 60,60,60,0,0,0,0,0, 0,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0, //'+' 0,0,0,0,0,0,0,0 , 0,0,64,64,64,64,64,254, 254,64,64,64,64,64,0,0 , 0,0,0,0,0,0,0,15, 15,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 , //'-' 0,64,64,64,64,64,64,0, 0,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 };// 6 x 8 font // 1 pixel space at left and bottom // index = ASCII - 32 #include <avr/pgmspace.h> unsigned char font6_8[][6] PROGMEM = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // sp { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // ! { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // " { 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // # { 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $ { 0x00, 0x62, 0x64, 0x08, 0x13, 0x23 }, // % { 0x00, 0x36, 0x49, 0x55, 0x22, 0x50 }, // & { 0x00, 0x00, 0x05, 0x03, 0x00, 0x00 }, // ' { 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 }, // ( { 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 }, // ) { 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 }, // * { 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 }, // + { 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 }, // , { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08 }, // { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 }, // . { 0x00, 0x20, 0x10, 0x08, 0x04, 0x02 }, // / { 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0 { 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1 { 0x00, 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2 { 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3 { 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4 { 0x00, 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5 { 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6 { 0x00, 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7 { 0x00, 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8 { 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9 { 0x00, 0x00, 0x36, 0x36, 0x00, 0x00 }, // : { 0x00, 0x00, 0x56, 0x36, 0x00, 0x00 }, // ; { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 }, // < { 0x00, 0x14, 0x14, 0x14, 0x14, 0x14 }, // = { 0x00, 0x00, 0x41, 0x22, 0x14, 0x08 }, // > { 0x00, 0x02, 0x01, 0x51, 0x09, 0x06 }, // ? { 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E }, // @ { 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C }, // A { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C { 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F { 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G { 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H { 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I { 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J { 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K { 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L { 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M { 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P { 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q { 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R { 0x00, 0x46, 0x49, 0x49, 0x49, 0x31 }, // S { 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T { 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U { 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V { 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F }, // W { 0x00, 0x63, 0x14, 0x08, 0x14, 0x63 }, // X { 0x00, 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y { 0x00, 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z { 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [ { 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55 }, // 55 { 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ] { 0x00, 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^ { 0x00, 0x40, 0x40, 0x40, 0x40, 0x40 }, // _ { 0x00, 0x00, 0x01, 0x02, 0x04, 0x00 }, // ' { 0x00, 0x20, 0x54, 0x54, 0x54, 0x78 }, // a { 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b { 0x00, 0x38, 0x44, 0x44, 0x44, 0x20 }, // c { 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F }, // d { 0x00, 0x38, 0x54, 0x54, 0x54, 0x18 }, // e { 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f { 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C }, // g { 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h { 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i { 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 }, // j { 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k { 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l { 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n { 0x00, 0x38, 0x44, 0x44, 0x44, 0x38 }, // o { 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 }, // p { 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC }, // q { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r { 0x00, 0x48, 0x54, 0x54, 0x54, 0x20 }, // s { 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t { 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u { 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v { 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w { 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 }, // x { 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C }, // y { 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z { 0x00,0x00, 0x06, 0x09, 0x09, 0x06 } // horiz lines }; Il linguaggio Processing Il linguaggio Processing è basato su Java. L’ambiente di sviluppo di Arduino è scritto in Processing. Processing deve essere installato dopo averlo scaricato dal sito di Arduino. La programmazione è più semplice del linguaggio Java usuale. E’ particolarmente adatto per programmi di grafica ed è stato usato anche in ambiente artistico. Per familiarizzarsi conviene caricare un esempio già pronto. Caricare dagli esempi, sezione Motion, Bounce. Premere Run. Avrete sperimentato un’applet Java. L'ambiente di programmazione PROCESSING in generale Processing è un ambiente di programmazione sviluppato al Media Lab del MIT, il cui scopo è di permettere ad artisti, designer ed in generale a persone con scarse cognizioni di informatica ma attratte dalle possibilità di applicazione dell'informatica all'arte e al design di poter scrivere dei programmi, più o meno sofisticati, in breve tempo, utilizzando uno strumento facile da usare ma sufficientemente potente per fare delle sperimentazioni nel settore della "computer art" e dell' "interaction design". Processing è un prodotto open source disponibile per le piattaforme Windows, Macintosh e Linux; lo si può scaricare dal sito ufficiale. . Processing è molto semplice da usare: si scrive il testo di un programma e lo si esegue (con il comando "run" o premendo il pulsante "Play" all'estrema sinistra della finestra dell'applicazione). I programmi scritti possono ovviamente essere salvati e successivamente modificati. Il tutto si fa in modo semplice e intuitivo, così che anche un utente inesperto possa imparare in pochi minuti ad usarlo. Java è un linguaggio relativamente recente ed è considerato il linguaggio più adatto per le applicazioni distribuite e per Internet. E’ scaricabile gratuitamente. Consente una programmazione procedurale classica, con sintassi simile al linguaggio C. E’ anche un linguaggio orientato agli oggetti, con sintassi simile al C++. La programmazione per oggetti non è semplicissima per chi non è abituato. Arduino con Processing rende le cose un po’ più facili. Ecco un esempio di programmazione procedurale per disegnare figure: void setup() { size (256,256); backgrond(0); } void draw() { stroke(255); strokeWeight(1); ellipseMode(CORNER); ellipse(72,100,110,130); triangle(88,100,168,100,128,50); stroke(140); beginShape(TRIANGLES); vertex(114,180); vertex(mouseX,mouseY); vertex(140,180); endShape(); strokeWeight(4); line(96,150,112,150); line(150,150,166,150); line(120,200,136,200); } E ora un esempio di programmazione orientata agli oggetti: Puppet pinocchio; void setup() { size(256,256); background(0); color tempcolor = color(255,0,0); pinocchio = new Puppet(tempcolor); } void draw() { background(0); pinocchio.draw(); } class Puppet { color colore; colore = c_; } void draw () { stroke(255); strokeWight(1); ellipseMode(CORNER); ellipse(72,100,110,130); stroke(colore); beginShape(TRIANGLES); vertex(114,180); vertex(mouseX,mouseY); vertex(140,180); endShape(); strokeWeight(4); line(96,150,112,150), line(150,150,166,150); } } Tipi di dati Variabili: se si riferiscono a tipi primitivi B = A provoca la copia di a in b. Se i dati sono oggetti e array, viene creato un puntatore. Scope: è l’ambito in cui si può accedere ad una variabile. Global scope significa che la variabile è visibile ovunque ed è dichiarata fuori da setup(). Local scope significa che è definita all’interno di un blocco di programma ed visibile solo al suo interno. Funzioni: come in C e C++ sono lo strumento principalee consentono una programmazione modulare. Classi e Oggetti Una classe è un insieme di dati e funzioni. Un oggetto è un’istanza di una classe. Processing su Arduino Le strutture di controllo di un linguaggio di programmazione sono dei costrutti (frasi) del linguaggio che controllano l’ordine di esecuzione delle istruzioni di un programma. Sequenza: le istruzioni sono eseguite in sequenza una dopo l’altra. Selezione: scelta dell’istruzione da eseguire tra più alternative. Iterazione: ripetizione (un certo numero di volte) di un gruppo di istruzioni. S e que nz a Sequenza : giustapposizione di istruzioni separate da “;” (punto e virgola) Una o più istruzioni possono essere raggruppate tra parentesi graffe {I1; …; In;} per formare una singola istruzione. Le istruzioni sono eseguite nell’ordine in cui appaiono: I1, … , In Esempio Il primo esempio di programma Processing disegna cinque rette oblique tra loro parallele. l'istruzione size(200, 200) crea una finestra di 200 per 200 pixel entro cui avviene il disegno, l'istruzione background(0) sceglie il nero come colore dello sfondo, l'istruzione stroke(255) sceglie il bianco come colore per il disegno delle linee, l'istruzione strokeWeight(5) imposta lo spessore delle linee a 5 pixel, l'istruzione smooth()arrotonda l'estremità delle linee. Ciascuna delle 5 linee di codice che seguono disegna una linea, ad es. l'istruzione line(10, 80, 30, 40)disegna una linea con estremi nei punti di coordinate (10, 80) e (30, 40). Il sistema di coordinate di Processing pone l'origine degli assi nell'angolo superiore sinistro della finestra di disegno, per cui spostandosi verso destra aumenta il valore della prima coordinata di un punto, mentre spostandosi verso il basso aumenta il valore della seconda coordinata. Si noti che l'immagine appare in bianco e nero, poiché è stato scelto di disegnare con immagini a livelli di grigio, scelti tra 256 tonalità diverse: 0 per il nero e 255 per il bianco. Ogni istruzione del programma è seguita da un commento, un testo inserito come documentazione del programma, che non viene considerato come codice, ma viene scartato durante l'esecuzione del programma. I commenti usati in questo programma sono monoriga, ovvero iniziano dopo i simboli "//" e terminano in fondo alla riga. Uso delle variabili Se si osserva attentamente il programma precedente, le cinque righe parallele sono disegnate incrementando di 10 unità la prima componente dei punti estremi di ogni retta. Il prossimo esempio differisce dal precedente per l'introduzione di una variabile intera di nome x a cui si assegna inizialmente il valore 0 con la dichiarazione int x = 0 e dopo il disegno di ogni linea è incrementata di 10 unità con il comando di assegnamento x=x+10 . Una dichiarazione ha il compito di creare una variabile stabilendo il nome e l'insieme dei valori che la variabile può assumere (ovvero il tipo della variabile, in questo caso di tipo int che denota i numeri interi), nonché di riservare nella memoria del calcolatore lo spazio necessario per memorizzare il valore corrente della variabile. In Processing come in Java e tanti altri linguaggi, una variabile non può essere usata se non è prima dichiarata. Invece in JavaScript la dichiarazione di una variabile non è necessaria. L'effetto finale del programma è sempre lo stesso, ma, a differenza del programma precedente, il codice mette bene in evidenza la relazione tra le coordinate delle 5 linee. I te r a z i one I costrutti di iterazione eseguono un certo numero di volte un'istruzione, detta corpo. Un costrutto di iterazione è anche detto istruzione di ciclo, o semplicemente ciclo. Noi esamineremo due tipi di costrutti di iterazione: comando while while (<espressione logica> ) I L'esecuzione di un comando while causa l'esecuzione dell'istruzione (il corpo del while) se l’espressione logica (il test di while) è vera, poi si valuta di nuovo l’espressione logica: se è vera si esegue di nuovo l’istruzione, e così via finché l’espressione logica diventa falsa. Esempio di uso del comando while Osservando il codice degli esempi precedenti, le cinque righe parallele sono disegnate incrementando di 10 unità la prima componente dei punti estremi di ogni retta. Si ha quindi una ripetizione (5 volte) dell'esecuzione del comando di tracciatura delle linee. La tracciatura delle linee può avvenire senza scrivere 5 volte lo stesso comando, usando il comando while. Il corpo del ciclo while disegna una linea ed incrementa il valore della variabile intera x di 10 unità. Pertanto, la variabile x assume i valori 0, 10, 20, 30, 40, in corrispondenza dei quali una linea viene disegnata. Quando assume il valore 50, la guardia del ciclo while diventa falsa e l'esecuzione del ciclo termina. La figura seguente mostra come il ciclo while possa essere usato per calcolare i numeri triangolari, ovvero i numeri interi positivi ottenuti come somma dei numeri interi compresi tra 1 e un intero positivo n. Il programma funziona in modo analogo. comando for for (I1 ; E ; I2 ) I3 I1 è l’istruzione di inizializzazione; E è il test del for ; I2 è l’istruzione di incremento o aggiornamento; I3 è il corpo del for ; l'esecuzione di un comando for avviene come segue: innanzi tutto è eseguita l'istruzione I1 di inizializzazione (che di solito assegna un valore ad una variabile, detta variabile di controllo del ciclo for); se la guardia è vera si esegue il corpo I3 e l’aggiornamento I2 , poi si valuta di nuovo la guardia: se è vera si esegue di nuovo il corpo e l’aggiornamento, finché la guardia diventa falsa. Esempio di uso del comando for Questo esempio è una riscrittura del programma precedente usando il ciclo for. Si noti che l'istruzione che assegna 0 alla variabile x è l'istruzione di inizializzazione del ciclo for, mentre l'istruzione che incrementa di 10 unità il valore di x è l'istruzione di aggiornamento del ciclo for. Il corpo del for è costituito dalla sola istruzione di tracciatura di una linea e viene ripetuto 5 volte. Attenzione: le istruzioni di ciclo non sono solo dei semplici sostituti del comando di sequenzializzazione che permettono di abbreviare i programmi, ma permettono di fare molto di più. Se non usiamo i cicli e vogliamo disegnare 10 righe anziché 5, dobbiamo modificare il programma, aggiungendo 5 nuovi comandi di disegno. Il prossimo esempio mostra come con le istruzioni di iterazione sia possibile disegnare un numero di linee che dipende dal numero di secondi del tempo in cui si lancia l'esecuzione del programma: non è pertanto stabilire a priori quante linee si devono disegnare e un tale programma non si può realizzare con la sola sequenzializzazione di comandi. Alla variabile intera s è assegnato il numero dei secondi letti dall'orologio del calcolatore. Nell'immagine precedente il testo che appare nella finestra del terminale indica che il valore assegnato ad s è 4. Il programma disegnerà pertanto quattro righe, come mostrato nella figura seguente: Il corpo line(5*x+10, 40, 5*x+10, 80); del comando for successivo che esegue la tracciatura di una linea verticale è eseguito un numero di volte pari al valore della variabile s: infatti, la variabile x di conteggio del for è inizializzata a 0 e fintanto che il suo valore è minore di quello di s, si traccia una linea e si incrementa il valore di x. In generale, se s è una variabile intera con un valore definito, l'esecuzione del ciclo for(int x /* = corpo 0; del x<s; for x=x+1){ */ }; provoca l'esecuzione ripetuta s volte del corpo del for. Con le istruzioni di ciclo, si può incappare nel cosidetto "fencepost error" (errore dello steccato e dei pali), che consiste nell'eseguire il corpo del ciclo una volta in più o in meno del numero corretto di iterazioni. Per essere sicuri che il programma funzioni correttamente, provate ad assegnare dei valori piccoli alla variabile s, ad es. 0 o 1 e vedete cosa succede: se le prove hanno successo, allora il programma dovrebbe essere corretto. Per concludere l'analisi di quest'esempio, si noti il commento multiriga in fondo al codice. Si tratta di un tipo di commento che inizia con i simboli "/*" , termina con i simboli "*/" e può occupare un numero arbitrario di righe di testo. S e l e z i one I costrutti di selezione scelgono di eseguire un'istruzione piuttosto che un'altra in base al valore di un'espressione logica ( espressione che assume solo due valori, vero o falso, e pertanto una tale espressione è anche detta espressione booleana, da George Boole, matematico e logico del diciannovesimo secolo). Noi esamineremo due tipi di costrutti di selezione: comando if if (<espressione logica> ) I1 L'esecuzione di tale comando causa l'esecuzione dell'istruzione I1 se l’espressione logica (detta guardia) è vera Esempio di uso del comando if Il seguente programma disegna 5 righe parallele di colore grigio se, al momento dell'esecuzione del programma, l'orologio del calcolatore indica un'orario in cui il valore dei secondi è inferiore a 30; in caso contrario, le righe sono di colore bianco. La decisione di quale colore applicare è fatta con il costrutto if. Il colore delle linee da tracciare viene preimpostato con la terza riga di codice stroke(255) che sceglie il colore bianco. Il successivo comando if if (s<30){ stroke(128); // Set line value to gray }; imposta con l'istruzione stroke(128)il colore grigio come colore di tracciatura se il numero dei secondi, contenenuto nella variabiles è minore di 30. comando if-else if (<espressione logica> ) I1 else I2 L'esecuzione di tale comando causa l'esecuzione dell'istruzione I1 se l’espressione logica è vera; dell'istruzione I2 in caso contrario. Esempio di uso del comando if-else Il seguente programma disegna 5 righe parallele di colore grigio se, al momento dell'esecuzione del programma, l'orologio del calcolatore indica un'orario in cui il valore dei secondi è inferiore a 30; in caso contrario, le righe sono di colore bianco. La decisione di quale colore applicare è fatta con il costrutto if-else A differenza dell'esempio precedente, il colore delle linee da tracciare non viene preimpostato, ma viene determinato dal comando if-else if (s<30){ stroke(128); // Set line value to gray } else { stroke(255); // Set line value to white }; che imposta con l'istruzione stroke(128)il colore grigio come colore di tracciatura se il numero dei secondi, contenenuto nella variabiles è minore di 30, mentre in caso contrario il colore di tracciatura è impostato al colore bianco con l'istruzionestroke(255). P r i m o es e m pi o di gr a fi c a 2 D Il prossimo semplicissimo programma mostra come disegnare semplici figure geometriche. Per un approfondimento, si veda il link GRAFICA 2D e per informazioni sull'uso delle primitive grafiche 2D (istruzioni per il disegno di figure geometriche bidimensionali, si consulti l'elenco denominato "2D Primitives" alla pagina del manuale di riferimento di Processing . Il programma è quasi autoesplicativo. Gli unici commenti riguardano i comandistroke() e fill(), che stabiliscono il colore di disegno dei contorni e rispettivamente dell'area interna di una figura. I tre numeri interi che costituiscono gli argomenti di queste istruzioni rappresentano un colore in formato RGB (Red, Green, Blue) . Al contrario, i metodi noStroke() e noFill() inibiscono il disegno del contorno o dell'interno di una figura, fino all'esecuzione di un nuovo comando stroke() o fill().Il comando bezier() disegna una curva di Beziér, definita da quattro punti: i punti estremi e due punti di controllo che ne determinano la curvatura. Per comprendere meglio le curve di Beziér, segui questo link. Per determinare il codice RGB di un colore o viceversa per determinare il colore di una tripla RGB di numeri interi, si può usare nel menu "Tools" di Processing l'opzione "Color selector". Il risultato dell'esecuzione del programma è il seguente Note s ul l ' es e c uzione de i pr ogr a mm i Come abbiamo visto, Processing dà la possibilità di scrivere ed eseguire programmi in modo immediato. Il codice dei programmi scritti è memorizzato in un file con estensione ".pde" (sigla di Processing Development Environment). Al momento della sua esecuzione, tale file viene tradotto in un codice detto bytecode, contenuto insieme ad altri file all'interno di un file con estensione ".jar" (sigla di Java ARchive). Il bytecode all'interno del file jar viene eseguito dall'interprete Java, detto anche JRE (sigla di Java Run-time Environment) o JVM (sigla di Java Virtual Machine). Le fasi di traduzione (detta comunemente compilazione) del codice sorgente (il file pde) e di esecuzione del bytecode avvengono in modo trasparente per l'utente. Tutti i file prodotti mantengono lo stesso nome, cambiando solo l'estensione. In realtà, il bytecode prodotto da Processing è un programma di tipo speciale, detto applet, che può essere inserito all'interno di una pagina web con un'apposito marcatore <applet>. In questo modo, il programma una volta compilato è inserito all'interno di una pagina web e, al momento della visualizzazione della pagina, l'applet è eseguito. L'unica cosa necessaria per la visualizzazione degli applet è che sul calcolatore sia installato l'interprete Java, (JRE o JVM). Attenzione, Processing crea l'applet solo se si esegue il comando "export". In tal caso, esso genera il bytecode dell'applet ed una semplice pagina web di nome index.html contenente un riferimento all'applet mediante il marcatore omonimo. Di seguito sono elencati i collegamenti alle pagine web contenenti gli esempi visti finora, più alcuni esempi supplementari. Processing può anche creare un'applicazione se si esegue il comando "export application". In tal caso, esso genera oltre al bytecode dell'applet un file eseguibile che avvia a sua volta l'esecuzione dell'applet. L'applicazione può essere generata per i sistemi Windows, Mac e Linux. Esempi vari Ecco il programma Rimbalza (bounce) /** * Bounce. * * When the shape hits the edge of the window, it reverses its direction. * * Updated 1 September 2002 */ import processing.serial.*; String portname = "/dev/tty.usbserial-A4001qa8"; Serial port; // Create object from Serial class int size = 60; // Width of the shape float xpos, ypos; // Starting position of shape float xspeed = 3.9; // Speed of the shape float yspeed = 3.1; // Speed of the shape int xdirection = 1; // Left or Right int ydirection = 1; // Top to Bottom void setup() { size(400, 400); colorMode(HSB, 255); noStroke(); frameRate(30); ellipseMode(CENTER); // draw from center out smooth(); // Set the starting position of the shape xpos = width/2; ypos = height/2; // Open the port that the board is connected to and use the same speed (19200 bps) port = new Serial(this, portname, 19200); } void draw() { if (port.available() > 0) { // If data is available, size = port.read(); // read it and store it as the new size } background(102); // Update the position of the shape xpos = xpos + ( xspeed * xdirection ); ypos = ypos + ( yspeed * ydirection ); // Test to see if the shape exceeds the boundaries of the screen // If it does, reverse its direction by multiplying by -1 int halfsize = size/2; // because we're drawing from the circle's center if (xpos + halfsize > width || xpos - halfsize < 0) { xdirection *= -1; } if (ypos + halfsize > height || ypos - halfsize < 0) { ydirection *= -1; } // Draw the shape fill(size,255,255); // we're in HSB mode, so first value is color ellipse(xpos, ypos, size, size); } Ora un gioco (Pong) /** * * When the shape hits the edge of the window, it reverses its direction. * * Updated 1 September 2002 */ import processing.serial.*; String portname = "/dev/tty.usbserial-A4001qa8"; Serial port; // Create object from Serial class int size = 60; // Width of the shape float xpos, ypos; // Starting position of shape float xspeed = 3.9; // Speed of the shape float yspeed = 3.1; // Speed of the shape int xdirection = 1; // Left or Right int ydirection = 1; // Top to Bottom void setup() { size(400, 400); colorMode(HSB, 255); noStroke(); frameRate(30); ellipseMode(CENTER); // draw from center out smooth(); // Set the starting position of the shape xpos = width/2; ypos = height/2; // Open the port that the board is connected to and use the same speed (19200 bps) port = new Serial(this, portname, 19200); } void draw() { if (port.available() > 0) { // If data is available, size = port.read(); // read it and store it as the new size } background(102); // Update the position of the shape xpos = xpos + ( xspeed * xdirection ); ypos = ypos + ( yspeed * ydirection ); // Test to see if the shape exceeds the boundaries of the screen // If it does, reverse its direction by multiplying by -1 int halfsize = size/2; // because we're drawing from the circle's center if (xpos + halfsize > width || xpos - halfsize < 0) { xdirection *= -1; } if (ypos + halfsize > height || ypos - halfsize < 0) { ydirection *= -1; } // Draw the shape fill(size,255,255); // we're in HSB mode, so first value is color ellipse(xpos, ypos, size, size); } Ecco un altro esempio * Simple Read * * Read data from the serial port and change the color of a rectangle * when a switch connected to a Wiring or Arduino board is pressed and released. * This example works with the Wiring / Arduino program that follows below. */ import processing.serial.*; String portname = "/dev/tty.usbserial-A4001qa8"; Serial port; // Create object from Serial class int val=100; // Data received from the serial port, with an initial value void setup() { size(400, 400); colorMode(HSB, 255); ellipseMode(CENTER); noStroke(); frameRate(30); smooth(); // draw from center out // Open the port the board is connected to port = new Serial(this, portname, 19200); } void draw() // or "COM8" { if (port.available() > 0) { // If data is available, val = port.read(); // read it and store it in val } background(99); // Draw the shape fill(val,255,255); // we're in HSB mode, so first value is color ellipse(width/2, height/2, 250,250); } Ora Arduino suona! * Arduino Sounds * * Play WAV or MP3 files when piezo knocks from an Arduino running the * "PiezoKnock" sketch or when a computer keyboard key is pressed. * * Taken from the Minim "trigger" sketch: * * This sketch demonstrates how to use the <code>trigger</code> method of an <code>AudioSample</code>. <br /> * <code>AudioSample</code>s can only be triggered, not cue'd and looped * or anything else you might do with an <code>Playable</code> object. The advantage, however, is that * an <code>AudioSample</code> can be retriggered while it is still playing, which will cause the sample to * overlap with itself . */ import ddf.minim.*; import processing.serial.*; String portname = "/dev/tty.usbserial-A4001qa8"; Serial port; // Create object from Serial class // or "COM8" AudioSample sounds[]; String sound_names[] = { "cat.wav", "fx.mp3", "electric_wrench.wav", "wehoa.mp3", "oriental_gong_2.wav", "yipee.wav", "car_brake.wav" // find more wav or mp3 files and put them in the "data" directory }; void setup() { size(400, 400); background(0); stroke(255); // always start Minim before you do anything with it Minim.start(this); Minim.debugOn(); sounds = new AudioSample[sound_names.length]; for( int i=0; i< sound_names.length; i++ ) { sounds[i] = Minim.loadSample(sound_names[i], 512); } // Open the port that the board is connected to and use the same speed (19200 bps) port = new Serial(this, portname, 19200); } void draw() { // do the drawing on events } void soundball() { int r = int(random(sounds.length)); println("picked sound #"+r); sounds[r].trigger(); // play a random sound int x = int(random(0,300)); int y = int(random(0,300)); fill(240,0,0); ellipse(x,y, 40,40); fill(30,0,0); ellipse(x,y, 8,8); } void serialEvent(Serial p) { char inByte = port.readChar(); println("received char: "+ inByte); if( inByte == '!' ) { // '!' is end of "knock!" soundball(); } } void keyPressed() { if(key == ' ') { background(40,40,40); } soundball(); } // erase screen void stop() { // always close Minim audio classes when you are done with them for( int i=0; i<sounds.length; i++ ) { sounds[i].close(); } super.stop(); } Bibliografia -M. Banzi, Getting started with Arduino, O’Reilly, Cabridge,Beijing, 2009 -Tod E. Kurt, Bionic Arduino, MachineProject, 2007 -S. Monk, 30 Arduino Projects, McGrawHill, New York,2010 -M. Schmidt, Arduino, A Quick-Start Giude, The Pragmatic Programmers, 2011 -C. Reas and Ben Fry, Getting Started with Processing, O’Reilly, 2010 -www.arduino.cc -www.webalice.it -www.shiffman.net/itp Grazie per l’attenzione. Buon apprendimento.