![12 Numerazione capitolo](12-num-capitolo.png) ## 12 - KNOCK LOCK **CREA LA TUA SERRATURA SEGRETA PER TENERE LONTANI GLI OSPITI INDESIDERATI!** > **Scopri:** input con un piezo, scrivere le funzioni > > **Tempo:** 1 ORA > **Livello:** ■ ■ ■ ■ □ > **Basato sui progetti:** 1, 2, 3, 4, 5 **Il *piezo*, che hai usato per riprodurre suoni nei progetti theremin e tastiera musicale, può anche essere utilizzato come dispositivo di ingresso. Quando è collegato a 5V, il sensore può rilevare le vibrazioni misurabili dagli ingressi analogici di Arduino. Avrai bisogno di collegare una resistenza di alto valore (come 1 mega ohm) come riferimento a massa per far funzionare tutto correttamente.** ![Ingredienti](12-ingredienti.png) >**INGREDIENTI** >* Servomotore >* Piezo >* Pulsante >* LED (Rosso, Giallo, Verde) >* Resistenza da 10 Kilo Ohm >* Resistenza da 220 Ohm (x3) >* Resistenza da 1 Mega Ohm >* Condensatore da 100 uF >* Piedini del connettore a pettine (3 piedini) Quando il piezo viene premuto contro una superficie solida in grado di vibrare, come un tavolo in legno, Arduino può percepire l'intensità del colpo. Utilizzando queste informazioni puoi contare quante volte è stato bussato in un intervallo prestabilito. Nel codice è possibile monitorare il numero di colpi e vedere se corrispondono alle tue impostazioni. Un interruttore ti permetterà di bloccare il motore in posizione. Alcuni LED ti daranno lo stato: un LED rosso indica che la scatola è chiusa a chiave, un LED verde indica che è sbloccata e un LED giallo permette di sapere se è stato ricevuto un colpo valido. Scriverai anche una funzione che ti consente di sapere se il colpo è troppo forte o troppo leggero. Scrivere funzioni consente di risparmiare tempo riutilizzando il codice invece di riscriverlo molte volte. Le funzioni possono avere dei parametri e restituire valori. In questo caso, fornirai alla funzione il volume del colpo. Se è nell'intervallo giusto, incrementi una variabile. È possibile costruire anche solo il circuito, ma è molto più divertente se lo si utilizza come strumento per chiudere a chiave qualcosa. Se hai una scatola di legno o di cartone puoi tagliarci dei fori e utilizzare il servomotore per aprire e chiudere un chiavistello, tenendo le persone lontane dalle tue cose. ### COSTRUISCI IL CIRCUITO Ci sono molte connessioni sulla scheda, assicurati di tenere traccia di come sono collegate le cose. ![Schema del circuito e montaggio](12-immagine_circuito_knock_lock.png) 1. Collega l'alimentazione e la massa su entrambi i lati della breadboard. Posiziona il pulsante sulla breadboard e collega una estremità a 5V. Collega l'altro lato dell'interruttore a massa attraverso una resistenza da 10 kilo ohm. Collega questo punto di incrocio al piedino digitale 2 di Arduino. 2. Collega i fili dal piezo alla breadboard. Collega un filo all'alimentazione. Se il piezo ha un filo rosso o uno contrassegnato con un "+", è quello da collegare all'alimentazione. Se il piezo non indica la polarità, allora si può collegare in entrambi i modi. Collega l'altra estremità del piezo al piedino analogico 0 di Arduino. Colloca una resistenza da 1 mega ohm tra la massa e l'altro filo. Valori di resistenza più bassi rendono il piezo meno sensibile alle vibrazioni. 3. Collega i LED, connettendo i catodi (piedino corto) a massa, e mettendo una resistenza da 220 ohm in serie con gli anodi. Attraverso le loro rispettive resistenze, collega il LED giallo al piedino digitale 3 di Arduino, il LED verde al piedino digitale 4 e il LED rosso al piedino digitale 5. 4. Inserisci i piedini del connettore a pettine nel connettore del servomotore (vedi la Fig. 3). Collega il filo rosso all'alimentazione e quello nero a massa. Metti un condensatore elettrolitico da 100 uF tra l'alimentazione e la massa per compensare eventuali irregolarità nella tensione e assicurati di aver collegato correttamente la polarità del condensatore. Collega il cavo dei dati del servo al piedino 9 di Arduino. ![Schema elettrico](12-schema-elettrico_knock_lock.png) ![COnnettori](12-connettori.png) *Fig.3: Il servomotore è dotato di un connettore femmina, quindi hai bisogno di aggiungere piedini maschi per collegarlo alla breadboard.* ### IL CODICE #### La libreria Servo Come nel progetto 'Indicatore d'umore', è necessario importare la libreria `Servo` e creare un'istanza per utilizzare il motore. ```cpp #include Servo myServo; ``` #### Costanti utili Crea le costanti per dare un nome ai tuoi ingressi e uscite. ```cpp const int piezo = A0; const int switchPin = 2; const int yellowLed = 3; const int greenLed = 4; const int redLed = 5; ``` #### Variabili per memorizzare i valori Crea le variabili per memorizzare i valori dell'interruttore e del piezo. ```cpp int knockVal; int switchVal; ``` #### Soglie di rilevamento Crea delle costanti da usare come soglie per i livelli minimo e massimo dei colpi. ```cpp const int quietKnock = 10; const int loudKnock = 100; ``` #### Variabili per lo stato di blocco La variabile `locked` ti consente di sapere se la serratura è chiusa o no. Il `boolean` è un tipo di dato che può solo essere vero (1) o falso (0). Dovresti iniziare con la serratura sbloccata. L'ultima variabile globale contiene il numero di colpi validi che hai rilevato. ```cpp boolean locked = false; int numberOfKnocks = 0; ``` #### Imposta la direzione dei piedini Nel `setup()`, attacca il servo al piedino 9. Imposta i piedini del LED come uscite e i piedini dell'interruttore come ingressi. ```cpp void setup(){ myServo.attach(9); pinMode(yellowLed, OUTPUT); pinMode(redLed, OUTPUT); pinMode(greenLed, OUTPUT); pinMode(switchPin, INPUT); Serial.begin(9600); ``` #### Sblocco Avvia la comunicazione seriale con il computer in modo da poter controllare il volume del colpo, lo stato corrente del blocco e quanti colpi mancano allo sblocco. Accendi il LED verde, muovi il servo nella posizione di sblocco e stampa lo stato attuale sul monitor seriale indicando che il circuito è nella posizione sbloccata. ```cpp digitalWrite(greenLed, HIGH); myServo.write(0); Serial.println("The box is unlocked!"); } ``` #### Controlla l'interruttore Nel `loop()`, per prima cosa verifica se la scatola è bloccata o no. Questo determina ciò che accade nel resto del programma. Se è bloccata, leggi il valore dell'interruttore. ```cpp void loop(){ if(locked == false){ switchVal = digitalRead(switchPin); ``` #### Blocco Se l'interruttore è chiuso (lo stai premendo), modifica la variabile `locked` a `true`, indicando che il blocco è innestato. Spegni il LED verde e accendi il LED rosso. Se non hai acceso il monitor seriale, questo è un utile feedback visivo per informarti dello stato del blocco. Sposta il servo in posizione di blocco e stampa un messaggio sul monitor seriale che indica che la scatola è bloccata. Aggiungi un ritardo in modo che la serratura abbia tempo per cambiare posizione. ```cpp if(switchVal == HIGH){ locked = true; digitalWrite(greenLed,LOW); digitalWrite(redLed,HIGH); myServo.write(90); Serial.println("The box is locked!"); delay (1000); } } ``` #### Controlla il sensore del colpo Se la variabile `locked` è vera, ed è attivo il blocco, leggi l'intensità della vibrazione del piezo e memorizzala in `knockVal`. ```cpp if(locked == true){ knockVal = analogRead(piezo); ``` #### Conta solo i colpi validi L'istruzione successiva verifica se hai meno di tre colpi validi e se ci sono vibrazioni sul sensore. Se questi sono entrambi veri, controlla per vedere se il colpo corrente è valido o no e incrementa la variabile `numberOfKnocks`. Qui chiami la funzione `checkForKnocks()`. Scriverai la funzione una volta che hai finito il `loop()`, ma sai già che stai per chiedere se si tratta di un colpo valido perciò passa `knockVal` come parametro. Dopo aver verificato il risultato della funzione, stampa il numero di colpi di cui hai ancora bisogno. ```cpp if(numberOfKnocks < 3 && knockVal > 0){ if(checkForKnock(knockVal) == true){ numberOfKnocks++; } Serial.print(3-numberOfKnocks); Serial.println("more knocks to go"); } ``` #### Sblocco (verifica finale) Controlla se hai tre o più colpi validi. In questo caso, cambia la variabile `locked` su falso e sposta il servo nella posizione di sblocco. Attendi pochi millesimi di secondi per dargli tempo di muoversi e modifica lo stato dei LED verde e rosso. Stampa un messaggio di stato sul monitor seriale, indicando che la scatola è sbloccata. ```cpp if(numberOfKnocks >= 3){ locked = false; myServo.write(0); delay(20); digitalWrite(greenLed,HIGH); digitalWrite(redLed,LOW); Serial.println("The box is unlocked!"); } } } ``` #### Definisci una funzione per verificare la validità dei colpi È ora di scrivere la funzione `checkForKnock()`. Quando la stai scrivendo, hai bisogno di indicare se restituirà un valore o no. Se non hai intenzione di restituire un valore, lo dichiari come tipo `void`, simile alle funzioni `loop()` e `setup()`. Se vuoi restituire un valore, è necessario dichiarare quale tipo avrà (`int`, `long`, `float`, etc.). In questo caso, verifica se il colpo è valido (vero) o no (falso). Dichiara la funzione come tipo `boolean`. ```cpp boolean checkForKnock(int value){ ``` #### Controlla la validità dei colpi Questa particolare funzione verificherà un numero (la tua variabile `knockVal`) per vedere se è valido o no. Per passare questa variabile alla funzione, crea un parametro quando dichiari la funzione. Nella funzione, ogni volta che ti riferisci a `value` conterrà il numero ricevuto come argomento dal programma principale. A questo punto `value` è impostato al valore della variabile `knockVal`. Controlla se `value` è maggiore del colpo debole e inferiore del colpo forte. ```cpp if(value > quietKnock && value < loudKnock){ ``` #### Indica se il colpo è valido Se il valore è tra questi due, è un bussare valido. Fai lampeggiare una volta il LED giallo e stampa il valore del colpo sul monitor seriale. ```cpp digitalWrite(yellowLed, HIGH); delay(50); digitalWrite(yellowLed, LOW); Serial.print("Valid knock of value "); Serial.println(value); ``` #### La funzione restituisce true o false Per informare il programma principale dell'esito del confronto, utilizza il comando `return`, che termina anche la funzione: una volta eseguito, si ritorna al programma principale. Se `value` è fuori dall'intervallo previsto, stampa il valore sul monitor seriale e la funzione restituisce `false`. ```cpp return true; } else { Serial.print("Bad knock value "); Serial.println(value); return false; } } ``` ### USALO Quando colleghi il circuito ad Arduino, apri il monitor seriale. Dovresti vedere accendersi il LED verde e il servo si sposta nella posizione di sblocco. Il monitor seriale dovrebbe stampare "The box is unlocked!". Probabilmente senti che il piezo fa un piccolo clic quando viene alimentato. Prova a bussare delicatamente e forte per vedere quale intensità del colpo attiva la funzione. Saprai che sta funzionando quando il LED giallo lampeggia e il monitor seriale ti dice che hai ricevuto un colpo valido e il suo valore. Ti permette anche di sapere il numero di colpi che mancano per sbloccare la scatola. Una volta raggiunto il giusto numero di colpi, la luce rossa si spegne, la luce verde si accende, il servo si sposta di 90 gradi e il monitor seriale ti informa che il blocco è disinnestato. ![FOrbici e colla](forbici-colla.png) > I valori per il colpo ideale possono variare da quelli dell'esempio. Questo dipende da un certo numero di variabili, come il tipo di superficie su cui è fissato il sensore e come è fissato. Utilizzando il monitor seriale e l'esempio `AnalogInSerialOut` nell'IDE di Arduino, trovi un valore del colpo adatto alla tua configurazione. Qui trovi una spiegazione dettagliata di questo esempio: [arduino.cc/analogtoserial](http://arduino.cc/analogtoserial) > > Se inserisci il progetto in una scatola, è necessario fare i fori per i LED e l'interruttore. Avrai anche bisogno di fare un fermo in cui inserire il servomotore. È utile avere un foro per farci passare il cavo USB per scoprire il grado di sensibilità ai colpi del nuovo ambiente. Potresti aver bisogno di riorganizzare la breadboard e Arduino o saldare i LED e l'interruttore per renderli accessibili dall'esterno del contenitore. La saldatura è un processo di unione di due o più componenti metallici con una lega che viene fusa sulla giunzione. Se non hai mai saldato prima, chiedi a qualcuno che ha esperienza di aiutarti o fai un po' di pratica sul filo di scarto prima di tentare con i componenti di questo progetto. La saldatura è una connessione permanente, quindi assicurati di poter hackerare ciò che stai usando. Guarda [arduino.cc/soldering](http://arduino.cc/soldering) per una buona spiegazione di come saldare. ![Istruzioni per l'installazione nella scatola 1](12-immagine_assemblaggio_scatola_1.png) ![Istruzioni per l'installazione nella scatola 2](12-immagine_assemblaggio_scatola_2.png) *1. Taglia 2 fori nella scatola: uno su un lato e uno sul coperchio. Posiziona il servo nella scatola in modo che il braccio possa muoversi dentro e fuori i fori quando la scatola è chiusa.* *2. Fissa il servo in una posizione con del nastro adesivo in modo che il braccio possa ruotare facilmente attraverso la fessura che hai fatto.* ![Lente](lente.png) > Scrivere le proprie funzioni non solo permette di controllare il flusso del codice più facilmente, ma aiuta anche a mantenerlo leggibile quando i progetti diventano sempre più grandi. Nel corso del tempo, più codice scrivi e più troverai funzioni che è possibile riutilizzare in diversi progetti, rendendo il processo più veloce e vicino al tuo stile di programmazione. ![Cervello](cervello.png) > Questo esempio conta semplicemente il giusto numero di colpi, non importa quanto tempo è necessario. Si può fare un progetto più complesso creando un timer con la funzione `millis()`. Usa il timer per identificare se il colpo accade in un determinato periodo di tempo. Guarda di nuovo il progetto della clessidra digitale per un esempio di come funziona un timer. Non sei limitato a trovare i colpi in un intervallo specifico. Puoi rilevare schemi complessi di colpi in base alla quantità di vibrazioni e di tempo. C'è una serie di esempi online su come effettuare questa operazione, cerca "Arduino knock lock" per scoprirli. **Gli elementi piezo possono essere utilizzati come ingressi quando sono collegati come divisori di tensione con una resistenza di valore elevato. Progettare una funzione è un modo semplice per scrivere codice che può essere riutilizzato per compiti specifici.** ### CODICE COMPLETO ```cpp /* Arduino Starter Kit example Project 12 - Knock Lock This sketch is written to accompany Project 12 in the Arduino Starter Kit Parts required: - 1 megohm resistor - 10 kilohm resistor - three 220 ohm resistors - piezo - servo motor - push button - one red LED - one yellow LED - one green LED - 100 uF capacitor created 18 Sep 2012 by Scott Fitzgerald Thanks to Federico Vanzati for improvements https://store.arduino.cc/genuino-starter-kit This example code is part of the public domain. */ // import the library #include // create an instance of the Servo library Servo myServo; const int piezo = A0; // pin the piezo is attached to const int switchPin = 2; // pin the switch is attached to const int yellowLed = 3; // pin the yellow LED is attached to const int greenLed = 4; // pin the green LED is attached to const int redLed = 5; // pin the red LED is attached to // variable for the piezo value int knockVal; // variable for the switch value int switchVal; // variables for the high and low limits of the knock value const int quietKnock = 10; const int loudKnock = 100; // variable to indicate if locked or not bool locked = false; // how many valid knocks you've received int numberOfKnocks = 0; void setup() { // attach the servo to pin 9 myServo.attach(9); // make the LED pins outputs pinMode(yellowLed, OUTPUT); pinMode(redLed, OUTPUT); pinMode(greenLed, OUTPUT); // set the switch pin as an input pinMode(switchPin, INPUT); // start serial communication for debugging Serial.begin(9600); // turn the green LED on digitalWrite(greenLed, HIGH); // move the servo to the unlocked position myServo.write(0); // print status to the Serial Monitor Serial.println("the box is unlocked!"); } void loop() { // if the box is unlocked if (locked == false) { // read the value of the switch pin switchVal = digitalRead(switchPin); // if the button is pressed, lock the box if (switchVal == HIGH) { // set the locked variable to "true" locked = true; // change the status LEDs digitalWrite(greenLed, LOW); digitalWrite(redLed, HIGH); // move the servo to the locked position myServo.write(90); // print out status Serial.println("the box is locked!"); // wait for the servo to move into position delay(1000); } } // if the box is locked if (locked == true) { // check the value of the piezo knockVal = analogRead(piezo); // if there are not enough valid knocks if (numberOfKnocks < 3 && knockVal > 0) { // check to see if the knock is in range if (checkForKnock(knockVal) == true) { // increment the number of valid knocks numberOfKnocks++; } // print status of knocks Serial.print(3 - numberOfKnocks); Serial.println(" more knocks to go"); } // if there are three knocks if (numberOfKnocks >= 3) { // unlock the box locked = false; // move the servo to the unlocked position myServo.write(0); // wait for it to move delay(20); // change status LEDs digitalWrite(greenLed, HIGH); digitalWrite(redLed, LOW); Serial.println("the box is unlocked!"); numberOfKnocks = 0; } } } // this function checks to see if a detected knock is within max and min range bool checkForKnock(int value) { // if the value of the knock is greater than the minimum, and larger // than the maximum if (value > quietKnock && value < loudKnock) { // turn the status LED on digitalWrite(yellowLed, HIGH); delay(50); digitalWrite(yellowLed, LOW); // print out the status Serial.print("Valid knock of value "); Serial.println(value); // return true return true; } // if the knock is not within range else { // print status Serial.print("Bad knock value "); Serial.println(value); // return false return false; } } ```