Hello Everyone,
I am new to programming and I'm attempting to learn a little by creating a Ghostbusters trap that has lights, sounds, and smoke.
The basic idea is that like in the movie, when the trap is activated it triggers different states.
- Startup
- Open Trap
- Capture Ghost
- Reset
The code below is what I am working from. I'm currently trying to sort out a few things that I am hoping you can help me learn.
First
I am using the following code I found on these forums in an attempt to randomize the capture sound so it plays different ghost sounds when capturing.
char trackName[ 13 ];
int group = 4; // can be 1 to 10 for your code
sprintf( trackName, "trk%02d_%02d.mp3", group, random( 100 ) );
// output will have the form "trk09_07.mp3", assuming the random number was 7
musicPlayer.playFullFile( trackName );
}
I want these to play randomly, but not necessarily all the time. My plan to solve this was to add 5 instances of the base sound so it had a higher chance of playing than the special sounds. Unless there is a more elegant programming way to do this?
Goal: To have the base "clean" sound play most of the time with a few randomized "ghost" sounds thrown in for variety.
Second
The way the trap is programmed is that one of the buttons on the side, when pressed will trigger the trap. This can also be done with a foot pedal. So you can do either or if you like.
On the other side of the trap there is another button. Currently this doesn't do anything. I am wondering if it might be possible to make it a volume switch instead?
The first question is. This switch has two connections for the push and three for the rotary. How would I wire this to the Metro/Music Maker Shield so it would work, as well... what is required from a programming perspective to control the master volume?
Third
If you look at a Ghostbusters trap you'll see there is the main trap and a pedal. In this build, the LED on the pedal does nothing. I would like it to light up and blink along with the main red LED on the trap.
The problem is that the trap and pedal are connected via a hose. This hose as a red and blue wire that connects them both allowing the pedal to be a switch to activate the trap. There are no room for any other wires so this brings me to my dilemma.
I have an "itsybitsy" micro controller. Is it possible to use this, connected to the same switch to trigger and control the LED? I am assuming the switch has no power so I'm not sure how it would power the LED?
Does this make sense?
Thanks!
/********************************************************
ECTOLABS ARDUIO CODE FOR CHARLESWORTH DYNAMICS GHOST TRAP
v1.0 (EctoLabs/Dave Tremaine 2020). Modified from original
code by Jeremy Williams 2016
- Using 3x RGBW NeoPixel Jewels in place of single LEDs.
- Added reusable trapDoor() function to open and close
doors. Includes immediate servo.detach() after each
movement to avoid conflict with NeoPixel library
during LED animation.
- Removed sine tone and replaced with bargraph startup
indicator.
- Rewritten trap states to better mimic movie behaviour.
- Added 'millisDelay' library as alternative to delay()
and improve timing issues.
- Added blue sparking FX after ghost capture.
- Removed extraneous unused code and test functions.
Ghost trap kit designed by Sean Charlesworth 2016-2018
Programming and electronics by Jeremy Williams 2016
********************************************************/
#include <millisDelay.h>
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <Adafruit_LEDBackpack.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <SD.h>
#include <Servo.h>
#include <Adafruit_NeoPixel.h>
// Servos
Servo servoR;
Servo servoL;
int servoCloseL = 180;
int servoCloseR = 0;
int servoOpenL = 70;
int servoOpenR = 120;
int doorDelay = 30;
#define SERVO_RIGHT_PIN 9
#define SERVO_LEFT_PIN 10
// Pins for single LEDs
byte ledRed = 15;
byte ledYellow = 16;
// Pedal & side knob activation pin
byte activationSwitch = 5;
// Smoke machine
byte smokePin = 6;
boolean endingSmoke = false; // smoke effect after trap close?
// Bargraph
Adafruit_24bargraph bar = Adafruit_24bargraph();
// Music Maker Shield
#define SHIELD_RESET -1 // VS1053 reset pin (unused!)
#define SHIELD_CS 7 // VS1053 chip select pin (output)
#define SHIELD_DCS 6 // VS1053 Data/command select pin (output)
#define CARDCS 4 // Card chip select pin
#define DREQ 2 // VS1053 Data request, ideally an Interrupt pin
Adafruit_VS1053_FilePlayer musicPlayer =
Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);
// NeoPixels
#define PIXELS_PIN 5
#define PIXELS_COUNT 21
Adafruit_NeoPixel strip(PIXELS_COUNT, PIXELS_PIN, NEO_GRBW + NEO_KHZ800);
// Initial states
int trapState = 0;
boolean autoreset = false;
boolean redLEDState = true;
boolean remotePressed = false;
boolean smokeActive = false;
boolean opened = false;
boolean beeping = false;
boolean sparking = false;
unsigned long captureStart = 0;
long debounceBuffer = 0;
long redFlashTime = 0;
long whiteFlashTime = 0;
long smokeToggleTime = 0;
byte activeFlasher = 0;
// Setup non-blocking timers
millisDelay captureDelay;
millisDelay bgDelay;
millisDelay sparkDelay;
millisDelay sparkTime;
// Feature toggles
boolean smoke = true;
boolean sfx = true;
// Audio Track Random
char trackName;
/////////
// SETUP
/////////
void setup() {
// Start debug output
Serial.begin(57600);
Serial.println(F("\n** STARTUP **"));
// Bargraph red startup indicator
bar.begin(0x70);
for (uint8_t b = 0; b < 12; b++) {
bar.setBar(23 - b, LED_RED);
bar.writeDisplay();
delay(30);
}
// Calibrate closed door position
trapDoors("calibrate");
// Initialise music player
if (! musicPlayer.begin()) {
Serial.println(F("=> Couldn't find music player, do you have the right pins defined?"));
while (1);
}
Serial.println(F("=> Music player found"));
musicPlayer.GPIO_pinMode(activationSwitch, INPUT);
// Check SD card
if (!SD.begin(CARDCS)) {
Serial.println(F("=> SD failed, or not present"));
while (1);
}
Serial.println(F("=> SD Card OK"));
// Interrupt pin
if (! musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT))
Serial.println(F("=> DREQ pin is not an interrupt pin"));
// Initialise static LEDs
pinMode (ledYellow, OUTPUT);
pinMode (ledRed, OUTPUT);
// Initialise NeoPixels and set to off
strip.begin();
strip.show();
// Make sure smoke pump is inactive
musicPlayer.GPIO_digitalWrite(smokePin, LOW);
musicPlayer.GPIO_pinMode(smokePin, INPUT);
// Clear bargraph
for (uint8_t b = 0; b < 12; b++) {
bar.setBar(23 - b, LED_OFF);
bar.writeDisplay();
delay(50);
}
// Startup complete
if(sfx==true){
musicPlayer.setVolume(50, 50);
musicPlayer.startPlayingFile("start.mp3");
}
Serial.println(F("=> Startup complete (State: 0)"));
Serial.println(F("\nWAITING FOR INPUT..."));
}
void loop() {
checkRemote();
////////////////////
// MAIN TRAP STATES
////////////////////
// OPEN TRAP
if (remotePressed && trapState == 0) {
// NeoPixels on
strip.fill(strip.Color(255,102,255,0));
strip.show();
// Play SFX
if(sfx==true){
musicPlayer.stopPlaying();
musicPlayer.setVolume(30, 30);
musicPlayer.startPlayingFile("open.mp3");
}
// Open doors
trapDoors("open");
// Update trap state
trapState = 1;
Serial.println(F("\n** TRAP OPENED (State: 1) **"));
Serial.println(F("\nWAITING FOR INPUT OR TIMEOUT..."));
// CAPTURING
} else if (remotePressed && trapState == 1) {
captureDelay.start(7800); // time synced with capture SFX
boolean capturing = true;
int strobeUp = 50;
int strobeDown = 50;
int strobeCycle = 0;
// Play capture SFX
if(sfx==true){
musicPlayer.stopPlaying();
musicPlayer.setVolume(30, 30);
char trackName[ 13 ];
int group = 4; // can be 1 to 10 for your code
sprintf( trackName, "trk%02d_%02d.mp3", group, random( 100 ) );
// output will have the form "trk09_07.mp3", assuming the random number was 7
musicPlayer.playFullFile( trackName );
}
// NeoPixels burst
for (uint8_t i=0; i<255; i=i+5) {
//Serial.println(i);
if (i>102) {
strip.fill(strip.Color(255,i,255,i));
} else {
strip.fill(strip.Color(255,102,255,i));
}
strip.show();
delay(1);
}
while(capturing == true){
//Serial.println(captureDelay.remaining());
// NeoPixel strobe
if(strobeCycle == 0){
if(strobeUp<=251){
strobeUp = strobeUp + 4;
}
if(strobeDown>=1){
strobeDown = strobeDown - 1;
}
}
strobeCycle++;
if(strobeCycle==3){
strobeCycle = 0;
}
strip.fill(strip.Color(strobeUp,strobeUp,strobeUp,strobeUp));
strip.show();
delay(20);
strip.fill(strip.Color(strobeDown,strobeDown,strobeDown,strobeDown));
strip.show();
delay(20);
// Smoke on for final 7 seconds
if((captureDelay.remaining() <= 7000) && smokeActive == false && smoke == true){
musicPlayer.GPIO_pinMode(smokePin, OUTPUT);
musicPlayer.GPIO_digitalWrite(smokePin, HIGH);
smokeActive = true;
}
// Close doors after 8 seconds and proceed
if(captureDelay.justFinished()){
trapDoors("close");
strip.fill(strip.Color(255,255,255,255));
strip.show();
capturing = false;
}
}
// Smoke off
if(smoke==true){
musicPlayer.GPIO_digitalWrite(smokePin, LOW);
musicPlayer.GPIO_pinMode(smokePin, INPUT);
}
// NeoPixels off
strip.fill(strip.Color(0,0,0,0));
strip.show();
// Silence for 4 seconds before indicators
bgDelay.start(4000);
boolean silence = true;
while(silence == true){
if(bgDelay.justFinished()){
// Play bargraph SFX
if (sfx == true){
musicPlayer.setVolume(30, 30);
musicPlayer.startPlayingFile("bargraph.mp3");
}
silence = false;
}
}
// Fill bargraph
for (uint8_t b = 0; b < 12; b++) {
bar.setBar(23 - b, LED_YELLOW);
bar.writeDisplay();
delay(20);
}
// Yellow LED on
digitalWrite(ledYellow, HIGH);
// Wait 1 second
delay(1000);
// Start red LED beep loop
if (sfx == true){
musicPlayer.stopPlaying();
musicPlayer.setVolume(40, 40);
musicPlayer.startPlayingFile("beeponly.mp3");
}
// Start timer for sparks
sparkDelay.start(6000);
// Update trap state
trapState = 2;
beeping = true;
Serial.println(F("\n** FULL TRAP (State: 2 FLASHING) **"));
Serial.println(F("\nWAITING FOR INPUT OR TIMEOUT..."));
// RESET TRAP
} else if ((remotePressed && trapState == 2) || autoreset == true) {
// Make sure doors are closed
trapDoors("close");
// Music player stop
musicPlayer.stopPlaying();
// Yellow and Red LEDs off
redLEDState = 1;
digitalWrite(ledRed, LOW);
digitalWrite(ledYellow, LOW);
// NeoPixels off
strip.fill(strip.Color(0,0,0,0));
strip.show();
// Bargraph reset
for (uint8_t b = 12; b > 0; b--) {
bar.setBar(23 - b + 1, LED_OFF);
bar.writeDisplay();
delay(50);
}
// Make sure smoke is off
smokeActive = false;
smokeToggleTime = 0;
musicPlayer.GPIO_pinMode(smokePin, INPUT);
musicPlayer.GPIO_digitalWrite(smokePin, LOW);
// Update trap state
trapState = 0;
autoreset = false;
opened = false;
Serial.println(F("\n** RESET TRAP (State: 0)**"));
Serial.println(F("\nWAITING FOR INPUT..."));
//////////////////
// LOOPING STATES
//////////////////
// OPEN LOOP
} else if (trapState == 1) {
if (sfx == true && musicPlayer.stopped()) {
autoreset = true;
Serial.println(F("\n** TIMEOUT **"));
}
if (opened == false) {
for (uint8_t i=0; i<250; i=i+10) {
strip.fill(strip.Color(255,102,255,i));
strip.show();
delay(1);
}
for (uint8_t i=240; i>0; i=i-10) {
strip.fill(strip.Color(255,102,255,i));
strip.show();
delay(1);
}
opened = true;
}
// Flash NeoPixels randomly
if (millis() > whiteFlashTime){
whiteFlashTime = millis() + 50;
strip.setPixelColor(activeFlasher, strip.Color(255,102,255,255));
strip.show();
delay(30);
strip.setPixelColor(activeFlasher, strip.Color(255,102,255,0));
strip.show();
activeFlasher = random(0, 22);
}
// FULL TRAP LOOP
} else if (trapState == 2 && beeping == true) {
if (sfx == true && musicPlayer.stopped()) {
// Solid red light once beeping stops
digitalWrite(ledRed, HIGH);
redLEDState = 0;
// Update trap state
beeping = false;
Serial.println(F("\n** FULL TRAP (State: 2 IDLE) **"));
Serial.println(F("\nWAITING FOR INPUT..."));
} else {
// Flash red light during beeps
if (redLEDState) {
digitalWrite(ledRed, HIGH);
} else {
digitalWrite(ledRed, LOW);
}
redLEDState = !redLEDState;
// Blue sparks synched with SFX
if (sparkDelay.justFinished()) {
sparkTime.start(5000);
sparking = true;
Serial.println(F("=> Blue sparks"));
}
if (sparkTime.justFinished()) {
sparking = false;
} else if (sparking == true) {
activeFlasher = random(0, 22);
strip.setPixelColor(activeFlasher, strip.Color(0,0,255,0));
strip.show();
delay(30);
strip.setPixelColor(activeFlasher, strip.Color(0,0,0,0));
strip.show();
}
delay(160);
}
}
}
/////////////
// FUNCTIONS
/////////////
void trapDoors(String mode) {
if (mode=="calibrate") {
servoR.write(servoCloseR);
servoL.write(servoCloseL);
}
servoR.attach(SERVO_RIGHT_PIN);
servoL.attach(SERVO_LEFT_PIN);
if (mode=="open") {
servoR.write(servoOpenR);
servoL.write(servoOpenL);
} else {
servoL.write(servoCloseL);
delay(doorDelay);
servoR.write(servoCloseR);
}
delay(300);
servoR.detach();
servoL.detach();
}
void checkRemote() {
if (musicPlayer.GPIO_digitalRead(activationSwitch) == HIGH && millis() > debounceBuffer) {
debounceBuffer = millis() + 1000;
remotePressed = true;
}
else remotePressed = false;
}