Maker.io main logo

Interactive Spooky Halloween Cat

2018-10-26 | By SparkFun Electronics

Courtesy of SparkFun

As we approach the end of October, the spooky Halloween energy is starting to find its way into my world. I'm not much of a Halloween-er myself, but I have always felt that Halloween is an excellent excuse to show off your killer maker skills, creativity and maybe even scare some folks. This week I made a little interactive Halloween decoration that activates when someone walks by.

My Interactive Spooky Halloween Cat consists of a plastic skeleton cat I found at Michaels and an enclosure that I laser cut from black acrylic. Inside the enclosure is a SparkFun BlackBoard as the brains of the project, an Ultrasonic Sensor for distance sensing, a Qwiic MP3 Trigger Board to add some hissing sound effects, a small hamburger speaker for the sound, a small servo motor to create motion in the cat's jaw, 16 WS2812 LEDs to create the red light and a small breadboard to make the circuit building quick and easy. A full list of parts used in this project can be found here.

Image of Interactive Spooky Halloween Cat

Let's take a look at how this project interacts with the world around it. When a user enters within 100 cm of the ultrasonic sensor, the skeleton and eye sockets light up, it makes a loud, angry hissing sound, and the jaw begins moving up and down.

In order to accomplish this interaction (in addition to the circuitry described further down), the servo motor is connected to a clear strand of fishing line tied to the bottom of the cat's jaw bone, so when the servo pulls back, the jaw opens up. I also added some white elastic tied to the top and bottom part of the jaw, kind of like a rubber band on braces, ensuring the jaw closes when the servo pushes back forward. I cut small sound holes in the enclosure above where the speaker is to get the best out of the sound.

I also made some modifications to the WS2812 LEDs. First, I cut two individual LEDs from the strip and soldered them together with hook-up wire at a distance of about one half-inch. Then, I connected these via hook-up wire to the remaining 14 LEDs on the rest of the strip. I unscrewed the cat's skull to remove it from the body and open it up, then carefully drilled holes in the eye sockets, behind which I glued my two individual LEDs. Then I put the head back together and onto on the rest of the skeleton. Finally, I carefully hot glued the remainder of the LEDs down the inside of the cat's spine.

Below is a photograph of the circuit I used in this project. Because some of the parts I used are not available for fritzing diagrams, and because it is hard to see all the connections, I created the table below the image to outline each connection used in this project.

Image of circuit I used in this project

Image of outline each connection used in this project

My program for this project can be found below. You will notice there are two programs included. The first is the program specific for this project's interaction. The second is written for interfacing with the MP3 Trigger Shield and should be pasted into a second tab in your Arduino IDE. The MP3 Trigger code has all been picked up from Nate's example code for this board, which allowed me to add sound incredibly easily!

Interaction Code:

Copy Code
/*Interactive Halloween Cat Sketch by Melissa Felderman for SparkFun Electronics October 2018

MP3 trigger control taken from example code by Nathan Seidle: https://cdn.sparkfun.com/assets/d/d/e/a/1/Qwiic_MP3_Trigger_Examples.zip */


#include <adafruit_neopixel.h></adafruit_neopixel.h>

#include <wire.h></wire.h>

#include <servo.h></servo.h>


byte mp3Address = 0x37; //Unshifted 7-bit default address for Qwiic MP3


#define PIN 6

#define numPix 16

#define trigPin 13

#define echoPin 12


bool state = true;


Servo myservo;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(numPix, PIN, NEO_GRB + NEO_KHZ800);


void setup() {

Serial.begin(9600);

pinMode(trigPin, OUTPUT);

pinMode(echoPin, INPUT);


Wire.begin();

strip.begin();

myservo.attach(9);


myservo.write(180);

mp3ChangeVolume(31); //Volume can be 0 (off) to 31 (max)

}


void loop() {

long duration, distance;

digitalWrite(trigPin, LOW); // Added this line

delayMicroseconds(2); // Added this line

digitalWrite(trigPin, HIGH);

// delayMicroseconds(1000); - Removed this line

delayMicroseconds(10); // Added this line

digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

distance = (duration / 2) / 29.1;


if (distance < 100) { // This is where the LED On/Off happens


mp3PlayTrack(1);

for (int i = 0; i < numPix; i++) {

strip.setPixelColor(i, 255, 0, 0);

} strip.show();


for (int i = 0; i < 8; i++){

myservo.write(90);

delay(1000);

myservo.write(180);

delay(1000);

}

}


else {

for (int i = 0; i < numPix; i++) {

strip.setPixelColor(i, 0, 0, 0);

} strip.show();

myservo.write(180);


}

if (distance >= 100 || distance <= 0) {

Serial.println("Out of range");

}

else {

Serial.print(distance);

Serial.println(" cm");

}

delay(500);

}

MP3 Trigger Code by Nate:

Copy Code
//These are the commands we can send

#define COMMAND_STOP 0x00

#define COMMAND_PLAY_TRACK 0x01 //Play a given track number like on a CD: regardless of file names plays 2nd file in dir.

#define COMMAND_PLAY_FILENUMBER 0x02 //Play a file # from the root directory: 3 will play F003xxx.mp3

#define COMMAND_PAUSE 0x03 //Will pause if playing, or starting playing if paused

#define COMMAND_PLAY_NEXT 0x04

#define COMMAND_PLAY_PREVIOUS 0x05

#define COMMAND_SET_EQ 0x06

#define COMMAND_SET_VOLUME 0x07

#define COMMAND_GET_SONG_COUNT 0x08 //Note: This causes song to stop playing

#define COMMAND_GET_SONG_NAME 0x09 //Fill global array with 8 characters of the song name

#define COMMAND_GET_PLAY_STATUS 0x0A

#define COMMAND_GET_CARD_STATUS 0x0B

#define COMMAND_GET_VERSION 0x0C

#define COMMAND_SET_ADDRESS 0xC7



//Checks the status of the player to see if MP3 is playing

//Returns true if song is playing

boolean mp3IsPlaying()

{

mp3Command(COMMAND_GET_PLAY_STATUS);



delay(20); //Give the QMP3 time to get the status byte from MP3 IC before we ask for it



//01: play, 02: stop, 03: pause

byte playStatus = mp3GetResponse();

if(playStatus == 0x01) return(true);

return(false);

}



//Plays a given track number

//Think of this like a CD. The user can arrange the order of MP3s

//however. playTrack(4) will play whatever is in the 4th file.

void mp3PlayTrack(byte trackNumber)

{

mp3Command(COMMAND_PLAY_TRACK, trackNumber); //Play track

}



//Plays a file that has been named specifically.

//For example: passing in 6 will play F006xxx.mp3

void mp3PlayFile(byte fileNumber)

{

mp3Command(COMMAND_PLAY_FILENUMBER, fileNumber); //Play file number

}



//Stop playing the current track

void mp3Stop()

{

mp3Command(COMMAND_STOP);

}



//Change the equalizer to one of 6 types

void mp3ChangeEQ(byte eqType)

{

//0-normal, 1-pop, 2-rock, 3-jazz, 4-classical, 5-bass

mp3Command(COMMAND_SET_EQ, eqType); //Change equalizer to bass

}



//Get the current status of the Qwiic MP3

byte mp3Status()

{

return(mp3GetResponse());

}



//Checks to see if MP3 player has a valid SD card

boolean mp3HasCard()

{

mp3Command(COMMAND_GET_CARD_STATUS);



delay(20); //Give the QMP3 time to get the status byte from MP3 IC before we ask for it



return(mp3GetResponse());

}



//Get the 8 characters of the song currently playing

String mp3SongName()

{

String thisSongName = "";

mp3Command(COMMAND_GET_SONG_NAME);



delay(50); //Give the QMP3 time to get the name from MP3 IC before we ask for it



Wire.requestFrom(mp3Address, 8); //Song names are max 8 chars



while(Wire.available())

{

thisSongName += (char)Wire.read();

}

return(thisSongName);

}



//Get the number of songs on the SD card (in root and subfolders)

//Limited to 255

byte mp3SongCount()

{

mp3Command(COMMAND_GET_SONG_COUNT); //Get current song count



delay(50); //Give the QMP3 time to get the count from MP3 IC before we ask for it



return(mp3GetResponse());

}



//Change volume to zero (off) to 31 (max)

void mp3ChangeVolume(byte volumeLevel)

{

mp3Command(COMMAND_SET_VOLUME, volumeLevel); //Change volume

}



//Play the next track

//Think of this like a CD. The audio files can be in any order. The user

//sets the file order. This plays the next one.

void mp3PlayNext()

{

mp3Command(COMMAND_PLAY_NEXT);

}



//Play the previous track

//Think of this like a CD. The audio files can be in any order. The user

//sets the file order. This plays the previous one.

void mp3PlayPrevious()

{

mp3Command(COMMAND_PLAY_PREVIOUS);

}



//Checks to see if Qwiic MP3 is responding over I2C

boolean mp3IsPresent()

{

Wire.beginTransmission(mp3Address);

if (Wire.endTransmission() != 0)

return(false); //Sensor did not ACK

return(true);

}



//Pause a currently playing song, or begin playing if current track is paused

boolean mp3Pause()

{

mp3Command(COMMAND_PAUSE);

}



//Change the I2C address

//If you forget what address you've set the QMP3 to then close the

//ADR jumper. This will force the I2C address to 0x36

boolean mp3ChangeAddress(byte address)

{

mp3Command(COMMAND_SET_ADDRESS, address);

mp3Address = address; //Change the global variable to match the new address

}



//Send command to Qwiic MP3 with options

boolean mp3Command(byte command, byte option)

{

Wire.beginTransmission(mp3Address);

Wire.write(command);

Wire.write(option);

if (Wire.endTransmission() != 0)

return(false); //Sensor did not ACK

return(true);

}



//Send just a command to Qwiic MP3

boolean mp3Command(byte command)

{

Wire.beginTransmission(mp3Address);

Wire.write(command);

if (Wire.endTransmission() != 0)

return(false); //Sensor did not ACK

return(true);

}



//Ask for a byte from Qwiic MP3

//The response depends on what the last command was

//It is often the system status but can be song count or volume level

byte mp3GetResponse()

{

Wire.requestFrom(mp3Address, 1);



if (Wire.available())

return (Wire.read());



Serial.println("Error: Sensor did not respond");

return(0);

}



//Returns the firmware version as a float

float mp3GetVersion()

{

mp3Command(COMMAND_GET_VERSION);



Wire.requestFrom(mp3Address, 2); //2 bytes for Version



if (Wire.available() == 0) return 0;

float versionNumber = Wire.read();

versionNumber += (float)Wire.read() / 10.0;



return (versionNumber);

}

I hope you enjoyed reading about this project and that it inspired you to get spooky with your projects this Halloween. Let us know your thoughts in the comments below!

制造商零件编号 PRT-08431
JUMPER WIRE M/M 6" 10PCS
SparkFun Electronics
制造商零件编号 PRT-14426
QWIIC CABLE - 50MM
SparkFun Electronics
制造商零件编号 PRT-12043
BREADBOARD TERM STRIP 1.85X1.38"
SparkFun Electronics
制造商零件编号 COM-14023
SPEAKER 4OHM 3W TOP PORT
SparkFun Electronics
制造商零件编号 ROB-09065
SERVOMOTOR RC 6V GENERIC SUB-MIC
SparkFun Electronics
制造商零件编号 3942
ULTRASONIC SENSOR SONAR DISTANCE
Adafruit Industries LLC
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum