I am working on a Thermal Detonator Star Wars prop for Halloween and I have the LED portion completed pretty much how I would like it to be using blink without delay. I have gotten sound working separately and I am trying to combine the two sketches together. What seems to be happening is the sound plays but the LEDs seize up and do not blink as expected. The Simple WAV sketch does include two "delays" one in setup and one in the "playFile" function I tried replacing the delay in in the "playFile" function with a millis() counter but that did not seem to solve the issue of sound playing but the LEDs not flashing properly. Any advice would be very helpful! Including the code below:
#include <Bounce2.h> // Include the Bounce2 library to handle button debouuncing
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
AudioPlaySdWav playWav1;
AudioOutputAnalog audioOutput;
AudioConnection patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000 sgtl5000_1;
//pin definitions
// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 13
const byte ButtonPin = 0;
const byte LedPins[] = {2, 3, 4};
const byte NrLeds = sizeof(LedPins);
const byte RedLedPin = 1;
//other constants
const unsigned int LedDelayTimeIdle = 1000; // 1 second delay between our LED flashes in our Idle and Arm functions
const unsigned int LedDelayTimeFlash = 500; // 1/2 second delay between our LED flashes in our FlashAllLights Function
const unsigned int LedDelayTimeRead = 5;
//variables
Bounce modeButton = Bounce(); // Instantiate a Bounce object
unsigned long previousMillis = -LedDelayTimeIdle; // Forces the time trigger to start at startup
byte nextLed; //Variable to help us keep track of what LED we are in our loops
byte mode; //Variable to keep track of what mode we are in based on button presses
void setup() {
Serial.begin(9600);
//Set ledPins to output
for (byte i = 0; i < NrLeds; i++) {
pinMode(LedPins[i], OUTPUT);
}
pinMode(RedLedPin, OUTPUT);
digitalWrite(RedLedPin, HIGH);
AudioMemory(8); // Audio connections require memory to work.
modeButton.attach(ButtonPin, INPUT); // Attach the debouncer to the button pin
modeButton.interval(50); // Set the button debouncer interval in ms
//Read the SD Card
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
}
void loop() {
// Checks the current amount of button presses and moves to the correct state based on that
CheckMode();
//Constantly checking if the button has been pressed during the Arm or Idle mode only
if (mode == 0 || mode == 1) {
UpdateButton();
}
}
void playFile(const char *filename)
{
Serial.print("Playing file: ");
Serial.println(filename);
// Start playing the file. This sketch continues to
// run while the file plays.
playWav1.play(filename);
// A brief delay for the library read WAV info
//delay(5);
if (millis() - previousMillis >= LedDelayTimeRead) {
previousMillis = millis();
}
// Simply wait for the file to finish playing.
//while (playWav1.isPlaying()) {
// uncomment these lines if you audio shield
// has the optional volume pot soldered
//float vol = analogRead(15);
//vol = vol / 1024;
// sgtl5000_1.volume(vol);
//}
}
void Idle() {
playFile("TDRUN.WAV"); // filenames are always uppercase 8.3 format
// Our starting state. Leds cycle at 1 second intervals
digitalWrite(8, HIGH); // Turn on the red LED and keep it on
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
//only make the nextLed light up
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], i == nextLed);
}
//goto next led
nextLed++;
if (nextLed >= NrLeds) {
nextLed = 0;
}
}
}
void Arm() {
playFile("TDCOU.WAV");
// Turn the first two LEDs on and leave them on
for (byte i = 0; i < 2; i++) {
digitalWrite(LedPins[i], HIGH);
}
//Check for 1 second delay and then turn third LED on and off
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
digitalWrite(LedPins[2], !digitalRead(LedPins[2]));
}
}
void CountDown() {
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
// Turn all three LEDs on one at a time and leave them on
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], nextLed > i);
}
nextLed++;
//Flash all three LEDs every 500 milliseconds 3 times
if (nextLed > NrLeds + 1) {
mode = 3;
}
}
}
void FlashAllLights() {
//Check if enough time has passed to blink all three LEDs
if (millis() - previousMillis >= LedDelayTimeFlash) {
previousMillis = millis();
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], !digitalRead(LedPins[i]));
digitalWrite(RedLedPin, !digitalRead(RedLedPin));
if (nextLed >= 10) {
mode = 0;
}
}
nextLed++;
}
}
void UpdateButton() {
//Check if the button has been pressed and increment the mode variable by one for each time the button is pressed
modeButton.update();
if (modeButton.rose()) {
mode++;
}
}
void CheckMode() {
// Checks the current amount of button presses and moves to the correct state based on that
if (mode == 0) {
Idle();
} else if (mode == 1) {
Arm();
//Reset loop counting variables for the next time we call the CountDown function
nextLed = 0;
} else if (mode == 2) {
CountDown();
} else if (mode == 3) {
FlashAllLights();
}
}
// A brief delay for the library read WAV info
//delay(5);
if (millis() - previousMillis >= LedDelayTimeRead) {
previousMillis = millis();
}
You have simply replaced the blocking delay function with blocking code of your own. There is nothing magic about millis() that stops it being blocking, you have to write code to stop it blocking by doing something else if the time has not yet passed.
However this is only a small one off delay and I don’t see why it is needed anyway.
What you should be doing is once you have set the sound going use the
while (playWav1.isPlaying())
loop to do things with your LEDs.
This should be in the style of a state machine as you do with the blink without delay example but don’t use millis just use each iteration of the loop to do one step of your anamation with the lights.
I took a long break on this project and I am returning to it now that Haloween is over and my Ghost Trap project is finally complete. I have tried a few different approaches to having the sound play while the LEDs blink but haven't had success in getting it working the way I want it to. I think what is happening is the loop is calling the functions over and over again causing the sound files to get called over and over again... Not sure what to do about that though... Below is the current state of the code...any pointers would be great!
#include <Bounce2.h> // Include the Bounce2 library to handle button debouuncing
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
AudioPlaySdWav playWav1;
AudioOutputAnalog audioOutput;
AudioConnection patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000 sgtl5000_1;
//pin definitions
// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 13
const byte ButtonPin = 0;
const byte LedPins[] = {2, 3, 4};
const byte NrLeds = sizeof(LedPins);
const byte RedLedPin = 1;
//other constants
const unsigned int LedDelayTimeIdle = 1000; // 1 second delay between our LED flashes in our Idle and Arm functions
const unsigned int LedDelayTimeFlash = 500; // 1/2 second delay between our LED flashes in our FlashAllLights Function
const unsigned int LedDelayTimeRead = 5;
//variables
Bounce modeButton = Bounce(); // Instantiate a Bounce object
unsigned long previousMillis = -LedDelayTimeIdle; // Forces the time trigger to start at startup
byte nextLed; //Variable to help us keep track of what LED we are in our loops
byte mode; //Variable to keep track of what mode we are in based on button presses
void setup() {
Serial.begin(9600);
//Set ledPins to output
for (byte i = 0; i < NrLeds; i++) {
pinMode(LedPins[i], OUTPUT);
}
pinMode(RedLedPin, OUTPUT);
digitalWrite(RedLedPin, HIGH);
AudioMemory(8); // Audio connections require memory to work.
modeButton.attach(ButtonPin, INPUT); // Attach the debouncer to the button pin
modeButton.interval(50); // Set the button debouncer interval in ms
//Read the SD Card
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
}
void loop() {
// Checks the current amount of button presses and moves to the correct state based on that
CheckMode();
//Constantly checking if the button has been pressed during the Arm or Idle mode only
if (mode == 0 || mode == 1) {
UpdateButton();
}
}
void playFile(const char *filename)
{
Serial.print("Playing file: ");
Serial.println(filename);
// Start playing the file. This sketch continues to
// run while the file plays.
playWav1.play(filename);
// A brief delay for the library read WAV info
//delay(5);
// if (millis() - previousMillis >= LedDelayTimeRead) {
// previousMillis = millis();
// }
// Simply wait for the file to finish playing.
// while (playWav1.isPlaying()) {
//playWav1.stopPlayback();
//CheckMode();
// }
}
void Idle() {
playFile("TDRUN.WAV"); // filenames are always uppercase 8.3 format
while (playWav1.isPlaying()) {
UpdateButton();
// Our starting state. Leds cycle at 1 second intervals
digitalWrite(8, HIGH); // Turn on the red LED and keep it on
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
//only make the nextLed light up
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], i == nextLed);
}
//goto next led
nextLed++;
if (nextLed >= NrLeds) {
nextLed = 0;
}
}
}
}
void Arm() {
playFile("TDCOU.WAV");
// Turn the first two LEDs on and leave them on
for (byte i = 0; i < 2; i++) {
digitalWrite(LedPins[i], HIGH);
}
//Check for 1 second delay and then turn third LED on and off
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
digitalWrite(LedPins[2], !digitalRead(LedPins[2]));
}
}
void CountDown() {
if (millis() - previousMillis >= LedDelayTimeIdle) {
previousMillis = millis();
// Turn all three LEDs on one at a time and leave them on
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], nextLed > i);
}
nextLed++;
//Flash all three LEDs every 500 milliseconds 3 times
if (nextLed > NrLeds + 1) {
mode = 3;
}
}
}
void FlashAllLights() {
//Check if enough time has passed to blink all three LEDs
if (millis() - previousMillis >= LedDelayTimeFlash) {
previousMillis = millis();
for (byte i = 0; i < NrLeds; i++) {
digitalWrite(LedPins[i], !digitalRead(LedPins[i]));
digitalWrite(RedLedPin, !digitalRead(RedLedPin));
if (nextLed >= 10) {
mode = 0;
}
}
nextLed++;
}
}
void UpdateButton() {
//Check if the button has been pressed and increment the mode variable by one for each time the button is pressed
modeButton.update();
if (modeButton.rose()) {
mode++;
}
}
void CheckMode() {
// Checks the current amount of button presses and moves to the correct state based on that
if (mode == 0) {
Idle();
} else if (mode == 1) {
Arm();
//Reset loop counting variables for the next time we call the CountDown function
nextLed = 0;
} else if (mode == 2) {
CountDown();
} else if (mode == 3) {
FlashAllLights();
}
}