craig jameson
Published © GPL3+

Monster in a Box

A Monster in a Box Halloween prop is a box or crate that appears to be jailing a monster which is on the verge of escaping.

IntermediateFull instructions provided20 hours11,111
Monster in a Box

Things used in this project

Hardware components

Wooden Crate/Box
×1
Arduino UNO & Genuino UNO
Arduino UNO & Genuino UNO
×1
Adafruit Wave Shield
×1
SainSmart 4-Channel Relay Module
×1
Windshield Wiper Motor
×1
Red LED Christmas Lights (100)
×1
Smoke Machine
×1
Powered Amp/Speaker
×1

Story

Read more

Custom parts and enclosures

Monster Sounds Zip File

A Zip file containing all of the sounds for this project

Schematics

Monster In A Box Wiring

Code

Monster_In_A_Box_V03.ino

C/C++
Arduino code for the Monster in a Box prop
#include "MonsterSounds.h"

#define LID_BOUNCER 6   // Connect Digital Pin on Arduino to Relay Module
#define RED_LEDS    7   // Connect Digital Pin on Arduino to Relay Module
#define FOG_MACHINE 8   // Connect Digital Pin on Arduino to Relay Module
#define UNUSED      9   // Connect Digital Pin on Arduino to Relay Module

#define PIR_SENSOR      A0
#define ACTION_2_BUTTON A3
#define PAUSE_BUTTON    A4
#define DEBUG_BUTTON    A5
 
#define FRAMERATE          20
#define FRAMEDELAY         1000/FRAMERATE

#define WAKE_SECONDS_MIN    3 // Seconds
#define WAKE_SECONDS_MAX    5 // Seconds
#define WAKE_DELAY         30 // Seconds

#define WAKE_FRAMES_MIN     WAKE_SECONDS_MIN  * FRAMERATE
#define WAKE_FRAMES_MAX     WAKE_SECONDS_MAX * FRAMERATE
#define WAKE_DELAY_FRAMES   WAKE_DELAY  * FRAMERATE

MonsterSounds sounds;

void setup() 
{  
  // initialize serial communication:
  Serial.begin(9600);
  
  // Setup all the relay Pins
  pinMode(LID_BOUNCER, OUTPUT);
  pinMode(RED_LEDS,    OUTPUT);
  pinMode(FOG_MACHINE, OUTPUT);
  pinMode(UNUSED,      OUTPUT);

  // Force all Effects to OFF
  stopAllEffects();

  pinMode(PIR_SENSOR,      INPUT);
  pinMode(ACTION_2_BUTTON, INPUT_PULLUP);
  pinMode(PAUSE_BUTTON,    INPUT_PULLUP);
  pinMode(DEBUG_BUTTON,    INPUT_PULLUP);

  sounds.initialize(); // Monster Sounds
  
  sounds.playSystemReady();delay(1000);
  
  Serial.println();
  Serial.print("*** System Ready ***");
  Serial.println();
  
}

boolean monsterIsAwake = false;
int pauseCountdown         = 0;
int lidBounceCountdown     = 0;
int flashRedLightCountdown = 0;
int smokeCountdown         = 0;
int reawakeCountdown       = 0;

void loop() 
{ 
  if (pauseCountdown > -1 ) pauseCountdown--;
  
  // NOTE: All buttons are using pullups, thus LOW means the button is PRESSED
  
  if (pauseCountdown > 0 || digitalRead(PAUSE_BUTTON) == LOW) 
  {
    if (pauseCountdown <= 0 && digitalRead(PAUSE_BUTTON) == LOW)  Serial.println("PAUSED");
   
    stopAllEffects();
    sounds.stopAll();
    reawakeCountdown = WAKE_DELAY_FRAMES;

    if (digitalRead(PAUSE_BUTTON) == LOW) pauseCountdown = 50;
    
  
  } else {
    if (reawakeCountdown ==0) Serial.println("ARMED");
    if ((digitalRead(PIR_SENSOR) == HIGH || digitalRead(ACTION_2_BUTTON) == LOW) && reawakeCountdown <=0)   
    {
      wakeMonster();
    }
  
    if (monsterIsAwake) 
    {
      processAnimation();
    
    } else {
     
      sounds.playSnore();
      
      if (reawakeCountdown > -1 ) reawakeCountdown--;
    
    }
  }
  
  delay(FRAMEDELAY);
}


/*
 * Wake Monster
 */
void wakeMonster() 
{
  if (!monsterIsAwake)
  {
    int activityDuration = random(WAKE_FRAMES_MIN, WAKE_FRAMES_MAX); // this is how long the monster will be active

    Serial.print("WAKE UP : ");
    Serial.print(WAKE_FRAMES_MIN);
    Serial.print(" : ");
    Serial.print(WAKE_FRAMES_MAX);
    Serial.print(" : ");
    Serial.println(activityDuration);
    
    reawakeCountdown = isDebug() ? 0 : WAKE_DELAY_FRAMES;    // force a delay between wake cycles
    Serial.print("******"); 
    Serial.print(reawakeCountdown); 
    Serial.println( "******");
    
    monsterIsAwake = true;

    bounceLid(activityDuration);
    flashRedLight(activityDuration);
    activateSmoke(activityDuration);    // Hold the smoke button for 1 second 

    sounds.playRoar();
    
  }
}

 
/*
 * 
 */
void processAnimation()
{ 
  if (lidBounceCountdown % 10 == 0) {
    Serial.print(lidBounceCountdown);
    Serial.print("  -  ");
    Serial.print(flashRedLightCountdown);
    Serial.print("  -  ");
    Serial.print(smokeCountdown);
    Serial.println(" ");
  }
  
  monsterIsAwake = lidBounceCountdown > 0 || flashRedLightCountdown > 0 || smokeCountdown > 0;
  
  if (lidBounceCountdown <= 0) 
  {
    bounceLid(0);
  } else {
    lidBounceCountdown--;
  }
  
  if (flashRedLightCountdown <= 0) 
  {
    flashRedLight(0);
  } else {
    flashRedLightCountdown--;
  }
  
  if (smokeCountdown <= 0) 
  {
    activateSmoke(0);
  } else {
    smokeCountdown--;
  }
  
}


/*
 * Disable all of the Effects 
 * This effectively turns the monster off
 */
void stopAllEffects()
{
  openRelay( LID_BOUNCER );
  openRelay( RED_LEDS );
  openRelay( FOG_MACHINE );
  openRelay( UNUSED );
  
  lidBounceCountdown     = 0;
  flashRedLightCountdown = 0;
  smokeCountdown         = 0;
  reawakeCountdown       = 0;

  monsterIsAwake = false;
}


/*
 * Bounce the box lid
 *     duration of 0 means STOP
 */
void bounceLid(int duration)
{
  if (duration <= 0) 
  {
    openRelay(LID_BOUNCER);    
    
  } else {
    // start the lid bouncing
    closeRelay(LID_BOUNCER);
    
    // set a timeout to stop after duration
    lidBounceCountdown = duration;
  }
}


/*
 * Flash Red Lights
 *     duration of 0 means STOP
 */
void flashRedLight(int duration)
{
  if (duration <= 0) 
  {
    openRelay(RED_LEDS);
    
  } else {
    // start the light flashing
    closeRelay(RED_LEDS);
    
    // set a timeout to stop after duration
    flashRedLightCountdown = duration;
  }
  
}

/*
 * Activate Smoke
 */
 void activateSmoke(int duration)
 {
  // 'press' the smoke button
  // duration should be a fixed amount of time needed for the machine to respond to the action
  // set a timeout to stop after duration
  
  if (duration <= 0) 
  {
    openRelay(FOG_MACHINE);
    
  } else {
    // start the light flashing
    closeRelay(FOG_MACHINE);
    
    // set a timeout to stop after duration
    smokeCountdown = duration;
  }
 }

/* 
 *  Sets the Normally Open (NO) terminal to OPEN
 *  Normally Closed will become Closed
 */
 void openRelay(int channel)
 {
  digitalWrite(channel, HIGH);
 }

/* 
 *  Sets the Normally Open (NO) terminal to Closed.
 *  Normally Closed will become OPEN
 */
 void closeRelay(int channel)
 {
  digitalWrite(channel, LOW);
 }

/*
 * 
 */
bool isDebug()
{
  return digitalRead(DEBUG_BUTTON) == LOW;
}

MonsterSounds.h

C/C++
Arduino header for the monster sounds library
/* NOTE ABOUT ARDUINO PINS
 * Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface). 
 * Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin. 
 * However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC. 
 * To chanage these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins. 
 * That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
 * 
 */
 
#include <Arduino.h>
#include <Wire.h>
#include <WaveHC.h>
#include <WaveUtil.h>

    static const char roar0[] = "ROAR0000.wav";
    static const char roar1[] = "ROAR0001.wav";
    static const char roar2[] = "ROAR0002.wav";
    static const char * const roarSounds[] = {roar0, roar1, roar2};
    
    static const char sleep0[] = "SNORE000.wav";
    static const char sleep1[] = "SNORE001.wav";
    static const char sleep2[] = "SNORE002.wav";
    static const char * const sleepSounds[] = {sleep0, sleep1, sleep2};
    
    int previousRoarSound = -1;
  
class MonsterSounds 
{
  private:  

    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 for a phrase
    WaveHC    wave; // A single wave object -- only one sound is played at a time
    
    void playfile(char *name);

  public:
    void initialize(); 
    void playSystemReady();
    void playRoar();    
    void playSnore();    
    void stopAll();
};

MonsterSounds.ino

C/C++
Arduino code for the monster sounds library
/* NOTE ABOUT ARDUINO PINS
 * Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface). 
 * Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin. 
 * However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC. 
 * To chanage these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins. 
 * That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
 * 
 */
 
void MonsterSounds::initialize()
{
  Serial.println("Initializing Sounds...");
  if(!card.init())        Serial.println(F("Card init. failed!"));
  if(!vol.init(card))     Serial.println(F("No partition!"));
  if(!root.openRoot(vol)) Serial.println(F("Couldn't open dir"));
  Serial.println(F("Files found:"));
  root.ls();

  randomSeed(analogRead(0));
}


void  MonsterSounds::playSystemReady()
{
  this->playfile("WELCOME0.WAV");
}

void  MonsterSounds::playRoar()
{
  int index = random(3);  // 0, 1, 2
  while (index == previousRoarSound)
  {
    index = random(3);
  }
  previousRoarSound = index;
  
  this->playfile(roarSounds[index]);
}

void  MonsterSounds::playSnore()
{
  if (!wave.isplaying) // Do not interupt an exising sound with snoring
  {
    int index = random(3); // 0, 1, 2
    this->playfile(sleepSounds[index]);
  }
}

void MonsterSounds::stopAll()
{
  wave.stop(); // Stop any currently-playing WAV
}

// -------------------------------------------------------------------
// playfile()    
// Open and start playing a WAV file
// -------------------------------------------------------------------
void MonsterSounds::playfile(char *name) 
{    
  PgmPrint("Playing sound: ");
  Serial.println(name); 
  
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }

  if (!file.open(root, name)) {
    PgmPrintln("File not found ");
    return;
  }

  if (!wave.create(file)) {
    PgmPrintln("Not a valid WAV");
    return;
  }
  
  // ok time to play!
  wave.play();
}

Credits

craig jameson

craig jameson

2 projects • 10 followers

Comments