Stop a song that is playing and trigger another one once an IR is broken

Hi guys,

I am working in a project for my foosball table. I want to play random audio files during the match (usually longer than 1 minute each) in an infinity loop until an IR sensor is broken (a goal has been scored). Once this happens, I want to stop the ongoing audio, trigger another one (the announcer yelling goal) and and change the score on my 7-segment LED. Everything is working fine on its own. My problem is when I put everything to work simultaneously.

My problem:

The way I am coding this, I keep my arduino busy all he time, so it never gets a chance to read the IR input and trigger the "goal" loop. This is my first arduino project (very ambitious, I know) so I am kind of stuck with knowing what to do. I am just looking for some advises to guide me through the easier solution. I am not sure whether I need to work with two arduinos or simple using the right code structure would work just fine. I am using arduino uno and the Adafruit Audio FX Sound Board to play the audio files. This is the code: Thanks a lot!!!!

/* 
  Adapted from teacher Morgenthaler and student Leo at Swiss school www.oszt.ch
*/

#include <Wire.h>                                 
#include <Adafruit_GFX.h>                         
#include "Adafruit_LEDBackpack.h"
#include <Adafruit_Soundboard.h>
#include "SoftwareSerial.h"

long randNumber;
long randGol;

// Initailizing the 7-segment display 
Adafruit_7segment matrix = Adafruit_7segment();   

// Defining varialbes for the two goals
int Rot;                                          
int Blau;

// Defining Arduino's board-LED. For controlling purpose only during coding
#define LEDPIN 13                                 
                                                 
// Defining the two pins the data connections of the breakbeam sensors 
#define SENSORPIN 4                               
#define SENSORPIN2 2  

// Defining the pin for the back to zero button                            
#define SENSORPIN3 7   

#define SFX_TX 3
#define SFX_RX 6
// Connect to the RST pin on the Sound Board
#define SFX_RST 10

SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX);

Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST);


 // Initalizing and defining states for the breakbeam sensors and the back to zero button 
int sensorState = 0, lastState=0;               
int sensorState2 = 0, lastState2=0;              
int sensorState3 = 0, lastState3=0;              

char *soundName[] = {
  "01      OGG",
  "02      OGG",
  "03      OGG",
  "04      OGG"

};

char *golSound[] = {
  "gol1    OGG",
  "gol2    OGG"
  
};
                                                 
void setup() {   

       Serial.begin(9600);

     
  // Initializing Arduino's board LED for output                                                
  pinMode(LEDPIN, OUTPUT);                        
                                                  
  // Initializing pin for input from breakbeam sensor                                               
  pinMode(SENSORPIN, INPUT);  
  // Activating the pullup resistor for this pin to make the two sensor states clear for Arduino                    
  digitalWrite(SENSORPIN, HIGH);                  

  // Same for other breakbeam sensor
  pinMode(SENSORPIN2, INPUT);                     
  digitalWrite(SENSORPIN2, HIGH);

  // Same for back to zero button
  pinMode(SENSORPIN3, INPUT);                     
  digitalWrite(SENSORPIN3, HIGH);

  // Start value for goals
  Rot = 0;                                         
  Blau = 0;

  //Define address for display
  matrix.begin(0x70);         

                       ss.begin(9600);

                       randomSeed(analogRead(0));

sfx.playTrack("inicio  OGG");
                       
}



void loop(){
randNumber = random(5);
sfx.playTrack(soundName[randNumber]);


                                        
  // Next 3 lines: Read and save states of the 2 sensors and the back to zero button                                               
  sensorState = digitalRead(SENSORPIN);           
  sensorState2 = digitalRead(SENSORPIN2);
  sensorState3 = digitalRead(SENSORPIN3);         

  
  // The following lines are for displaying the the goal values on the display.
  // This if argument prevents from displaying a zero with numbers under 10
  if(Rot / 10)
  // Display position 0 will show the first position of goal variable Rot
  matrix.writeDigitNum(0, (Rot / 10) );    
    // Display position 1 will show the second position of goal variable Rot      
    matrix.writeDigitNum(1, Rot % 10 );
    // A colon will be displayed between the two varialbes            
    matrix.drawColon(true);  
    
 // This if argument prevents from displaying a zero for other variable 
  if(Blau / 10)
    // Display position 3 will show the first position of goal variable Blau                     
    matrix.writeDigitNum(3, (Blau / 10) );   
    
    //  Display position 4 will show the second position of goal variable Blau     
    matrix.writeDigitNum(4, Blau % 10 );    
    // Display everyting above      
    matrix.writeDisplay();                       


   // 3 if-else arguments following for the two breakbeam sensors and the back to zero button 
   // If sensor light beam is broken, system reads status low and turns the control LED on                                     
  if (sensorState == LOW) {                 
    digitalWrite(LEDPIN, HIGH);             
  } 
  // otherwise no power for the control LED
  else {
    digitalWrite(LEDPIN, LOW);            
  }
  
  // If SensorState does not differ from lastState...
  // (The "!" reverses the signal state to function correclty.
  // Without it, the goal values constantly increase when light beams ar UNbroken)
  if (sensorState && !lastState) {                
                                                  
    // ...The message Unbroken would be displayed on Arduino's serial monitor                                              
    Serial.println("1 Unbroken");                 
                                               
  } 
  // If it differs, "Broken" would be displayed and the goal variable Red is increased. 
  if (!sensorState && lastState) {
    Serial.println("1 Broken");                                               

                randGol = random(2);

    Rot = Rot + 1;      

sfx.playTrack(golSound[randGol]);

                             
  }
  
  // Same for other breakbeam sensor
  if (sensorState2 && !lastState2) {               
    Serial.println("2 Unbroken");
  }
  if (!sensorState2 && lastState2) {
    Serial.println("2 Broken");
    Blau = Blau + 1;        
 
    sfx.playTrack("1001    OGG");
                      
  }
  // Check status of back to zero button 
  if (sensorState3 && !lastState3) {               
  }
  // If pressed, the serial display would show "reset" and both goal variables are reset to zero.
  if (!sensorState3 && lastState3) {
    Serial.println("reset");                       
    Blau = 0;                                      
    Rot = 0;
    // Clears everything displayed on LED
    matrix.clear();
  }
 // Save the last sensor states 
  lastState = sensorState;                     
  lastState2 = sensorState2;                       
  lastState3 = sensorState3;
}                                                  
// End of programm. Restart void loop

I feel like you've actually got a good start here. Keep breaking it down piece by piece. I THINK your problem is that you don't have an ELSE command to revert the IF's back to normal state. Check out the StateCase example in the IDE to explain further. I don't have any experience with the Adafruit Soundboard, but it appears to work like an audioshield or DF Mini, and the audio part appears to be ok. Can you be more specific as to where the code is breaking down during use?
/

Thanks for the help, man

Would the StateCase example the same as IfStatementConditional? I didn't find any specific example with that description.

halloweenrick:
Can you be more specific as to where the code is breaking down during use?
/

So, when I turn the arduino on, the infinity loop "sfx.playTrack(soundName[randNumber])" starts and puts the board in a busy state until the current audio finishes. Once one audio is finished, another one starts right away (by the way, I also wanted to add a delay between the audios). Since those audios are long and are played in loop, this busy period last a good chunk of time. So, if any other action (like a score) happens during this time, nothing else happens. Neither the "goal" audio is triggered (sfx.playTrack(golSound[randGol]) nor the scoreboard register the score.

I need a way to trigger the audio file sfx.playTrack(soundName[randNumber]) and bring the board back to a waiting period so it would be able to receive information from the other inputs, like the IR beam. Do you think I can do that with the proper code? You mentioned something about adding a else for my ifs.

Thanks again

Sorry, yes it was switchCase example in the IDE. Under 05. Control in the Examples menu.

So an OVERLY Simplified way to think of this is: you have 3 "cases"
(if I understand correctly)

Case 1: Normal Gameplay - Let's call this "NormalState"

  • Audio gaming music plays from one folder on the SD Card as game is played

Case 2: Red Scores from IR sensor on Red Goal- let's call this "RedState"
-Music plays to signify red scoring
-Scoreboard adds +1 to red score total

Case 3: Blue scores from IR Sensor on Blue Goal- let's call this "BlueState"
-Music plays to signify blue scoring
-Scoreboard adds +1 to blue score total

Take a look at the sample code of switchCase to see how the states change, and revert back to Case 1. Here is a program of mine that operates a halloween prop that uses State change wording to operate:

[code// Linear Actuator Tombstone with Arduino Uno with a SEEED Studio Relay with a Raising Relay, A Lowering Relay, and a 12V LED Bulb to shine on tombstone during raising process.
 
const byte pin5 = 5;  //Linear Actuator Lowering Relay #3
const byte pin4 = 4;  //Linear Actuator Raising Relay #4
const byte LED = 7;   //LED Bulb on Relay #1 
const byte heartBeat = 13;
 
boolean blockingFlag = true;
 
unsigned long Millis13;
unsigned long lastMillis;
unsigned long wait = 3000;
 
enum States {
  StateStart, State2, State3, State4, State5, 
};
States mState = StateStart;
 
void setup()
{
  pinMode(heartBeat, OUTPUT);
 
  pinMode(pin5, OUTPUT);
  pinMode(pin4, OUTPUT);
  pinMode(LED, OUTPUT);
 
  delay(3000);
 
} //END of setup()
 
 
void loop()
{
  //**********************************
  //some code to check for blocking sketches
  if (millis() - Millis13 >= 200)
  {
    digitalWrite(heartBeat, !digitalRead(heartBeat)); //toggle D13
    Millis13 = millis(); //re-initalize
  }
 
 
  //********************************** BWD being used here
  if (millis() - lastMillis >= wait)
  {
    nonBlockingMethod();
  }
 
  
} //END of loop()
 
 
 
// ******************* Functions *******************
 
void nonBlockingMethod()
{
  switch (mState)
  {
    //***************************    
    case StateStart:
      {
        digitalWrite(pin5, HIGH);    // Raising Relay for Linear Actuator raises for 15 seconds
        digitalWrite(LED,HIGH);      // LED Bulb comes on
        wait = 15000;                  // wait for 15 seconds
        lastMillis = millis();
        mState = State2;
      }
      break;
 
    //***************************
    case State2:
      {
        digitalWrite(pin5, LOW);    // Raising Relay turns off, Linear Actuator remains raised
        digitalWrite(LED,HIGH);     //turn on LED Bulb
        wait = 50000;                  // wait for 50 seconds
        lastMillis = millis();
        mState = State3;
      }
      break;
 
    //***************************
    case State3:
      {
        digitalWrite(pin4, HIGH);     // Lowering Relay for Linear Actuator lowers for 15 seconds
        digitalWrite(LED, LOW);       //LED Bulb Turns off
        wait = 15000;                 // wait for 15 seconds
        lastMillis = millis();
        mState = State4;
      }
      break;
 
    //***************************    //LEDs OFF
    case State4:
      {
        digitalWrite(pin4, LOW);     // Lowering Relay for Linear Actuator remains off for 50 seconds
        digitalWrite(LED, LOW);      //LED Bulb remains off
        wait = 50000;                  // wait for 50 seconds
        lastMillis = millis();
        mState = State5;
      }
      break;
 
     
    //***************************
    case State5:
      {
        blockingFlag = true;
        wait = 3000;                 // wait for 3000ms
        mState = StateStart;
      }
 
      break;
 
    //***************************
    default:
      {
      }
      break;
 
  } //END of switch case
 
} //END of nonBlockingMethod()]

So in mine, each case begets another, till I revert back to StateStart at the end of Case 5.

So if we have the assumed 3 cases above, and add in the IR Inputs:

-NormalState goes until Red State or Blue State triggers
-RedState or BlueState trigger occurs with audio and scoreboard, THEN changes back to NormalState.

If/else is its own "example", but I feel switchCase/StateChange would be better for you. Hope that helps.

You are a genius, man.

I used switchCase to create different circumstances and it worked nicely. I am sharing the solution since it might help other people. Thanks a lot

announcer = 0;
switch (announcer) {
    
    case 0:
   
    Serial.println("Start");
    sfx.playTrack("1002    OGG");   

    announcer = 1;
    break;
    
    case 1:
    if (sensorState == HIGH || sensorState2 == HIGH) { 
    
    randNumber = random(0, 30);
    Serial.println(randNumber); //print the number

    sfx.playTrack(soundName[randNumber]); // announcer speaking
    
     
      }
      if (sensorState == LOW) announcer = 2; //Team 1 scores
      if (sensorState2 == LOW) announcer = 3; //Team 2 scores    
      
      break;

case 2:
      
      Serial.println("Team 1 goal");
      sfx.stop();
      randGol = random(0, 5);
      Serial.println(randGol);//print the number
      Rot = Rot + 1;      
      sfx.playTrack(gol[randGol]);
      
      announcer = 1;
      break;

    case 3:
      
     Serial.println("Team 2 goal");//print the number
      sfx.stop();
      Blau = Blau + 1;         
      sfx.playTrack("1001    OGG");
     announcer = 1;
      
      break;

The project works fine. The only issue I have know is about the response time of my IR sensor. To trigger the audios after scoring a goal (case 2 and 3) the ball needs to travel slow enough to register it. Before to move this post to the sensor topic, I just wanted to check whether this slow response time could be duo to any issue in my code. These IR break beans (Adafruit 3mm LED) are supposed to have a very fast response, so I don't think it should be anything with the sensor itself.

Thanks

FANTASTIC!!!! Glad I could help. As far as your PIR, the code itself can't help there. You may try a larger PIR sensor. A 3mm PIR has got to be tiny, possibly that is the problem? Good job on the project.