Activating a Sequence of LEDs Timed to Audio - Troubleshooting

Hey Everyone,

I apologize in advance if I'm unable to format my code correctly in this post. It's my first in this forum.

I've avoided asking questions because I'm trying to learn all this myself but I've reached a point where I am at a loss.

Project Overview

I'm building a 350 Scale Enterprise Refit from the original series Star Trek movies. I will incorporate 6 buttons, corresponding to each of the 6 movies. Each button activates a 'scene' from one of the movies. A selection of audio from the movie will play (using SD card reader & external amp) combined with a timed LED sequence matching what's happening in the scene.

In this particular instance, I am working on the scene from ST:II, a battle between the Enterprise and Khan.

Scene Reference: Reliant's Prefix Code (1-6-3-0-9) - YouTube
The audio sequence begins at 8:04 and runs to 9:28 in the above Youtube Clip.

The Problem

What I am trying to achieve is lighting a sequence of LEDs in time with the Prefix input (8:21 in the above clip) using a shift register. This has been successful. The LEDs light in sequence with Spock's button pushes. They remain on until the first phaser is fired (8:42) and then shut off.

What I would like next to happen is to blink an LED rapidly (on/off cycle 25/25ms) while the phasers are active, then shut off. At the second phase blast, the same happens again.

I have been able to make the light turn ON steadily, but not turn OFF. And I have been able to get the light to turn ON in time with the phaser blast.

I have tried using the basic blink without delay sketch, which works when it's outside the button press if statement. But when it is placed within that statement, it turns on and does not blink.

I have also tried using the LedFlasher library, which does the same. Works fine outside the code range for the effect, but only turns on and does not blink while inside it.

I have also tried variations of both using the setup array, a new array, and various wild guesses.

I am not expecting anyone to write the code for me. I need to understand how this works so I can replicate and modify as needed for other functions. I am hoping someone can atleast tell me why the BWD sketch doesnt work in this instance, or what I might consider trying, or need to consider, or need to look at to get it to work.

The code is based on Ostrich Longneck's Arduino For Starships Series. It took me a long time and a lot of reading and trial and error with the code to figure out how the array works with the shift register.

Here is the full code:

#include <pcmRF.h>
#include <pcmConfig.h>
#include <TMRpcm.h>


// Audio TMRpcm Library by Various Contributors
#include <TMRpcm.h>
#include <SPI.h>
#include <SD.h>
#include <LedFlasher.h>


  const int TwokButton = 18; // TWOK Effect
  int TwokButtonState = 0; // flag to check if button has been pressed
// Set up Arrays & Variables used for the Prefix Effect 
  const int PreFix [5] = {1000, 1980, 2860, 3400, 19692}; // Timeline in milliseconds between floodlights 
  int PreFixStart = 18500; // Start-up delay
  int PreFixStack = 1; // Variable used to calculate value to be written to 74HC595 Shift Register
  int PreFixTimer = 0; //Light Number variable 
  long PreFixSeq = 0; //
  long PreFixSeqStart = 0; // Time since sequence was started
  
  // Set up Arrays & Variables used for the Phaser Effect 
  const int Phaser [2] = {19692, 22000}; // Phaser timing array 
  long PhaserStart = 0; // The time at which the phaser effect should start 
  long PhaserEnd = 0; // The time at which the phaser effect should end 
  int PhaserTimer = 0; 
  int PhaserState = LOW; // Intitial phaser state when attemping to use Blink w/o delay
  const int PhaserLed = 6; // Phaser LED Pin 
  unsigned long PhaserPulse = 25; // Used in Blink without delay sketch
  unsigned long previousMillis = 0; // Used in Blink without delay sketch
  
// Set up variables for 74HC595 Shift Register
  int latchPin = 4; // Pin connected to ST_CP of 74HC595
  int clockPin = 2; // Pin connected to SH_CP of 74HC595
  int dataPin = 3; // Pin connected to DS of 74HC595


 

 
 int firstSound = 1; // flag to finish playing intro before other sound effects start
  char mychar;
  const int CS_PIN = 10;
  TMRpcm audio;

  
int acc = 0 ;  // integration state
int acc2 = 0 ;

void noise_shape (int sample)  // here sample is signed 16 bit value
{
  acc += sample ;  // two integral stages
  acc2 += acc ;
  int top_part = acc2 & 0xFF00 ;   // get 8 bits to be output
  acc -= top_part ;  // feedback negatively to both integral stages
  acc2 -= top_part ;   
  analogWrite (9, 0xFF & (top_part >> 8)) ;
}

  void setup() 
  {
  // Setup for TWOK Phaser
    pinMode (Phaser, OUTPUT);

    
  // Set up pins used by shift register;
    pinMode (latchPin, OUTPUT);
    pinMode (clockPin, OUTPUT);
    pinMode (dataPin, OUTPUT);


  // Clear shift register
    digitalWrite (latchPin, LOW);
    shiftOut (dataPin, clockPin, MSBFIRST, 0);
    digitalWrite (latchPin, HIGH);

    
  // Set up pins for audio effects
    audio.speakerPin = 9; // Speaker pin on output pin 9
    //Serial.begin(9600);
  //Serial.println("Initializing Card");
  pinMode(CS_PIN, OUTPUT);
  if(!SD.begin(CS_PIN)) {
    //Serial.println("Card Failure");
    return;
  }
  //Serial.println("Card Ready");
  audio.setVolume(5);
  audio.quality(1);
  

  pinMode (TwokButton, INPUT); //Twok Effect on 18

  
    }  // end of setup

void loop() // The main program
  {


   


  TwokButtonState = digitalRead (TwokButton);
  if (TwokButtonState == HIGH){
      audio.play("TWOK.wav");

   PreFixSeq = millis() + PreFixStart;
   while (millis() < PreFixSeq){}  //Do Nothing Until Correct Audio Time has Elapsed 

   PreFixSeqStart = millis(); // Marks time when first leds turns on 

 for(PreFixTimer; PreFixTimer < 5; PreFixTimer += 1){
    digitalWrite (latchPin, LOW);
    shiftOut (dataPin, clockPin, MSBFIRST, PreFixStack);
    digitalWrite (latchPin, HIGH);
    PreFixStack = PreFixStack * 2 + 1;
    PreFixSeq = PreFixSeqStart + PreFix [PreFixTimer];
    while (millis() < PreFixSeq) {  } // Pauses between Prefix Lightup sequence
   digitalWrite (latchPin, LOW); // Clear the Shift register at the first phaser fire 
    shiftOut (dataPin, clockPin, MSBFIRST, 0);
    digitalWrite (latchPin, HIGH);
      }
    PhaserStart = PreFixSeqStart + Phaser [0]; // An attempt to define the time at which Phaser LED should turn on
    PhaserEnd = PreFixSeqStart + Phaser [1]; // An attempt to define the time at which the Phaser LED should turn off
 if(PhaserStart == millis()){
 //  digitalWrite(PhaserLed, HIGH); // If I remove BWD code, this works to turn it on at the correct time but doesnt blink.  

    // Below is the Blink without delay code. 

    unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= PhaserPulse) {
// save the last time you blinked the LED
      previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (PhaserState == LOW) {
     PhaserState = HIGH;
   } else {
      PhaserState = LOW;
   }

    // set the LED with the PhaserState of the variable:
   digitalWrite(PhaserLed, PhaserState);

  }
 }
  }

  }  // end of loop

Below are the snippets of code I am specifically working with starting with the setup functions:


  const int TwokButton = 18; // TWOK Effect
  int TwokButtonState = 0; // flag to check if button has been pressed
// Set up Arrays & Variables used for the Prefix Effect 
  const int PreFix [5] = {1000, 1980, 2860, 3400, 19692}; // Timeline in milliseconds between floodlights 
  int PreFixStart = 18500; // Start-up delay
  int PreFixStack = 1; // Variable used to calculate value to be written to 74HC595 Shift Register
  int PreFixTimer = 0; //Light Number variable 
  long PreFixSeq = 0; //
  long PreFixSeqStart = 0; // Time since sequence was started
  
  // Set up Arrays & Variables used for the Phaser Effect 
  const int Phaser [2] = {19692, 22000}; // Phaser timing array 
  long PhaserStart = 0; // The time at which the phaser effect should start 
  long PhaserEnd = 0; // The time at which the phaser effect should end 
  int PhaserTimer = 0; 
  int PhaserState = LOW; // Intitial phaser state when attemping to use Blink w/o delay
  const int PhaserLed = 6; // Phaser LED Pin 
  unsigned long PhaserPulse = 25; // Used in Blink without delay sketch
  unsigned long previousMillis = 0; // Used in Blink without delay sketch
  
// Set up variables for 74HC595 Shift Register
  int latchPin = 4; // Pin connected to ST_CP of 74HC595
  int clockPin = 2; // Pin connected to SH_CP of 74HC595
  int dataPin = 3; // Pin connected to DS of 74HC595

The Loop Code

void loop() // The main program
  {


   


  TwokButtonState = digitalRead (TwokButton);
  if (TwokButtonState == HIGH){
      audio.play("TWOK.wav");

   PreFixSeq = millis() + PreFixStart;
   while (millis() < PreFixSeq){}  //Do Nothing Until Correct Audio Time has Elapsed 

   PreFixSeqStart = millis(); // Marks time when first leds turns on 

 for(PreFixTimer; PreFixTimer < 5; PreFixTimer += 1){
    digitalWrite (latchPin, LOW);
    shiftOut (dataPin, clockPin, MSBFIRST, PreFixStack);
    digitalWrite (latchPin, HIGH);
    PreFixStack = PreFixStack * 2 + 1;
    PreFixSeq = PreFixSeqStart + PreFix [PreFixTimer];
    while (millis() < PreFixSeq) {  } // Pauses between Prefix Lightup sequence
   digitalWrite (latchPin, LOW); // Clear the Shift register at the first phaser fire 
    shiftOut (dataPin, clockPin, MSBFIRST, 0);
    digitalWrite (latchPin, HIGH);
      }
    PhaserStart = PreFixSeqStart + Phaser [0]; // An attempt to define the time at which Phaser LED should turn on
    PhaserEnd = PreFixSeqStart + Phaser [1]; // An attempt to define the time at which the Phaser LED should turn off
 if(PhaserStart == millis()){
 //  digitalWrite(PhaserLed, HIGH); // If I remove BWD code, this works to turn it on at the correct time but doesnt blink.  You can see what happens when this code is executed in Video 01. 

    // Below is the Blink without delay code. You can see what this code does in Video 02

    unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= PhaserPulse) {
// save the last time you blinked the LED
      previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (PhaserState == LOW) {
     PhaserState = HIGH;
   } else {
      PhaserState = LOW;
   }

    // set the LED with the PhaserState of the variable:
   digitalWrite(PhaserLed, PhaserState);

  }
 }
  }

Thanks for reading!

I can only post two links so here are the links to the YouTube videos showing what happens in each sketch I use. (see code for where each applies)

Video 01
Video 02

I figured it out using the code below. In order to get the Phaser LEDs to pulse and to do it only when that particular button was pressed, I had to include two conditions. The first condition for flashing the LEDs was to make sure they turned on at the correct time (PhaserStart) and the second condition was to ensure the flashers only ran if the correct button was pressed (PhaserActive).

 TwokButtonState = digitalRead (TwokButton);
  if (TwokButtonState == HIGH){
      audio.play("TWOK.wav");
   PhaserActive = 1;
   PreFixSeq = millis() + PreFixStart;
   while (millis() < PreFixSeq){}  //Do Nothing Until Correct Audio Time has Elapsed 

   PreFixSeqStart = millis(); // Marks time when first leds turns on 

 for(PreFixTimer; PreFixTimer < 5; PreFixTimer += 1){
    digitalWrite (latchPin, LOW);
    shiftOut (dataPin, clockPin, MSBFIRST, PreFixStack);
    digitalWrite (latchPin, HIGH);
    PreFixStack = PreFixStack * 2 + 1;
    PreFixSeq = PreFixSeqStart + PreFix [PreFixTimer];
    while (millis() < PreFixSeq) {  } // Pauses between Prefix Lightup sequence
   digitalWrite (latchPin, LOW); // Clear the Shift register at the first phaser fire 
    shiftOut (dataPin, clockPin, MSBFIRST, 0);
    digitalWrite (latchPin, HIGH);
      }
      
  }
 
  PhaserStart = PreFixSeqStart + Phaser [0];
  if (PhaserStart == millis()){
    PhaserState = 1;
  }
  if (PhaserStart + PhaserBurst1 == millis()){
    PhaserState = 0;
  }
  if (PhaserState == 1 and PhaserActive == 1) { // if phasers are active then update phaser flash
    phaserLights.update ();    
  }
  else {
   digitalWrite (PhaserLed, 0); // otherwise switch phasers off...
  } 
  PhaserStart = PreFixSeqStart + Phaser [1];
  if (PhaserStart == millis()){
    PhaserState = 1;
  }
  if (PhaserStart + PhaserBurst2 == millis()){
    PhaserState = 0;
  }
  if (PhaserState == 1 and PhaserActive == 1) { // if phasers are active then update phaser flash
    phaserLights.update ();    
  }
  else {
   digitalWrite (PhaserLed, 0); // otherwise switch phasers off...
  } 

If there is a way to reduce the code so I'm not duplicating the same thing twice with different values, I would welcome suggestions.

Cheers!

This line appears twice:

if (PhaserState == 1 and PhaserActive == 1) { // if phasers are active then update phaser flash
  phaserLights.update ();
}

Also in those lines you're using PhaserState and PhaserActive as boolean values.  If you declare them as type bool as opposed to int then instead of PhaserState = 1; you could write PhaserState = true; and the comparison could become

if (PhaserState and PhaserActive ) { // if phasers are active then update phaser flash
  phaserLights.update ();
}

Additionally, each boolean variable uses one byte of RAM while an int uses two.

Congrats on figuring it out yourself! :+1:t4:

Thanks, Doug! I really appreciate the feedback.

I guess I can remove the second if statement because the loop will run through it, and the second time it will read the change in states given just prior?

And I was trying figure out how to do the Bool thing, so thanks for clarifying that!

You can replace that with a simple delay() call since you aren't doing anything useful until it expires.
Also, depending on how long with will be running, eventually millis() will roll over to 0 and that code will break. The only guaranteed method is to track elapsed time, not wait until a future time.