La notte di Halloween mettete questa zucca stregata fuori dalla vostra porta e, con luci e suoni spaventosi, farete saltare di paura chiunque si avvicini! Potrete personalizzare questo progetto secondo la vostra fantasia: il codice scritto per Arduino è facilmente modificabile e potrete memorizzare sulla SDcard i suoni più terrificanti, mentre un sensore di distanza nascosto nel naso della zucca sarà pronto a cogliere di sorpresa i golosi!

Cosa occorre

  • 1x scheda Arduino UNO
  • 1x Wave Shield per Arduino
  • 1x SDcard
  • 1x piccolo altoparlante da 8Ohm Ø 7cm
  • 1x sensore ad ultrasuoni LV MaxSonar EZ1 – MB1010
  • LED rossi o arancioni con resistenze
  • 1x zucca di plastica cava (o un oggetto simile) + una ciotola di plastica
  • Cavetti di collegamento
  • Adesivi e accessori vari per decorare la zucca

Questo progetto non presenta particolari difficoltà, richiede unicamente una minima conoscenza di programmazione con Arduino ed un po’ di esperienza nelle operazioni di saldatura.
Per gli effetti sonori, è disponibile un gran numero di file MP3 adatti all’occasione; qui di seguito vengono riportati alcuni link:

 

Possibili modifiche nella scelta dei componenti utilizzati

Questo progetto può essere realizzato anche applicando le seguenti modifiche nella scelta dei componenti utilizzati:

 

Passo 1: Riprodurre suoni attraverso l’altoparlante

Assemblare la Wave Shield e collegarla alla scheda Arduino, predisponendola per il funzionamento; copiare la compilation di suoni di Halloween nella SDcard e avviare lo sketch di esempio DAPHC presente nella libreria WaveHC; ogni suono dovrebbe essere riprodotto in sequenza.
halloween_img1
Collegare l’altoparlante alla Wave Shield tramite saldatura di due cavi, come mostrato nell’immagine sotto riportata; dovreste essere in grado a questo punto di sentire gli effetti sonori in maniera chiara. Nel caso in cui si necessiti di maggior potenza, è possibile aggiungere un amplificatore operazionale che dovrebbe approssimativamente raddoppiare il volume in uscita.

halloween_img2            halloween_zucca-portacaramelle_img3

 

Passo 2: Scegliere e collegare il sensore di distanza

Il passo successivo riguarda la scelta del sensore di distanza. Ci sono due tipi di sensori fra cui scegliere: un sensore ad infrarossi come il Sensore di distanza Sharp GP2Y0A41SK0F, oppure un sensore ad ultrasuoni come il Sensore di distanza ad ultrasuoni LV MaxSonar EZ1 – MB1010. La differenza fra i due dispositivi riguarda la modalità di rilevamento della distanza: il sensore ad infrarossi opera al meglio su distanze brevi (fino ad un metro), mentre il sensore ad ultrasuoni è perfetto per le lunghe distanze (anche fino a 10metri a seconda del modello di sensore scelto).
halloween_zucca-portacaramelle_img4
Nel caso in cui si preferisca un sensore che operi a distanze maggiori, si può scegliere un MaxBotix MB1010 che dispone di segnali di uscita sia analogici che digitali ed è caratterizzato da un range di rilevamento fino a sei metri. In caso di utilizzo di un differente sensore ad ultrasuoni, potrebbe essere necessario variare il codice.
Il sensore MaxBotix è caratterizzato da semplicità d’uso; è sufficiente saldare tre cavi ai pin PWR (alimentazione), GND (massa) e AN (segnale analogico), così come mostrato in foto:

halloween_zucca-portacaramelle_img5           halloween_zucca-portacaramelle_img6

Infine, il sensore va collegato alla Wave Shield in modo che il segnale analogico del sensore sia connesso al pin 0 (Analog IN) e i cavi di alimentazione e massa siano collegati ai rispettivi pin della shield.
halloween_zucca-portacaramelle_img7
Qui di seguito viene riportato lo sketch da caricare su Arduino appositamente ideato per la Wave Shield:

#include <WaveHC.h>
#include <WaveUtil.h>
 
SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
FatReader file;   // This object represent the WAV file 
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time
 
#define playcomplete(x) ROM_playcomplete(PSTR(x))         // save RAM by using program memory strings
 
#define servo 7
#define redled 9
#define eyeleds 18
#define mouthleds 17
#define midmouthleds 16
#define outermouthleds 19
 
void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println(F("Wave test!"));
 
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(redled, OUTPUT);
  pinMode(servo, OUTPUT);
  pinMode(eyeleds, OUTPUT);
  pinMode(outermouthleds, OUTPUT);
  pinMode(midmouthleds, OUTPUT);
  pinMode(mouthleds, OUTPUT);
  
  randomSeed(analogRead(0));
 
 
  if (!card.init()) {
    Serial.println(F("Card init. failed!")); return;
  }
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
  
  // Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
    if (vol.init(card, part)) 
      break;                           // we found one, lets bail
  }
  if (part == 5) {                     // if we ended up not finding one  🙁
    Serial.println(F("No valid FAT partition!"));  // Something went wrong, lets print out why
  }
  
  // Lets tell the user about what we found
  putstring("Using partition ");
  Serial.print(part, DEC);
  Serial.print(F(", type is FAT"));
  Serial.println(vol.fatType(), DEC);     // FAT16 or FAT32?
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    Serial.println(F("Can't open root dir!"));      // Something went wrong,
  }
  
  // Whew! We got past the tough parts.
  Serial.println(F("Files found (* = fragmented):"));
 
  // Print out all of the files in all the directories.
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
}
 
void pulseServo(uint8_t servopin, uint16_t p) {
  
 digitalWrite(servopin, HIGH);
 delayMicroseconds(600);
 while (p--) {
   delayMicroseconds(4);
 }
 digitalWrite(servopin, LOW);
  delay(18);
}
 
uint8_t pumpkinstate = 0;
 
void loop() { 
   int distsensor, i;
   long time;
   /*
   for (i=0; i<50; i++) {
     pulseServo(servo,0);
   }
   for (i=0; i<50; i++) {
     pulseServo(servo,400);
   }
   return;
   */
   distsensor = 0;
   for (i=0; i<8; i++) {
     distsensor += analogRead(0);
     delay(50);
   }
   distsensor /= 8;
 
   Serial.print(F("Sensor = ")); Serial.println(distsensor);
   
   if (distsensor <= 500) { digitalWrite(eyeleds, HIGH); } if (distsensor > 500) {
     digitalWrite(eyeleds, LOW);  
     pumpkinstate = 1;
     // nobody there. one out of 200 times play one of the scary sounds (once every few minutes)
     i = random(200);
     //Serial.println(i);
     if (i == 0) {
       i = random(3);
       if (i == 0) {
           playcomplete("CACKLE.WAV");
       } else if (i == 1) {
           playcomplete("GOSTMOAN.WAV");
       } else {
           playcomplete("CATSCREM.WAV");   
       }
     }
   } else if ((distsensor > 300) && (distsensor < 400)) {
     if (pumpkinstate <= 1) { // play "hello children" playcomplete("HELOCHIL.WAV"); } else { i = random(60); // more often //Serial.println(i); if (i == 0) { i = random(3); if (i == 0) { playcomplete("KNOCKING.WAV"); } else if (i == 1) { playcomplete("MONSTER.WAV"); } else { playcomplete("SCREAM2.WAV"); } } } pumpkinstate = 2; } else if ((distsensor > 100) && (distsensor < 200)) {
     if (pumpkinstate <= 2) {    // play "hello children"
       playcomplete("GOBACK.WAV"); 
     } else {
       i = random(50);            // more often
       //Serial.println(i);
       if (i == 0) {
         i = random(3);
         if (i == 0) {
           playcomplete("GHOULLAF.WAV");
         } else if (i == 1) {
           playcomplete("SCREAM.WAV");
         } else {
           playcomplete("SCREECH.WAV");   
         }
       }
     }
     pumpkinstate = 3;
   } else if (distsensor < 50) {
     if (pumpkinstate <= 3) {    // play "hello children"
        playcomplete("HPYHALWN.WAV");    
     } else {
       i = random(30);            // more often
     //Serial.println(i);
     if (i == 0) {
       i = random(2);
       if (i == 0) {
           playcomplete("BOOHAHA.WAV");
       } else if (i == 1) {
           playcomplete("WELCOME.WAV");
       } 
     }
       
   }
    pumpkinstate = 4;
 }
}
 
void ROM_playcomplete(const char *romname) {
  char name[13], i;
  uint8_t volume;
  int v2;
  
  for (i=0; i<13; i++) {
    name[i] = pgm_read_byte(&romname[i]);
  }
  name[12] = 0;
  Serial.println(name);
  playfile(name);
  while (wave.isplaying) {
   volume = 0;
   for (i=0; i<8; i++) {
     v2 = analogRead(1) - 512;
     if (v2 < 0) v2 *= -1; if (v2 > volume)
       volume = v2;
     delay(5);
   }
   if (volume > 200) {
     digitalWrite(outermouthleds, HIGH);
   } else {
     digitalWrite(outermouthleds, LOW);
   }
   if (volume > 150) {
     digitalWrite(midmouthleds, HIGH);
   } else {
     digitalWrite(midmouthleds, LOW);
   } 
   if (volume > 100) {
     digitalWrite(mouthleds, HIGH);
   } else {
     digitalWrite(mouthleds, LOW);
   } 
   //Serial.print(F("vol = ")); Serial.println(volume, DEC);
  }
  file.close();
}
 
void playfile(char *name) {
 
   if (!file.open(root, name)) {
      Serial.println(F(" Couldn't open file")); return;
   }
   if (!wave.create(file)) {
     Serial.println(F(" Not a valid WAV")); return;
   }
   // ok time to play!
   wave.play();
}

Di seguito vengono riportati alcuni passaggi dello sketch sui quali si ritiene che sia importante porre attenzione. In questa prima parte di codice, che si analizza un po’ più nel dettaglio, si evidenzia come per ridurre la possibilità di acquisire letture errate, la misura della distanza viene calcolata come valore medio rispetto ad 8 letture differenti.

void loop() { 
...
distsensor = 0;

for (i=0; i<8; i++) {
         distsensor += analogRead(0);
         delay(50);
}
distsensor /= 8;

putstring_nl("Sensor = "); Serial.println(distsensor);
...

In questa seconda parte di codice si analizzano alcune delle ripetizioni di blocchi “if-then-else” inserite nello sketch. Una lettura del valore in uscita dal sensore pari a 500 indica che non si rilevano persone/oggetti nelle vicinanze della zucca (più precisamente si tratta di un range di 6 metri, in base alle caratteristiche del sensore scelto per questo progetto). Nel caso di assenza di persone nelle vicinanze della zucca, si genera tramite la funzione random() un numero casuale nel range 0-199. Se il numero che si genera è pari a 0 (ciò accade in media una volta su 200) allora verrà avviato, sempre in maniera casuale, uno fra i seguenti tre file audio: CACKLE.WAV, GOSTMOAN.WAV e CATSCREM.WAV.

     ...
     if (distsensor > 500) {
     ...
     pumpkinstate = 1;
     // nobody there. one out of 200 times play one of the scary sounds (once every few minutes)
     i = random(200);
     if (i == 0) {
       i = random(3);
       if (i == 0) {
           playcomplete("CACKLE.WAV");
       } else if (i == 1) {
           playcomplete("GOSTMOAN.WAV");
       } else {
           playcomplete("CATSCREM.WAV");   
       }
     }
...

La variabile “pumpkinstate” viene utilizzata per tenere traccia dell’effettiva distanza alla quale è stato rilevato il soggetto durante l’ultima misurazione; questa variabile permette di capire se le persone si stanno allontanando o se si stanno avvicinando alla zucca.

Passo 3: applicare il sensore di distanza ed i LED

A questo punto prendete la zucca cava (o l’oggetto da voi utilizzato per questo progetto) e procedete alle modifiche per poter applicare ad esso il sensore e i led che vi permetteranno di realizzare la zucca portacaramelle.

halloween_zucca-portacaramelle_img8         halloween_zucca-portacaramelle_img9

Sul naso della zucca, praticare un foro di una dimensione tale da permettere un preciso posizionamento del sensore di distanza ad ultrasuoni.

halloween_zucca-portacaramelle_img11   halloween_zucca-portacaramelle_img12

Sistemare il sensore di modo che punti su una zona sufficientemente ampia, così da impedire ad oggetti di interferire con un corretto funzionamento del sensore. L’ideale sarebbe riuscire ad avere una lettura di base sempre maggiore a 500. A questo punto provate a modificare nel codice a vostro piacimento i valori del numero random con il quale far partire la riproduzione dei suoni in sequenza, sperimentando le differenze in base ai valori impostati.

Infine, senza un po’ di LED che lampeggiano questo non si potrebbe definire un vero progetto elettronico!!!!!!!
halloween_zucca-portacaramelle_img13
Posizionare quindi i LED a vostro piacimento su occhi e bocca della zucca,  fino ad ottenere il risultato desiderato, aiutandovi con un po’ di colla a caldo. Nello specifico, in questo progetto si è scelto di posizionare 2 LED per illuminare gli occhi e 5 LED per la bocca.

halloween_zucca-portacaramelle_img14          halloween_zucca-portacaramelle_img15

Collegare i LED secondo uno schema prescelto; lo schema utilizzato per questo progetto prevede che i 2 LED degli occhi siano pilotati contemporaneamente mentre i 5 LED della bocca siano suddivisi in tre sezioni simmetriche che permettono alcune semplici animazioni. Poichè il LED rosso richiede una tensione di circa 2V e la scheda Arduino opera a 5V, è possibile unire due LED alla volta per semplificare le operazioni di saldatura.
halloween_zucca-portacaramelle_img16
I LED sono controllati dai pin della sezione Analog IN rimasti liberi. E’ importante notare che l’accoppiamento LED-Pin non deve rispettare un particolare schema; quello descritto in questo progetto è solamente un esempio: nello specifico, i due LED degli occhi sono connessi al pin analogico #4, i LED più esterni della bocca al pin analogico #5, i LED laterali al pin analogico #2 e quello più centrale al pin analogico #3.
Porre estrema attenzione nelle operazioni di saldatura per evitare dolorose bruciature e il danneggiamento della zucca di plastica!

halloween_zucca-portacaramelle_img17      halloween_zucca-portacaramelle_img18

Utilizzando dei cavetti a coccodrillo e un alimentatore portatile potrete testare il funzionamento dei LED.

halloween_zucca-portacaramelle_img19       halloween_zucca-portacaramelle_img20

Passo 4: collegare i LED alla scheda Arduino

Collegare infine i LED alla scheda Arduino UNO come indicato nello schema elettrico di cui sopra; l’obiettivo è di far lampeggiare i LED in funzione dei suoni emessi. Sebbene sia possibile programmare la funzione che realizzi la sequenza di accensione e spegnimento dei LED, è molto più semplice utilizzare un pin analogico per misurare l’uscita dell’amplificatore e programmare l’accensione dei LED in funzione del segnale dell’altoparlante. Per ottenere ciò, è necessario saldare un cavo tra il pin analogico #1 e una resistenza da 1.5KOhm.
halloween_zucca-portacaramelle_img21
E’ finalmente il momento di illuminare la zucca con luci scintillanti! La parte di sketch sotto riportata accenderà i LED posizionati negli occhi quando il sensore rileverà una persona nelle vicinanze:

...
   if (distsensor <= 500) {
     digitalWrite(eyeleds, HIGH); 
   } 
   if (distsensor > 500) {
     digitalWrite(eyeleds, LOW);  
...

Successivamente, con la parte di codice relativa alla riproduzione dei suoni, daremo voce alla zucca. Prima raccoglieremo un piccolo numero di letture da sensore; il segnale audio è centrato su un valore di 2.5 (in un range tra 0 e 5), quindi sottrarremo 512 (la metà del massimo valore analogico di lettura) e lo invertiremo se è negativo; questa operazione fornirà il valore assoluto del volume.
Raccoglieremo quindi il valore massimo di 8 letture; i LED della bocca si accenderanno a seconda di quanto sarà alto questo valore. Vi potrete divertire un po’ variando questi valori per avere differenti giochi di luci:

...
while (wave.isplaying) {
   volume = 0;
   for (i=0; i<8; i++) {
     v2 = analogRead(1) - 512;
     if (v2 < 0) 
        v2 *= -1;
     if (v2 > volume)
       volume = v2;
     delay(5);
   }
   if (volume > 200) {
     digitalWrite(outermouthleds, HIGH);
   } else {
     digitalWrite(outermouthleds, LOW);
   }
   if (volume > 150) {
     digitalWrite(midmouthleds, HIGH);
   } else {
     digitalWrite(midmouthleds, LOW);
   } 
   if (volume > 100) {
     digitalWrite(mouthleds, HIGH);
   } else {
     digitalWrite(mouthleds, LOW);
   } 
   //putstring("vol = "); Serial.println(volume, DEC);
  }
...

Per realizzare il video sotto riportato e dare un’idea dell’animazione che si genera, è stato modificato leggermente lo sketch creando una animazione un po’ più “accelerata”.

Passo 5: Assemblaggio finale

E’ tempo di assemblare il nostro progetto, ponendo all’interno della zucca l’altoparlante, la Wave Shield e la scheda Arduino. Resta solo da decidere il tipo di alimentazione. Una possibile soluzione è l’utilizzo di un pacco batteria con 4 stilo AA (di base, il progetto richiede circa da 4.5V a 6V).

halloween_zucca-portacaramelle_img22       halloween_zucca-portacaramelle_img23

In alternativa al pacco batteria proposto, è possibile alimentare questo progetto in due modi alternativi:

oppure

Entrambi i metodi alternativi proposti si dovranno collegare direttamente alla scheda Arduino. Nel caso si utilizzi l’alimentatore da parete sarà necessario praticare un piccolo foro sul retro della zucca.

halloween_zucca-portacaramelle_img24       halloween_zucca-portacaramelle_img25

A questo punto, è tutto pronto e non vi resta che poggiare sulla parte superiore della zucca una ciotola piena di caramelle e…….Buon Halloween!!
halloween_zucca_portacaramelle