Halloween Box Help

Hello All,

Noob here. Last year I created a wooden box that used an Uno to run some programs. It was centered around an ultrasonic sensor providing distance from the box.

Basic Operation:
When nobody was near, 4 Edison lightbulbs (through a relay) would be illuminated. When you came near, Edisions would extinguish and water vapor (relay again) would descend over a laser trip wire (deflected around by mirrors). When you tripped the Laser target, it would flicker the Edison lights like lightning. After, an LED "Big Dipper/Polaris" Constellation would light up in the throat of the "cave" to the candy.

This year I am having trouble with incorporating a new feature into my Halloween box. I wanted to add sound to go along with all of the different features. I am using the DFPlayer Mini to provide input to a 50W Amp ( 24V power supply) to run bigger sound over some 15-20W Speakers. My problem seems to be coming in my programing. It seems that the DFPlayer needs a delay built into the code to run the complete MP3. This is creating my issue. I want music to be playing continuously until somebody approaches. If I delay the loop it isn't checking the distance to set off the next phase of the box. I hope I was able to communicate this so it makes sense. I will post the code below. Thanks for any guidance in advance.


//Halloween 2023 Version

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

#define Dip 2 //Star
#define Polaris 3 //Star
#define S25 4 //Star
#define S71 5 //Star
#define LAZ 6 //Laser
#define TGT 7 //Tareget for Laser
#define Edison 8 //Relay for Edison lights
#define Fog 9 //Relay for Fog machine
#define MPRX 10 //MP3 RX hookup
#define MPTX 11 //MP3 TX Hookup
#define Echo 12 //Ultrasonic sensor 
#define Trig 13 //Ultrasonic Sensor


long duration, distance; //for UltraSonic
unsigned long refTime;
unsigned long trippoint;

const long threshold = 700;

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);


void setup() {
  mySoftwareSerial.begin(9600);
  Serial.begin(9600);

  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true);
  }

  Serial.println(F("DFPlayer Mini online."));

  myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms

  myDFPlayer.volume(30);

  pinMode (Trig, OUTPUT);
  pinMode (Echo, INPUT);
  pinMode (Dip, OUTPUT);
  pinMode (S25, OUTPUT);
  pinMode (S71, OUTPUT);
  pinMode (Polaris, OUTPUT);
  pinMode (LAZ, OUTPUT);
  pinMode (TGT, INPUT);
  pinMode (Edison, OUTPUT);
  pinMode (Fog, OUTPUT);

  digitalWrite (Dip, LOW);
  digitalWrite (S25, LOW);
  digitalWrite (S71, LOW);
  digitalWrite (Polaris, LOW);

}

void loop() {
  digitalWrite (Trig, LOW);
  delayMicroseconds (5);
  digitalWrite (Trig, HIGH);
  delayMicroseconds (10);
  digitalWrite (Trig, LOW);
  duration = pulseIn (Echo, HIGH);
  distance = (duration / 2) / 29.1;
  Serial.print (distance);
  Serial.println ("cm");

  if (distance < 140) {
    digitalWrite (Fog, HIGH);
    digitalWrite (Edison, LOW);
    digitalWrite (LAZ, HIGH);
    myDFPlayer.play (4); //"Who disturbs my Slumber?"
    delay (7000);

  }
  else  {
    digitalWrite (Fog, LOW);
    digitalWrite (Edison, HIGH);
    digitalWrite (LAZ, LOW);
    myDFPlayer.play (1); //Play 20's Music till approached
    delay (5000);
   

  }

  if ((distance < 140) && (digitalRead(TGT) == 0)) {
    myDFPlayer.play (5); //Thunder
    digitalWrite (Edison, HIGH); //Lightning Sequence
    delay (150);
    digitalWrite (Edison, LOW);
    delay (70);
    digitalWrite (Edison, HIGH);
    delay (50);
    digitalWrite (Edison, LOW);
    delay (150);
    digitalWrite (Edison, HIGH);
    delay (40);
    digitalWrite (Edison, LOW);
    delay (25);
    digitalWrite (Edison, HIGH);
    delay (300);
    digitalWrite (Edison, LOW);
    delay (1500);
    myDFPlayer.pause();
    delay (500);
    digitalWrite (Dip, HIGH);
    digitalWrite (S25, HIGH);
    digitalWrite (S71, HIGH);
    delay (1000);
    for (int i = 0; i < 255; i++) {
      analogWrite (Polaris, i);
      delay (5);
    }
    for (int i = 255; i > 0; i--) { //Pulsing North Star
      analogWrite (Polaris, i);
      delay (5);
    }
    digitalWrite (Dip, LOW);
    digitalWrite (S25, LOW);
    digitalWrite (S71, LOW);
    myDFPlayer.play (6);
    delay (500);
    for (int i = 0; i < 255; i++) {
      analogWrite (Polaris, i);
      delay (5);
    }
    for (int i = 255; i > 0; i--) {
      analogWrite (Polaris, i);
      delay (5);
      
    }

    digitalWrite (Polaris, LOW);

    delay (5000);
  }

}

Thanks Alto, I saw so many posts like this before and thought I wouldn't be that guy. I believe I fixed it right after my initial post.

2 Likes

Rather than put in a delay() after starting a track, you just need to keep track of whether or not you are in the middle of playing something or not. I'm not sure of the DrRobotDFPlayer library, but there are others available that have a .isPlaying() sort of function that will tell you if you are in the middle of a track or not.

You can then use this information to decide if you want to start a new track, repeat a track, etc. and still do your sonar pinging. You might also investigate a state machine (Search finite state machine)

1 Like

Try an interrupt on approaching bodies, then only disconnect the audio (as opposed to pausing the audio). Many threads of great length remain unsolved, trying to match hardware, library and sketch, trying IsPlaying() and isBusy pin.

There is a BUSY pin on the module.
It goes Low whenever a track is 'playing'.

After researching a bit I am trying to mentally picture this. Are you thinking the pinging would be the interrupt? If so would i just have an initial distance reading be the previous state and then any change to it would cause the interrupt to trigger? Thank you for the help.

The sonar/HC-SR04 only gives time-of-flight of the echo, not an interrupt (as far as I know). You would use a time/distance threshold. I should not have used "interrupt" in my previous description, rather, "condition" (of shortening echo or distance).

That makes sense to me. I have been running everything off of condition so far. (I think) My problem comes with having to put a delay in for running MP3Files, especially the "no ones around" music. Thanks for your patience.

Could you possibly reduce this to a much simpler demo sketch for easier understanding and discussion here please? Ideally a couple of alternative MP3 tracks and one or two buttons, but that still suffers the problem you describe.

@runaway_pancake has the pin to monitor, but a read on many "dfminiplayer" topics yields not-too-many solutions. That is why I suggest "cut the cable to the audio" (not wanting to use the word "interrupt") when a visitor arrives/departs.

//Simplified Box

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

#define Constellation 2 //Big Dipper
#define LAZ 6 //Laser
#define TGT 7 //Tareget for Laser
#define Edison 8 //Relay for Edison lights
#define MPRX 10 //MP3 RX hookup
#define MPTX 11 //MP3 TX Hookup
#define Echo 12 //Ultrasonic sensor 
#define Pulse 13 //Ultrasonic Sensor

long duration, distance; //for UltraSonic


SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value); //setting up Serial for DFPlayer Mini

void setup() {


  pinMode (Pulse, OUTPUT);
  pinMode (Echo, INPUT);
  pinMode (Constellation, OUTPUT);
  pinMode (LAZ, OUTPUT);
  pinMode (TGT, INPUT);
  pinMode (Edison, OUTPUT); //exterior lights

}

void loop() {
  digitalWrite (Pulse, LOW);  //Ultra Sensor to determine distance
  delayMicroseconds (5);
  digitalWrite (Pulse, HIGH);
  delayMicroseconds (10);
  digitalWrite (Pulse, LOW);
  duration = pulseIn (Echo, HIGH);
  distance = (duration / 2) / 29.1;
  

  if (distance < 140) { //When within 140 CM, Exterior lights off and laser is on
    digitalWrite (Edison, LOW);
    digitalWrite (LAZ, HIGH);
    myDFPlayer.play (4); //"Who disturbs my Slumber?"
    delay (7000);

  }
  else  {
    digitalWrite (Edison, HIGH); //If outside 140, exterior lights on, laser off
    digitalWrite (LAZ, LOW);
    myDFPlayer.play (1); //Play 1920's Music till approached
    delay (5000);


  }

  if ((distance < 140) && (digitalRead(TGT) == 0)) { //within 140 and laser sensor tripped
    myDFPlayer.play (5); // thunder sounds
    digitalWrite (Edison, HIGH); //lightning flashes on exterior lights
    delay (150);
    digitalWrite (Edison, LOW);
    delay (70);
    
    delay (500);
    digitalWrite (Constellation, HIGH); //Big Dipper turns on


    delay (5000);
  }

}

https://drive.google.com/file/d/1J1axHCuxuDu8bcRRoQs2fCJabdrUs5QQ/view?usp=drive_link

This is a video of the version from last year to give you a better idea.

I don't use Google Drive.

Thanks, but not quite what I had in mind, which was to lose the lasers, ultrasonics, etc!
:slightly_smiling_face:

I'll take a closer look in the morning. Will probably try substituting your sensor-derived distances with simple button presses. Although that might well be over-simplifying rather too much, it will at least mean I'll stand a chance of testing anything I come up with.

So could this be a case to use a While condition?

Have you been able to stop or pause a track?

The only way that i can get a track to play for longer than a fraction of a second is by adding a delay. Otherwise I just get clicking for each revolution of the loop. I know you didn't mean to say interrupt in your earlier post but it is the type of operation that this project needs. Checking the distance on the side while routing you into the correct state. Maybe the finite state machine that @blh64 suggested would work? I may be showing my ignorance of programing

Perhaps I change my sensor to a PIR inferred sensor and then I could use it as an interrupt?

Sounds like a good experiment. PIR has a few settings.

1 Like

I spent far too long today preparing MP3s and getting them reliably indexed and playable from a micro SD. Fun though. So I've made frustratingly little progress with coding, sorry. My main difficulty is getting quick response to button presses, to switch between the four files. I see the discussion about using Arduino interrupts, which I'm tempted to pursue. But I've not used them before so I know that would involve a fair bit of learning. Haven't given up, but won't get any significant time on it for a day or two. In the unlikely event that I get a breakthrough flash of inspiration...

Although I expect you already have the audio aspect of the project covered, here are the individual files I was working with.

25-saber-humming (58 s)

26-laser-sword-SF (17 s)

27-Light-Saber (2 s)

28-swing (2 s)

1 Like