Step 2: Choose and connect sensor

Next, we will connect up a distance sensor. There are two basic 'flavors' of distance sensing: infrared and sonar. IR works better for close range (about a meter or more) and sonar works better for longer ranges (up to 10 meters).

Sonar and IR sensors
Since I want this pumpkin to activate from a few meters away, I'm going with a sonar sensor. The nicest one I've seen so far is the Maxbotics EZ1 which has both digital and analog signal outputs, runs from a range of voltages and can detect more than 6 meters away. If you're using a different sonar, the code may be different so you will have to first get that device working and then come back once you have functional interface code.
The maxbotix is easy to use. Connect three wires: Power, Ground and Analog signal as shown.
Finally connect the sonar to the shield so that the analog signal feeds into Analog In #0 and the power and ground wires are connected to power and ground.
Here's the code we'll be running:
Download: file
#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();
}

Load up the sketch above into the Arduino. Note the following chunks of code.

In this bit we read 8 seperate measurements from the sonar and average them. This avoids one or two oddball readings:

Download: file
void loop() { 
...
   distsensor = 0;
   for (i=0; i<8; i++) {
     distsensor += analogRead(0);
     delay(50);
   }
   distsensor /= 8;

   putstring_nl("Sensor = "); Serial.println(distsensor);
...
After that we have some 'repeating' if-then statements. Here, we see how large the reading was. A reading of 500 indicates more than 6 meters away and in that case we will pick a random number between 0 and 199 and if its 0 (1/200 times) then we'll play one of three scary sounds.
Download: file
...
     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");   
       }
     }
...

The 'pumpkinstate' variable is used to keep track of how far the target was last we checked. This will let us know if they are getting closer or farther away over time.

Next get yourself one of those $1 plastic pumpkins from your local drug store.

They are hollow, making them perfect for stashing electronic wiring!
Cut a hole in the nose so that the sonar can be press-fit in.

Set up the sonar so that it is pointing at a large open space (the readings are consistantly over 500) Then experiment with walking near and around it. Adjust the randomness if you want to make it more or less noisy. You can also change what audio files get played when.

This guide was first published on Sep 28, 2013. It was last updated on Sep 28, 2013. This page (Step 2: Choose and connect sensor) was last updated on Feb 18, 2020.