Sunday, December 10, 2017

Monster in a Box

Unfortunately this blog post will be slightly incomplete. I have pictures of all the woodworking but I failed to take pictures of any of the electronics and it's now packed away since Halloween is over. I will still try to describe everything that was done.

This project started with an e-mail from a friend of mine. It was a video of a very similar project and I have wanted to get into some electronics projects and this seemed like a good start.
We ended up doing two boxes. One for each of us. I needed to replace a portion of my fence so it seemed like a good time to get some old looking wood. We had enough for his box but not enough for mine. Another technique I was interested in trying was aging old wood. A bit of vinegar with some steel wool was put in a jar for three days and I gave everything a first coat before building the box. I waited a few months for the second coat which in hindsight was a bit late. The steel wool had rusted so I got an look that was a bit more orange than I would have preferred.
I added three stencils once the box was fully aged. This included a 'Danger' sign on the top, a 'Do Not Feed' on the front and a 'This Side Up" sign on the back. I'm more of a precise woodworker then a Disney imagineer so all of my stencils were very neat. For the other box he just held it roughly and spray painted quickly. I think it made his look a bit more natural. The bars and hinges were all given a coat of black spray paint meant to look like black metal and I added chains and a lock. The handles were braced rope. Now it was time for the electronics.

I'll lay out all the electronic bits and include precise model numbers where I can. This is a pretty straight forward Arduino project and doesn't have to be perfect to still be scary.
Bouncing Lid - The lid bounces via a wooden wheel with randomly spaced cogs. It runs off a windshield wiper motor and uses a high voltage motor shield. I also had to add a few springs to get the lid moving up and down.
Lid Lights - The lights for the lid are LED stick on lights that require a 9v power supply. If you're going to get into electronics I recommend you keep every power supply you've ever owned. They are all different voltages and you can cut off the end and use them for a lot of projects. (Susay® Waterproof Red LED 3528 SMD 300LED 5M Flexible Light Strip 12V 2A 24W 60LED/M) I used a relay to allow me to turn them off and on. (SainSmart 4-Channel Relay Module)
LED Eyes - The eyes are just two simple LEDs. These are slightly brighter than your average LEDs but were easy to install and program.
Sound - The sound shield we used was the standard one for Arduino and allows us to directly connect speakers. This was attempted but in the end I went with a set with a subwoofer from Walmart. Only $30 and gave much louder sound with good bass. (http://www.robotshop.com/ca/en/cytron-easy-mp3-shield.html)
Lid Sensor - The lid sensor is a door bell. Simple but effective. (Almost. More on that later.)
Remote - The remote was a Gikfun IC2262/2272 4 Channel Wireless Remote Control. It has four buttons and allows me to have various 'scenes' from just lights to full on lid lifting.
Lock - We got a very authentic looking lock for only $20. You can probably find cheaper but this was solid metal. (2 of Deco 79 01101 Metal Brass Lock and Keys Wall Hanging, 4" x 2.5")

I have made note of a few things for next Halloween that weren't quite working. The range for the remote is very short. Will either need to find something more powerful or create a better antenna. The lid sensor works but doesn't stay turned on long enough. I started to experiment with slowing the motor down a bit before trying to see if the lid was closed but didn't get it working in time.

This was a fun project and here is the video of it in action. The very simple code is below that.




#include <SoftwareSerial.h>

// ------------------------------------------------------------------
// Include Sound drivers.
// ------------------------------------------------------------------
#include <CytronEZMP3.h>
CytronEZMP3 mp3;

// ------------------------------------------------------------------
// Pin Designations
// ------------------------------------------------------------------
// Remote Input Pins
const int remoteInputA = 11;
const int remoteInputB = 13;
const int remoteInputC = 12;
const int remoteInputD = 10;

// Sounds Shield
const int soundReceivePin = 2;
const int soundTransmitPin = 3;

// Motor Pins
const int motorDirPin = 4;
const int motorSpeedPin = 5;

// Park Pin
const int parkPin = 7;

// Lid Lights
const int lidPin = 8;

// Eyes
const int eyePin = 9;

// ------------------------------------------------------------------
// Random Variables
// ------------------------------------------------------------------
int debugMode = true;
int park = true;
int remoteReadState = 0;
int monsterVolume = 75;
int monsterFrequency = 5000;

// ------------------------------------------------------------------
// One Time Setup Code
// ------------------------------------------------------------------
void setup() {
  // Initiate the debug window.
  Serial.begin(9600);

  // Setup Remote
  pinMode(remoteInputA, INPUT);
  pinMode(remoteInputB, INPUT);
  pinMode(remoteInputC, INPUT);
  pinMode(remoteInputD, INPUT);
  digitalWrite(remoteInputA, LOW);
  digitalWrite(remoteInputA, LOW);
  digitalWrite(remoteInputA, LOW);
  digitalWrite(remoteInputA, LOW);

  // Setup Eyes
  pinMode(eyePin, OUTPUT);

  // Setup Lid
  pinMode(lidPin, OUTPUT);
  digitalWrite(lidPin, LOW);

  // Set up Parking Switch
  pinMode(parkPin, INPUT);

  // Set up the sounds
  if(!mp3.begin(soundReceivePin, soundTransmitPin))
  {
    Serial.println("Init failed");
    //while(1);
  }
  mp3.setVolume(monsterVolume);

  // Set up the motor. Make sure it's going in the right direction.
  pinMode(motorSpeedPin, OUTPUT);
  pinMode(motorDirPin, OUTPUT);
  digitalWrite(motorDirPin, LOW);
  digitalWrite(motorSpeedPin, LOW);
}

// ------------------------------------------------------------------
// Listening Loop
// ------------------------------------------------------------------
void loop() {
  // Testing
  testMonster();

  // Weak Monster
  if (digitalRead(remoteInputA) == HIGH)
  {
    executeWeakMonster();
  }

  // Mild Monster
  if (digitalRead(remoteInputB) == HIGH)
  {
    executeMildMonster();
  }

  // Crazy Monster
 if (digitalRead(remoteInputC) == HIGH)
  {
    executeCrazyMonster();
  }

  // Random Monster
  if (digitalRead(remoteInputD) == HIGH)
  {
    executeRandomMonster();
  }

}

// ------------------------------------------------------------------
// Debug and Testing Methods
// ------------------------------------------------------------------
void debugPrintLn(char* debugMessage) {
 if(debugMode == true)
  {
    Serial.println(debugMessage);
  }
}

void testMonster()
{
  debugPrintLn("TESTING");
  delay(5000);
  executeWeakMonster();
  delay(1000);
  executeMildMonster();
  delay(5000);
  executeWeakMonster();
  delay(1000);
  executeCrazyMonster();
  delay(monsterFrequency);
}

// ------------------------------------------------------------------
// Monster Eye Actions
// ------------------------------------------------------------------
void turnEyesOn()
{
  debugPrintLn("TURNING EYES ON");
  digitalWrite(eyePin, HIGH);
}

void turnEyesOff()
{
  debugPrintLn("TURNING EYES OFF");
  digitalWrite(eyePin, LOW);
}

void executeGlowingEyes()
{
  debugPrintLn("MAKING EYES GLOW");

  for(int brightnessCount=0; brightnessCount <= 255; brightnessCount++){
    analogWrite(eyePin, brightnessCount);  
    delay(10);
  }
  for(int brightnessCount=255; brightnessCount >= 0; brightnessCount--){
    analogWrite(eyePin, brightnessCount);
    delay(10);
  }
}

// ------------------------------------------------------------------
// Monster Growling Actions
// ------------------------------------------------------------------
void lowGrowlOne()
{
  debugPrintLn("LOW GROWL ONE");
  mp3.playTrack(1);
}

void lowGrowlTwo()
{
  debugPrintLn("LOW GROWL TWO");
  mp3.playTrack(2);
}

void lowGrowlThree()
{
  debugPrintLn("LOW GROWL THREE");
  mp3.playTrack(3);
}

void mediumGrowlOne()
{
  debugPrintLn("MEDIUM GROWL ONE");
  mp3.playTrack(5);
}

void mediumGrowlTwo()
{
  debugPrintLn("MEDIUM GROWL TWO");
  mp3.playTrack(5);
}

void mediumGrowlThree()
{
  debugPrintLn("MEDIUM GROWL THREE");
  mp3.playTrack(6);
}

void crazyGrowlOne()
{
  debugPrintLn("CRAZY GROWL ONE");
  mp3.playTrack(7);
}

void crazyGrowlTwo()
{
  debugPrintLn("CRAZY GROWL TWO");
  mp3.playTrack(8);
}

void crazyGrowlThree()
{
  debugPrintLn("CRAZY GROWL THREE");
  mp3.playTrack(9);
}

// ------------------------------------------------------------------
// Monster Lid Light Actions
// ------------------------------------------------------------------
void turnLidLightsOn()
{
  debugPrintLn("TURNING LID LIGHTS ON");
  digitalWrite(lidPin, LOW);
  delay(750);
}

void turnLidLightsOff()
{
  debugPrintLn("TURNING LID LIGHTS OFF");
  digitalWrite(lidPin, HIGH);
}

// ------------------------------------------------------------------
// Monster Lid Lifting Actions
// ------------------------------------------------------------------
void startSlowMotor()
{

  debugPrintLn("STARTING SLOW MOTOR");
  analogWrite(motorSpeedPin, 255);
}

void startFastMotor()
{

  debugPrintLn("STARTING FAST MOTOR");
  analogWrite(motorSpeedPin, 255);
}

void stopMotor()
{
  debugPrintLn("STOPPING MOTOR");
  analogWrite(motorSpeedPin, 0);
}

void parkMonster()
{
  debugPrintLn("ATTEMPTING TO PARK MONSTER");
  int parkCounter = 0;
  while (digitalRead(parkPin) == LOW)
  {
    parkCounter = parkCounter + 1;
    if (parkCounter == 2000)
    {
      break;
    }
  }
  debugPrintLn("PARKING MONSTER");
  stopMotor();
}

// ------------------------------------------------------------------
// Monster Definitions
// ------------------------------------------------------------------
void executeWeakMonster() {
  debugPrintLn("CALLING WEAK MONSTER");
  int randomNumber = random(0,3);
  if(randomNumber == 0)
  {
    int eyeCounter = 0;
    while (eyeCounter < 5)
    {
      eyeCounter = eyeCounter + 1;
      executeGlowingEyes();
    }
  }
  else if(randomNumber == 1)
  {
    int eyeCounter = 0;
    while (eyeCounter < 20)
    {
      eyeCounter = eyeCounter + 1;
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
    }
  }
  else if(randomNumber == 2)
  {
    int eyeCounter = 0;
    while (eyeCounter < 1)
    {
      eyeCounter = eyeCounter + 1;
      executeGlowingEyes();
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
    }
  }
}

void executeMildMonster()
{
  debugPrintLn("CALLING MILD MONSTER");
  int randomNumber = random(0,3);
  if(randomNumber == 0)
  {
    turnLidLightsOn();
    mediumGrowlOne();
    executeGlowingEyes();
    turnLidLightsOff();
  }
  else if(randomNumber == 1)
  {
    turnLidLightsOn();
    mediumGrowlTwo();    
    int eyeCounter = 0;
    while (eyeCounter < 10)
    {
      eyeCounter = eyeCounter + 1;
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
    }
    turnLidLightsOff();
  }
  else if(randomNumber == 2)
  {
    turnLidLightsOn();
    mediumGrowlThree();
    int eyeCounter = 0;
    while (eyeCounter < 1)
    {
      eyeCounter = eyeCounter + 1;
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
      turnEyesOn();
      delay(100);
      turnEyesOff();
      delay(100);
      executeGlowingEyes();
    }
    turnLidLightsOff();
  }
}

void executeCrazyMonster()
{
  debugPrintLn("CALLING CRAZY MONSTER");
  int randomNumber = random(1,2);
  if(randomNumber == 0)
  {
    crazyGrowlOne();
    executeGlowingEyes();
    turnLidLightsOn();
    turnEyesOn();
    delay(500);
    turnLidLightsOff();
    turnEyesOff();
    delay(500);
    turnLidLightsOn();
    turnEyesOn();
    delay(500);
    turnLidLightsOff();
    turnEyesOff();
    delay(500);
    turnLidLightsOn();
    turnEyesOn();
    delay(500);
    turnLidLightsOff();
    turnEyesOff();
    delay(500);
    turnLidLightsOn();
    turnEyesOn();
    delay(500);
    turnLidLightsOff();
    turnEyesOff();
    executeGlowingEyes();
  }
  else if(randomNumber == 1)
  {
    crazyGrowlTwo();
    executeGlowingEyes();
    turnLidLightsOn();
    turnEyesOn();
    startFastMotor();
    delay(3000);
    stopMotor();
    turnLidLightsOff();
    executeGlowingEyes();
    startFastMotor();
    turnLidLightsOn();
    delay(3000);
    startSlowMotor();
    delay(1000);
    parkMonster();
    turnLidLightsOff();
    turnEyesOff();
    executeGlowingEyes();
  }
}

void executeRandomMonster()
{
  debugPrintLn("CALLING RANDOM MONSTER");
  int randomNumber = random(0,3);
  if(randomNumber == 0)
  {
    executeWeakMonster();
  }
  else if(randomNumber == 1)
  {
    executeMildMonster();
  }
  else if(randomNumber == 2)
  {
    executeCrazyMonster();
  }
}