#4909923
I've been getting the question about changing the number of LED's in the main pack chain and what is required for that. I wrote up a detailed response and thought it was worth sharing here as well. Hope this helps with those that have questions on how to use different led configurations

I intentionally made the animations configurable so different counts of led's can be done. It helps to understand that the neopixels are addressed in a chain. In the case of my original build I used two 8 neopixel sticks for the powercell and then 4 neopixel jewels for the cyclotron. This ended up with the following chain

LED Index 0 - 15 is powercell
LED Index 16 - 22 is cyclotron 1
LED Index 23- 29 is cyclotron 2
LED Index 30- 36 is cyclotron 3
LED Index 37- 43 is cyclotron 4
LED Index 44- 47 is the vent lights

The entire chain is 48 LED's. If you want to change the counts then all you have to do is understand the order of the leds in the chain and configure it properly. Let's say I wanted to modify it so the cyclotron leds were just single neopixels. The indexes would change to

LED Index 0 - 15 is powercell
LED Index 16 - 16 is cyclotron 1
LED Index 17- 17 is cyclotron 2
LED Index 18 - 18 is cyclotron 3
LED Index 19 - 19 is cyclotron 4
LED Index 20 - 23 is the vent lights

You edit these indexes in the following code

int c1Start = 16;
int c1End = 22;
int c2Start = 23;
int c2End = 29;
int c3Start = 30;
int c3End = 36;
int c4Start = 37;
int c4End = 43;
int ventStart = 44;
int ventEnd = 47;

One special thing to note is that on my build not all of the powercell led's showed in the available window space so I added some code to the animation for the powercell to ignore leds that are not visible

const int powercellLedCount = 14; // total number of led's in the animation
const int powercellIndexOffset = 1; // first led offset into the led chain for the animation

So say I wanted all of the led's to be visible I would modify powercellIndexOffset to be 0 and the powercellLedCount would be 15 as everything is 0 based.

When you know how many led's are in the chain you can update the initialization of the neopixels with the proper number in the chain.

Adafruit_NeoPixel powerStick = Adafruit_NeoPixel({num leds in chain}, NEO_POWER, NEO_GRB + NEO_KHZ800);

Funnily enough I don't remember if that number is 0 based or not. As long as it is larger than the number of LED's you have you will be fine :)
#4909929
You can certainly do that but it is more complicated. The wand lights exist in a chain of 4 neopixels and are defined in the wandLights object. Simply add as many lights to the chain as you want and update the first param to match how many.

Adafruit_NeoPixel wandLights = Adafruit_NeoPixel(4, NEO_WAND, NEO_GRB + NEO_KHZ800);

There are no animations for these lights so they are used a bit differently. I have a helper function that you can use called

void setWandLightState(int lednum, int state, unsigned long currentMillis){

You can search the code for where I use it. All of the wand lights on the chain are updated using that function. It supports a number of features. It can set a light red, white, orange, blue, or off. It can also set white light flashing, slow orange flashing, red medium flashing, and fast red flashing. If you wanted to add more colors/flashes that is the function to add it to.
NotSabbat liked this
#4909964
You can certainly do that but it is more complicated. The wand lights exist in a chain of 4 neopixels and are defined in the wandLights object. Simply add as many lights to the chain as you want and update the first param to match how many.

Adafruit_NeoPixel wandLights = Adafruit_NeoPixel(4, NEO_WAND, NEO_GRB + NEO_KHZ800);

There are no animations for these lights so they are used a bit differently. I have a helper function that you can use called

void setWandLightState(int lednum, int state, unsigned long currentMillis){

You can search the code for where I use it. All of the wand lights on the chain are updated using that function. It supports a number of features. It can set a light red, white, orange, blue, or off. It can also set white light flashing, slow orange flashing, red medium flashing, and fast red flashing. If you wanted to add more colors/flashes that is the function to add it to.
It seemed like that would be how it was done, but I wasnt sure. I will see if I can get it working.

@Octafternoon : Looking really good!
#4910883
Ive been working on another pack with lights and sound and when I did the bar-graph the problem with the dim lighting followed the resisters. On my first pack the short resister was at the top, on my second pack I tried putting the shorter resister at the bottom. Right where the two resisters met was where the lights were dim on idle. I dont have pictures of that, but I did re-do the bar graph without resisters and they illuminate just fine. I ended up weaving a wire between the negative post and hooking the wire to a negative lead. I dont have a lot of pictures of this Im afraid, but here is what the bar graph looks like without resistors.

https://youtu.be/TgqF77VJ_Ko
#4910889
I'm glad you got it working how you like it but that is so odd. there has to be a difference of resistance between the large resistor network and the short one. If you have a multimeter you can actually check the resistance of each resistor in the network. I'd be interested to know if there's a difference between them.
#4910925
I technically do own a multi-meter, but I haven't used it in years, so I have no idea where it is.
So, something I didn't communicate very well in my previous post is that the dim lighting issue is always above the bottom resister, regardless of whether it is the short resistor or the long resister.
First, lets assume that the bottom of the bar graph is the end closest to the slo-blo light and the top is the closest to the vent lights. So, if I use the short resister on the "bottom" of the bar graph, on idle the bar graph will dim above the short resister. If I build the bar graph with the long resister on the bottom, the bar graph dims above the long resister. So the graph dims along the top resister regardless of which resister is placed where.
#4911359
Awhile back someone posted this MP3 sound card that uses an SD card for storage. The manual says that you can use an Arduino Leonardo, Mega, Uno or Due. I haven't done a lot of testing yet, but the card can be used with the Nano as well. I haven't done much with it yet, just got it to play with the most basic program, but I will post more info as I get it.

https://www.amazon.com/gp/product/B01JC ... UTF8&psc=1

Image
#4913771
Hi wondering if you can help?

I've followed everything and have my neopixels/switches all working as they should. When I add the soundboard to the mix my powercell lights slow down to a crawl and no sound comes through. Initially i though this was because of not enough voltage so i upped from the 3.3 to 5v and out came smoke.
Tore it all down and rebuilt on my mega, added a new soundboard which i had tested in stand alone (worked ok) Same problem.

On both setups pins used were:
ACT to D12
RST to D9
RX to D10
TX to D11.
Code://
#include <Adafruit_Soundboard.h>

#include <QueueArray.h>

// for the sound board
#include <SoftwareSerial.h>
#include "Adafruit_Soundboard.h"

#include <Adafruit_NeoPixel.h>

// for led triggers
#define HIGH 0x1
#define LOW 0x0

// neopixel pins / setup
#define NEO_POWER 2 // for cyclotron and powercell
Adafruit_NeoPixel powerStick = Adafruit_NeoPixel(48, NEO_POWER, NEO_GRB + NEO_KHZ800);

#define NEO_NOSE 3 // for nose of wand
Adafruit_NeoPixel noseJewel = Adafruit_NeoPixel(7, NEO_NOSE, NEO_GRB + NEO_KHZ800);

#define NEO_WAND 4 // for nose of wand
Adafruit_NeoPixel wandLights = Adafruit_NeoPixel(4, NEO_WAND, NEO_GRB + NEO_KHZ800);

// LED indexes into the neopixel powerstick chain for the cyclotron. Each stick has 8 neopixels for a total of
// 16 with an index starting at 0. These offsets are because my powercell window only shows 13 leds. If you can show more
// change the offset index and powercell count to get more or less lit.
const int powercellLedCount = 9; // total number of led's in the animation
const int powercellIndexOffset = 0; // first led offset into the led chain for the animation

// These are the indexes for the led's on the chain. Each jewel has 7 LEDs. If you are using a single neopixel or
// some other neopixel configuration you will need to update these indexes to match where things are in the chain
const int c1Start = 10;
const int c1End = 10;
const int c2Start = 11;
const int c2End = 11;
const int c3Start = 12;
const int c3End = 12;
const int c4Start = 13;
const int c4End = 13;
const int ventStart = 14;
const int ventEnd = 17;

// inputs for switches and buttons
const int THEME_SWITCH = 5;
const int STARTUP_SWITCH = 6;
const int SAFETY_SWITCH = 7;
const int FIRE_BUTTON = 8;

// soundboard pins and setup
#define SFX_RST 9
#define SFX_RX 10
#define SFX_TX 11
const int ACT = 12; // this allows us to know if the audio is playing

SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX);
Adafruit_Soundboard sfx = Adafruit_Soundboard( &ss, NULL, SFX_RST);

// ##############################
// available options
// ##############################
const bool useGameCyclotronEffect = true; // set this to true to get the fading previous cyclotron light in the idle sequence
const bool useCyclotronFadeInEffect = false; // Instead of the yellow alternate flashing on boot/vent this fades the cyclotron in from off to red
const bool useDialogTracks = true; // set to true if you want the dialog tracks to play after firing for 5 seconds

// Possible Pack states
bool powerBooted = false; // has the pack booted up
bool isFiring = false; // keeps track of the firing state
bool shouldWarn = false; // track the warning state for alert audio
bool shuttingDown = false; // is the pack in the process of shutting down
bool poweredDown = true; // is the pack powered down
bool venting = false; // is the pack venting

// physical switch states
bool startup = false;
bool theme = false;
bool safety = false;
bool fire = false;
bool warning = false;

// audio track names on soundboard
char startupTrack[] = "T00 WAV";
char blastTrack[] = "T01 WAV";
char endTrack[] = "T02 WAV";
char idleTrack[] = "T03 WAV";
char shutdownTrack[] = "T04 WAV";
char clickTrack[] = "T05 WAV";
char chargeTrack[] = "T06 WAV";
char warnTrack[] = "T07 WAV";
char ventTrack[] = "T08 WAV";
char texTrack[] = "T09 WAV";
char choreTrack[] = "T10 WAV";
char toolsTrack[] = "T11 WAV";
char listenTrack[] = "T12 WAV";
char thatTrack[] = "T13 WAV";
char neutronizedTrack[] = "T14 WAV";
char boxTrack[] = "T15 WAV";
char themeTrack[] = "T16 OGG";

// this queue holds a shuffled list of dialog tracks we can pull from so we don't
// play the same ones twice
QueueArray <int> dialogQueue;
int numDialog = 7;

// timer trigger times/states
unsigned long firingStateMillis;
const unsigned long firingWarmWaitTime = 5000; // how long to hold down fire for lights to speed up
const unsigned long firingWarnWaitTime = 10000; // how long to hold down fire before warning sounds

// Arduino setup function
void setup() {
// softwareserial at 9600 baud for the audio board
ss.begin(9600);

// set act modes for the fx board
pinMode(ACT, INPUT);

// configure nose jewel
noseJewel.begin();
noseJewel.setBrightness(100);
noseJewel.show(); // Initialize all pixels to 'off'

// configure powercell/cyclotron
powerStick.begin();
powerStick.setBrightness(75);
powerStick.show(); // Initialize all pixels to 'off'

// configure wand lights
wandLights.begin();
wandLights.setBrightness(75);
wandLights.show();

// set the modes for the switches/buttons
pinMode(THEME_SWITCH, INPUT);
digitalWrite(THEME_SWITCH, HIGH);
pinMode(STARTUP_SWITCH, INPUT);
digitalWrite(STARTUP_SWITCH, HIGH);
pinMode(SAFETY_SWITCH, INPUT);
digitalWrite(SAFETY_SWITCH, HIGH);
pinMode(FIRE_BUTTON, INPUT);
digitalWrite(FIRE_BUTTON, HIGH);
}

/* ************* Audio Board Helper Functions ************* */
// helper function to play a track by name on the audio board
void playAudio( char* trackname, int playing ) {
// stop track if one is going
if (playing == 0) {
sfx.stop();
}

// now go play
if (sfx.playTrack(trackname)) {
sfx.unpause();
}
}

void playDialogTrack( int playing ) {
// if the queue is empty reseed it
if ( dialogQueue.isEmpty() ) {
for (int i = 1; i <= numDialog; i++) {
dialogQueue.enqueue(i);
}
}

switch (dialogQueue.dequeue()) {
case (1):
playAudio(texTrack, playing);
break;
case (2):
playAudio(listenTrack, playing);
break;
case (3):
playAudio(choreTrack, playing);
break;
case (4):
playAudio(boxTrack, playing);
break;
case (5):
playAudio(thatTrack, playing);
break;
case (6):
playAudio(neutronizedTrack, playing);
break;
case (7):
playAudio(toolsTrack, playing);
break;
default:
playAudio(endTrack, playing);
break;
}
}

/* ************* Main Loop ************* */
int cyclotronRunningFadeOut = 255; // we reset this variable every time we change the cyclotron index so the fade effect works
int cyclotronRunningFadeIn = 0; // we reset this to 0 to fade the cyclotron in from nothing

// intervals that can be adjusted in real time to speed up animations
unsigned long pwr_interval = 60; // interval at which to cycle lights for the powercell. We update this in the loop to speed up the animation so must be declared here (milliseconds)
unsigned long cyc_interval = 1000; // interval at which to cycle lights for the cyclotron.
unsigned long cyc_fade_interval = 15; // fade the inactive cyclotron to light to nothing
unsigned long firing_interval = 40; // interval at which to cycle firing lights on the bargraph. We update this in the loop to speed up the animation so must be declared here (milliseconds).

void loop() {
// get the current time
unsigned long currentMillis = millis();

// find out of the audio board is playing audio
int playing = digitalRead(ACT);

// get the current switch states
int theme_switch = digitalRead(THEME_SWITCH);

// if the theme switch has recently changed from off to on we
// should play the full ghostbusters theme song
if (theme_switch == 1) {
if (theme == false) {
playAudio(themeTrack, playing);
theme = true;
}
} else {
theme = false;
}

int startup_switch = digitalRead(STARTUP_SWITCH);
int safety_switch = digitalRead(SAFETY_SWITCH);
int fire_button = digitalRead(FIRE_BUTTON);

// while the startup switch is set on
if (startup_switch == 1) {
// in general we always try to play the idle sound if started
if (playing == 1 && startup == true) {
playAudio(idleTrack, playing);
}

// choose the right powercell animation sequence for booted/on
if ( powerBooted == true ) {
// standard idle power sequence for the pack
poweredDown = false;
shuttingDown = false;
venting = false;
setWandLightState(3, 0, 0); //set sloblow red
setVentLightState(ventStart, ventEnd, 2);
powerSequenceOne(currentMillis, pwr_interval, cyc_interval, cyc_fade_interval);
} else {
// boot up the pack. powerSequenceBoot will set powerBooted when complete
powerSequenceBoot(currentMillis);
setWandLightState(3, 7, currentMillis); //set sloblow red blinking
}

// if we are not started up we should play the startup sound and begin the boot sequence
if (startup == false) {
startup = true;
playAudio(startupTrack, playing);

// get the current safety switch state
if (safety_switch == 1 && safety == false) {
safety = true;
}
}

if ( startup == true && safety_switch == 1 ) {
if ( venting == false && powerBooted == true ) {
setWandLightState(1, 2, 0); // set back light orange
setWandLightState(2, 1, 0); // set body led white
} else {
setWandLightState(1, 4, 0); // set back light off
setWandLightState(2, 4, 0); // set body led off
}

// if the safety switch is set off then we can fire when the button is pressed
if ( fire_button == 0) {
// if the button is just pressed we clear all led's to start the firing animations
if ( isFiring == false ) {
shutdown_leds();
isFiring = true;
}

// show the firing bargraph sequence
barGraphSequenceTwo(currentMillis);

// strobe the nose pixels
fireStrobe(currentMillis);

// now powercell/cyclotron/wand lights
// if this is the first time reset some variables and play the blast track
if (fire == false) {
shouldWarn = false;
fire = true;
firingStateMillis = millis();
playAudio(blastTrack, playing);
} else {
// find out what our timing is
unsigned long diff = (unsigned long)(millis() - firingStateMillis);

if ( diff > firingWarnWaitTime) { // if we are in the fire warn interval
pwr_interval = 10; // speed up the powercell animation
firing_interval = 20; // speed up the bar graph animation
cyc_interval = 50; // really speed up cyclotron
cyc_fade_interval = 5; // speed up the fade of the cyclotron
if (playing == 1 || shouldWarn == false ) {
shouldWarn = true;
playAudio(warnTrack, playing); // play the firing track with the warning
}
setWandLightState(0, 8, currentMillis); // set top light red flashing fast
} else if ( diff > firingWarmWaitTime) { // if we are in the dialog playing interval
pwr_interval = 30; // speed up the powercell animation
firing_interval = 30; // speed up the bar graph animation
cyc_interval = 200; // speed up cyclotron
cyc_fade_interval = 10; // speed up the fade of the cyclotron
if (playing == 1) {
playAudio(blastTrack, playing); // play the normal blast track
}
setWandLightState(0, 6, currentMillis); // set top light orange flashing
}
}
} else { // if we were firing and are no longer reset the leds
if ( isFiring == true ) {
shutdown_leds();
isFiring = false;
}

// and do the standard bargraph sequence
barGraphSequenceOne(currentMillis);

if (fire == true) { // if we were firing let's reset the animations and play the correct final firing track
clearFireStrobe();
setWandLightState(0, 4, currentMillis); // set top light off

pwr_interval = 60;
firing_interval = 40;
cyc_interval = 1000;
cyc_fade_interval = 15;
fire = false;

// see if we've been firing long enough to get the dialog or vent sounds
unsigned long diff = (unsigned long)(millis() - firingStateMillis);

if ( diff > firingWarnWaitTime) { // if we are past the warning let's vent the pack
playAudio(ventTrack, playing);
venting = true;
clearPowerStrip(); // play the boot animation on the powercell
} else if ( diff > firingWarmWaitTime) { // if in the dialog time play the dialog in sequence
if ( useDialogTracks == true ) {
playDialogTrack(playing);
} else {
playAudio(endTrack, playing);
}
} else {
playAudio(endTrack, playing);
}
}
}

// if the safety was just changed play the click track
if (safety == false) {
safety = true;
playAudio(chargeTrack, playing);
}
} else {
// if the safety is switched off play the click track
if (safety == true) {
setWandLightState(1, 4, 0); // set back light off
setWandLightState(2, 4, 0); // set body off
safety = false;
playAudio(clickTrack, playing);
}
}
} else { // if we are powering down
if ( poweredDown == false ) {
if ( shuttingDown == false ) {
playAudio(shutdownTrack, playing); // play the pack shutdown track
shuttingDown = true;
}
cyclotronRunningFadeOut = 255;
powerSequenceShutdown(currentMillis);
} else {
if (startup == true) { // if started reset the variables
clearPowerStrip(); // clear all led's
shutdown_leds();
startup = false;
safety = false;
fire = false;
}
}
}
delay(1);
}

/*************** Wand Light Helpers *********************/
unsigned long prevFlashMillis = 0; // last time we changed a powercell light in the idle sequence
bool flashState = false;
const unsigned long wandFastFlashInterval = 100; // interval at which we flash the top led on the wand
const unsigned long wandMediumFlashInterval = 500; // interval at which we flash the top led on the wand

void setWandLightState(int lednum, int state, unsigned long currentMillis) {
switch ( state ) {
case 0: // set led red
wandLights.setPixelColor(lednum, wandLights.Color(255, 0, 0));
break;
case 1: // set led white
wandLights.setPixelColor(lednum, wandLights.Color(255, 255, 255));
break;
case 2: // set led orange
wandLights.setPixelColor(lednum, wandLights.Color(255, 127, 0));
break;
case 3: // set led blue
wandLights.setPixelColor(lednum, wandLights.Color(0, 0, 255));
break;
case 4: // set led off
wandLights.setPixelColor(lednum, 0);
break;
case 5: // fast white flashing
if ((unsigned long)(currentMillis - prevFlashMillis) >= wandFastFlashInterval) {
prevFlashMillis = currentMillis;
if ( flashState == false ) {
wandLights.setPixelColor(lednum, wandLights.Color(255, 255, 255));
flashState = true;
} else {
wandLights.setPixelColor(lednum, 0);
flashState = false;
}
}
break;
case 6: // slower orange flashing
if ((unsigned long)(currentMillis - prevFlashMillis) >= wandMediumFlashInterval) {
prevFlashMillis = currentMillis;
if ( flashState == false ) {
wandLights.setPixelColor(lednum, wandLights.Color(255, 127, 0));
flashState = true;
} else {
wandLights.setPixelColor(lednum, 0);
flashState = false;
}
}
break;
case 7: // medium red flashing
if ((unsigned long)(currentMillis - prevFlashMillis) >= wandMediumFlashInterval) {
prevFlashMillis = currentMillis;
if ( flashState == false ) {
wandLights.setPixelColor(lednum, wandLights.Color(255, 0, 0));
flashState = true;
} else {
wandLights.setPixelColor(lednum, 0);
flashState = false;
}
}
break;
case 8: // fast red flashing
if ((unsigned long)(currentMillis - prevFlashMillis) >= wandFastFlashInterval) {
prevFlashMillis = currentMillis;
if ( flashState == false ) {
wandLights.setPixelColor(lednum, wandLights.Color(255, 0, 0));
flashState = true;
} else {
wandLights.setPixelColor(lednum, 0);
flashState = false;
}
}
break;
}

wandLights.show();
}

/*************** Vent Light *************************/
void setVentLightState(int startLed, int endLed, int state ) {
switch ( state ) {
case 0: // set all leds to white
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, powerStick.Color(255, 255, 255));
}
break;
case 1: // set all leds to blue
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, powerStick.Color(0, 0, 255));
}
break;
case 2: // set all leds off
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, 0);
}
break;
}
}

/*************** Powercell/Cyclotron Animations *********************/
// timer helpers and intervals for the animations
unsigned long prevPwrBootMillis = 0; // the last time we changed a powercell light in the boot sequence
const unsigned long pwr_boot_interval = 60; // interval at which to cycle lights (milliseconds). Adjust this if

unsigned long prevCycBootMillis = 0; // the last time we changed a cyclotron light in the boot sequence
const unsigned long cyc_boot_interval = 500; // interval at which to cycle lights (milliseconds).
const unsigned long cyc_boot_alt_interval = 600; // interval at which to cycle lights (milliseconds).

unsigned long prevShtdMillis = 0; // last time we changed a light in the idle sequence
const unsigned long pwr_shutdown_interval = 200; // interval at which to cycle lights (milliseconds).

unsigned long prevPwrMillis = 0; // last time we changed a powercell light in the idle sequence
unsigned long prevCycMillis = 0; // last time we changed a cyclotron light in the idle sequence
unsigned long prevFadeCycMillis = 0; // last time we changed a cyclotron light in the idle sequence

// LED tracking variables
const int powerSeqTotal = powercellLedCount; // total number of led's for powercell 0 based
int powerSeqNum = powercellIndexOffset; // current running powercell sequence led
int powerShutdownSeqNum = powercellLedCount - powercellIndexOffset; // shutdown sequence counts down

// animation level trackers for the boot and shutdown
int currentBootLevel = powercellIndexOffset; // current powercell boot level sequence led
int currentLightLevel = powercellLedCount - powercellIndexOffset; // current powercell boot light sequence led

void setCyclotronLightState(int startLed, int endLed, int state ) {
switch ( state ) {
case 0: // set all leds to red
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, powerStick.Color(255, 0, 0));
}
break;
case 1: // set all leds to orange
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, powerStick.Color(255, 106, 0));
}
break;
case 2: // set all leds off
for (int i = startLed; i <= endLed; i++) {
powerStick.setPixelColor(i, 0);
}
break;
case 3: // fade all leds from red
for (int i = startLed; i <= endLed; i++) {
if ( cyclotronRunningFadeOut >= 0 ) {
powerStick.setPixelColor(i, 255 * cyclotronRunningFadeOut / 255, 0, 0);
cyclotronRunningFadeOut--;
} else {
powerStick.setPixelColor(i, 0);
}
}
break;
case 4: // fade all leds to red
for (int i = startLed; i <= endLed; i++) {
if ( cyclotronRunningFadeIn < 255 ) {
powerStick.setPixelColor(i, 255 * cyclotronRunningFadeIn / 255, 0, 0);
cyclotronRunningFadeIn++;
} else {
powerStick.setPixelColor(i, powerStick.Color(255, 0, 0));
}
}
break;
}
}

// shuts off and resets the powercell/cyclotron leds
void clearPowerStrip() {
// reset vars
powerBooted = false;
poweredDown = true;
powerSeqNum = powercellIndexOffset;
powerShutdownSeqNum = powercellLedCount - powercellIndexOffset;
currentLightLevel = powercellLedCount;
currentBootLevel = powercellIndexOffset;
cyclotronRunningFadeIn = 0;

// shutoff the leds
for ( int i = 0; i <= c4End; i++) {
powerStick.setPixelColor(i, 0);
}
powerStick.show();

for ( int j = 0; j <= 3; j++ ) {
wandLights.setPixelColor(j, 0);
}
wandLights.show();

if ( venting == true ) {
setVentLightState(ventStart, ventEnd, 0);
}
}

// boot animation on the powercell/cyclotron
bool reverseBootCyclotron = false;
void powerSequenceBoot(unsigned long currentMillis) {
bool doUpdate = false;

// START CYCLOTRON
if ( useCyclotronFadeInEffect == false ) {
if ((unsigned long)(currentMillis - prevCycBootMillis) >= cyc_boot_interval) {
prevCycBootMillis = currentMillis;

if ( reverseBootCyclotron == false ) {
setCyclotronLightState(c1Start, c1End, 1);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c3Start, c3End, 1);
setCyclotronLightState(c4Start, c4End, 2);

doUpdate = true;
reverseBootCyclotron = true;
} else {
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c2Start, c2End, 1);
setCyclotronLightState(c3Start, c3End, 2);
setCyclotronLightState(c4Start, c4End, 1);

doUpdate = true;
reverseBootCyclotron = false;
}
}
} else {
if ((unsigned long)(currentMillis - prevCycBootMillis) >= cyc_boot_alt_interval) {
prevCycBootMillis = currentMillis;
setCyclotronLightState(c1Start, c4End, 4);
doUpdate = true;
}
}
// END CYCLOTRON

if ((unsigned long)(currentMillis - prevPwrBootMillis) >= pwr_boot_interval) {
// save the last time you blinked the LED
prevPwrBootMillis = currentMillis;

// START POWERCELL
if ( currentBootLevel != powerSeqTotal ) {
if ( currentBootLevel == currentLightLevel) {
if (currentLightLevel + 1 <= powerSeqTotal) {
powerStick.setPixelColor(currentLightLevel + 1, 0);
}
powerStick.setPixelColor(currentBootLevel, powerStick.Color(0, 0, 255));
currentLightLevel = powerSeqTotal;
currentBootLevel++;
} else {
if (currentLightLevel + 1 <= powerSeqTotal) {
powerStick.setPixelColor(currentLightLevel + 1, 0);
}
powerStick.setPixelColor(currentLightLevel, powerStick.Color(0, 0, 255));
currentLightLevel--;
}
doUpdate = true;
} else {
powerBooted = true;
currentBootLevel = powercellIndexOffset;
currentLightLevel = powercellLedCount - powercellIndexOffset;
}
// END POWERCELL
}

// if we have changed an led
if ( doUpdate == true ) {
powerStick.show(); // commit all of the changes
}
}

// idle/firing animation for the powercell/cyclotron
int cycOrder = 0; // which cyclotron led will be lit next
int cycFading = -1; // which cyclotron led is fading out for game style
void powerSequenceOne(unsigned long currentMillis, unsigned long anispeed, unsigned long cycspeed, unsigned long cycfadespeed) {
bool doUpdate = false; // keep track of if we changed something so we only update on changes

// START CYCLOTRON
if ( useGameCyclotronEffect == true ) { // if we are doing the video game style cyclotron
if ((unsigned long)(currentMillis - prevCycMillis) >= cycspeed) {
prevCycMillis = currentMillis;

switch ( cycOrder ) {
case 0:
setCyclotronLightState(c1Start, c1End, 0);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c3Start, c3End, 2);
cycFading = 0;
cyclotronRunningFadeOut = 255;
cycOrder = 1;
break;
case 1:
setCyclotronLightState(c2Start, c2End, 0);
setCyclotronLightState(c3Start, c3End, 2);
setCyclotronLightState(c4Start, c4End, 2);
cycFading = 1;
cyclotronRunningFadeOut = 255;
cycOrder = 2;
break;
case 2:
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c3Start, c3End, 0);
setCyclotronLightState(c4Start, c4End, 2);
cycFading = 2;
cyclotronRunningFadeOut = 255;
cycOrder = 3;
break;
case 3:
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c4Start, c4End, 0);
cycFading = 3;
cyclotronRunningFadeOut = 255;
cycOrder = 0;
break;
}

doUpdate = true;
}

// now figure out the fading light
if ( (unsigned long)( currentMillis - prevFadeCycMillis) >= cycfadespeed ) {
prevFadeCycMillis = currentMillis;
if ( cycFading != -1 ) {
switch ( cycFading ) {
case 0:
setCyclotronLightState(c4Start, c4End, 3);
break;
case 1:
setCyclotronLightState(c1Start, c1End, 3);
break;
case 2:
setCyclotronLightState(c2Start, c2End, 3);
break;
case 3:
setCyclotronLightState(c3Start, c3End, 3);
break;
}
doUpdate = true;
}
}
} else { // otherwise this is the standard version
if ((unsigned long)(currentMillis - prevCycMillis) >= cycspeed) {
prevCycMillis = currentMillis;

switch ( cycOrder ) {
case 0:
setCyclotronLightState(c4Start, c4End, 2);
setCyclotronLightState(c1Start, c1End, 0);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c3Start, c3End, 2);
cycFading = 0;
cyclotronRunningFadeOut = 255;
cycOrder = 1;
break;
case 1:
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c2Start, c2End, 0);
setCyclotronLightState(c3Start, c3End, 2);
setCyclotronLightState(c4Start, c4End, 2);
cycFading = 1;
cyclotronRunningFadeOut = 255;
cycOrder = 2;
break;
case 2:
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c3Start, c3End, 0);
setCyclotronLightState(c4Start, c4End, 2);
cycFading = 2;
cyclotronRunningFadeOut = 255;
cycOrder = 3;
break;
case 3:
setCyclotronLightState(c1Start, c1End, 2);
setCyclotronLightState(c2Start, c2End, 2);
setCyclotronLightState(c3Start, c3End, 2);
setCyclotronLightState(c4Start, c4End, 0);
cycFading = 3;
cyclotronRunningFadeOut = 255;
cycOrder = 0;
break;
}

doUpdate = true;
}
}
// END CYCLOTRON

// START POWERCELL
if ((unsigned long)(currentMillis - prevPwrMillis) >= anispeed) {
// save the last time you blinked the LED
prevPwrMillis = currentMillis;

for ( int i = powercellIndexOffset; i <= powerSeqTotal; i++) {
if ( i <= powerSeqNum ) {
powerStick.setPixelColor(i, powerStick.Color(0, 0, 150));
} else {
powerStick.setPixelColor(i, 0);
}
}

if ( powerSeqNum <= powerSeqTotal) {
powerSeqNum++;
} else {
powerSeqNum = powercellIndexOffset;
}

doUpdate = true;
}
// END POWERCELL

// if we changed anything update
if ( doUpdate == true ) {
powerStick.show();
}
}

// shutdown animation for the powercell/cyclotron
int cyclotronFadeOut = 255;
void powerSequenceShutdown(unsigned long currentMillis) {
if ((unsigned long)(currentMillis - prevShtdMillis) >= pwr_shutdown_interval) {
prevShtdMillis = currentMillis;

// START CYCLOTRON
for (int i = c1Start; i <= c4End; i++) {
if ( cyclotronFadeOut >= 0 ) {
powerStick.setPixelColor(i, 255 * cyclotronFadeOut / 255, 0, 0);
cyclotronFadeOut--;
} else {
powerStick.setPixelColor(i, 0);
}
}
// END CYCLOTRON

// START POWERCELL
for ( int i = powerSeqTotal; i >= powercellIndexOffset; i--) {
if ( i <= powerShutdownSeqNum ) {
powerStick.setPixelColor(i, powerStick.Color(0, 0, 150));
} else {
powerStick.setPixelColor(i, 0);
}
}

powerStick.show();

if ( powerShutdownSeqNum >= powercellIndexOffset) {
powerShutdownSeqNum--;
} else {
poweredDown = true;
powerShutdownSeqNum = powercellLedCount - powercellIndexOffset;
cyclotronFadeOut = 255;
}
// END POWERCELL
}
}

/*************** Nose Jewel Firing Animations *********************/
unsigned long prevFireMillis = 0;
const unsigned long fire_interval = 50; // interval at which to cycle lights (milliseconds).
int fireSeqNum = 0;
int fireSeqTotal = 5;

void clearFireStrobe() {
for ( int i = 0; i < 7; i++) {
noseJewel.setPixelColor(i, 0);
}
noseJewel.show();
fireSeqNum = 0;
}

void fireStrobe(unsigned long currentMillis) {
if ((unsigned long)(currentMillis - prevFireMillis) >= fire_interval) {
prevFireMillis = currentMillis;

switch ( fireSeqNum ) {
case 0:
noseJewel.setPixelColor(0, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(1, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(2, 0);
noseJewel.setPixelColor(3, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(4, 0);
noseJewel.setPixelColor(5, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(6, 0);
break;
case 1:
noseJewel.setPixelColor(0, noseJewel.Color(0, 0, 255));
noseJewel.setPixelColor(1, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(2, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(3, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(4, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(5, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(6, noseJewel.Color(255, 255, 255));
break;
case 2:
noseJewel.setPixelColor(0, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(1, 0);
noseJewel.setPixelColor(2, noseJewel.Color(0, 0, 255));
noseJewel.setPixelColor(3, 0);
noseJewel.setPixelColor(4, noseJewel.Color(0, 0, 255));
noseJewel.setPixelColor(5, 0);
noseJewel.setPixelColor(6, noseJewel.Color(255, 0, 0));
break;
case 3:
noseJewel.setPixelColor(0, noseJewel.Color(0, 0, 255));
noseJewel.setPixelColor(1, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(2, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(3, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(4, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(5, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(6, noseJewel.Color(255, 255, 255));
break;
case 4:
noseJewel.setPixelColor(0, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(1, 0);
noseJewel.setPixelColor(2, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(3, 0);
noseJewel.setPixelColor(4, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(5, 0);
noseJewel.setPixelColor(6, noseJewel.Color(255, 255, 255));
break;
case 5:
noseJewel.setPixelColor(0, noseJewel.Color(255, 0, 255));
noseJewel.setPixelColor(1, noseJewel.Color(0, 255, 0));
noseJewel.setPixelColor(2, noseJewel.Color(255, 0, 0));
noseJewel.setPixelColor(3, noseJewel.Color(0, 0, 255));
noseJewel.setPixelColor(4, noseJewel.Color(255, 0, 255));
noseJewel.setPixelColor(5, noseJewel.Color(255, 255, 255));
noseJewel.setPixelColor(6, noseJewel.Color(0, 0, 255));
break;
}

noseJewel.show();

fireSeqNum++;
if ( fireSeqNum > fireSeqTotal ) {
fireSeqNum = 0;
}
}
}

/*************** Bar Graph Animations *********************/
void shutdown_leds() {
// stub function for when I re-enable to bargraph
}
void barGraphSequenceOne(unsigned long currentMillis) {
// stub function for when I re-enable to bargraph
}
void barGraphSequenceTwo(unsigned long currentMillis) {
// stub function for when I re-enable to bargraph
}



Before I order another FX board and Arduino is there anything apparent from what im describing?

Also would it be possible to change from the FX board to a DF player? im new to coding so im unsure?

Thanks in advance

Glyn
#4913775
Things run slow when the sound board is not communicating properly. Basically the calls to play are blocking because nothing is there to play. There is something wrong with your wiring. Make sure all solder joints are good and also make sure the jumper UG to GND (to start the sound board in Uart mode) is connected. I highly recommend running the adafruit sketch to test the sound board with the arduino. It allows interactive testing capability

https://learn.adafruit.com/adafruit-aud ... io-control

The fx board takes 3-5 v and I've always run it on 5v. Same power source as the nano. If testing the nano off usb then the sound board can be run off the 5v pin on the arduino.

Sure other players can be used but the code will need to be changed for them. Would be updating the play function and the initialization.
Glyn2k liked this
#4915976
So, not sure if Ill ever let this thread truly die. I keep playing with the code :D

Ive been playing around with the Aideepen YX5300 UART MP3 Music Player Module and it absolutely does works with CountDeMonet's code with some pretty heavy modifications. First off it doesn't have an ACT or RST pin, just GRD , VCC, RX and RT.
This also means that, as far as I know, there is not a way to tell if it is currently playing audio. I have been able to get around this by setting up millis () timers, but it has been challenging to say the least. as another quirk it makes a loud popping noise when it stops playing one file and starts another. This can be mitigated by starting a new audio file before another is finished, but that also means I've been having to get creative with sound files and timers, specifically by adding silence to the end of repeating noises and timing the loop to start before it has finished playing. The Idle has noise has been a focus of this since it has to play immediately after so many noises.
The plus side is that it amplifies a bit louder than the Adafruit board and that near unlimited space is super great. Other than that it is a pain. If I get a finished program or find something better I will put it up.

CountDeMonet, thanks for getting the ball rolling on all of this. I wouldn't have sound or lights at all if it weren't for you....neither would the 4 other people in my group.
CountDeMonet liked this
#4915992
Glad to hear you got it working. Not having that feedback that the device is playing would be a very complex issue to resolve. With the audio fx board calling stop before play was a no go if there was nothing playing already. I'm assuming you had a similar issue with this board to have to do all that extra work. I would be interested in seeing the solution you came up with. Reducing the popping was one of the things I had to do with the audiofx board as well which is why the ACT pin is so heavily used in my code.

Always nice to hear how many people have made use of the code. This is one of my more popular projects even though I'm most proud of my droid :)

#4916070
Im honestly surprised its not more popular to be honest. You basically found a $10 option to get lights in a pack. I would figure that would be step one for any pack build. My chapter has 5 packs; 1 has the hardware and program you came up with for pack lights, wand lights and sound, 3 have the minimalist light option and the latest pack is the one Im tinkering with. Every-time we finish up a pack the first thing we do is throw a minimalist light kit into it.

Do you have a link to your droid build? I would love to see it.

As far as solutions, as I said the Aideepen YX5300 UART MP3 Music Player Module doesn't have a rst pin or an act pin. It does have a playing light and it could be possible to rig a wire up to it to do the job, but I didnt want to go that deep into modding yet. What I did end up doing is pulling out all of the code that involved the RST and ACT pin, which was not easy and then went back in and rebuilt it using the codes to command the YX5300.

I wont put the whole code here because its long and doesnt work entirely anyway, but here are the basics of what I did;
I declared some new int based on the individual sounds I was using

//******* sound int ********
int Startup = 0X01; // timed T00
int StartupLength = 6403;// timed T00 // actual 6503
int Blast = 0X02;// timed T01
int BlastLength = 16952; //timed T01
int BlastEnd = 0X03;// timed T02
int BlastEndLength = 1660; // timed
int Idle = 0X04; // cycled T03
int IdleLength = 3055; // timed actual 4005
int Shutdown = 0X05; //timed T04
int ShutdownLength = 3100; //timed
int Click = 0X06;// timed T05
int ClickLength = 183;// timed
int GunOn = 0X07;// timed T06
int GunOnLength = 1568;// timed
int Vent = 0X09;// timed T08
int VentLength = 6366;// timed

I made the lengths of the tracks INT so I could just modify one field rather than a bunch; Learned that from your original code.

Then used the millis () function to set up timers for example;

if (idling == true && musicplaying == false){
IdleMillis = millis();
if (IdleMillis - StartIdleMillis >= IdleLength){
sendCommand(CMD_PLAY_W_INDEX, Idle);
StartIdleMillis = IdleMillis;
}
}

and

if (startup == false) {
startup = true;
sendCommand(CMD_PLAY_W_INDEX, Startup);
TrackNumber = 0;
StartupRunning = 0;
// get the current safety switch state
if (safety_switch == 1 && safety == false) {
safety = true;
}


I was trying to set it up so that when I wanted the pack to play the Idle noises it would trigger an Idle state so I wouldn't have to set it up after each sound that is followed by the pack rumble, but I wasn't very successful. Currently it will will turn on via the power switch, play the startup noise and go into the idle hum pretty well, but Ive pretty much stalled out on the rest of the sounds like the gun firing noise turning off and going into the hum, etc.

At this point I think Im going to give up on this little board. The sound is great and it plays music just fine, but I just dont think it is going to be worth all of the time it will take to get rid of all the quirks just for the extra storage space.
On the plus side; now that I know more about programming and what is needed to make this sound board stuff work, Ive found some other boards that have SD card slots that may work as drop in options to your original code.

Cytron Easy MP3 Shield : Available on amazon for around $20. It takes a mini SD card as an integrated 3W amp and it looks like it has a pin dedicated to 'busy' so it should work as an ACT pin. You can also change SD card files via a usb cable connected to the shield . Oddly it can also use a thumb drive, so thats neat.


Loria VS1053 MP3 Module: Available on amazon for around $10. I havent been able to find much documentation on this board yet, but the price point is obviously compelling.

I may end up getting both and seeing how they work. Until then the adafruit FX card goes back in.
CountDeMonet liked this
#4916071
Ah yeah I can see what you had to do. I would probably have tapped into that play light :) I'm sure there are better options now but I used the audio fx board on my droid build as well and the 16mb version worked great for that. I have like 8mb left on that card and I have over 100 audio clips and 9 full songs in ogg format.

Those other options you mentioned are interesting. I used a cytron motor controller for the dome rotation of the droid and that company really impressed me. I had a few questions about the hardware and one of the lead engineers messaged me back with great info. I ended up going with a dimensions engineering controller for the main drive system but cytron makes one half the cost that is really tempting and I might look at for my next build. I wouldn't hesitate trying anything of theirs based off my experience with their other hardware for sure.

if you wanna learn more about the droid I did a few presentations at a local maker space for the robotics club. The main presentation is on my github site: https://github.com/CountDeMonet/Arduino ... -21-18.pdf

and also a post on my blog https://vineripesoftware.wordpress.com/ ... ech-droid/

TBH that droid wiring is SOOOO much easier than the proton pack. While the code/hardware design for the pack was like 85% of the work it was only like 20% for the droid. I spent way more time building and designing 3d parts for it which was what was fun for me. Making me stretch my skills :)

PS: I have the minimal version in several spirit packs as well and one hangs on my wall behind me while I work plugged into the wall running non stop :) while the full thing is a ton of fun I don't need my spirit packs to be fully functional.
#4916178
Ah yeah I can see what you had to do. I would probably have tapped into that play light :) I'm sure there are better options now but I used the audio fx board on my droid build as well and the 16mb version worked great for that. I have like 8mb left on that card and I have over 100 audio clips and 9 full songs in ogg format.
Yeah, I guess I was tired of trying to get it to work and just wanted to go with something that had what I needed built in, but it should work that way. I did seem to get a lot of extra noise in the speakers when using it compared to using the FX as well. May be because I was doing something wrong, may be because its a cheap card, I dont know.

Thats crazy. I think I was able to put something like 1-2 more songs on the FX card and it was out of space. I believe it was scaled down to mono as well.
Those other options you mentioned are interesting. I used a cytron motor controller for the dome rotation of the droid and that company really impressed me. I had a few questions about the hardware and one of the lead engineers messaged me back with great info. I ended up going with a dimensions engineering controller for the main drive system but cytron makes one half the cost that is really tempting and I might look at for my next build. I wouldn't hesitate trying anything of theirs based off my experience with their other hardware for sure.
That makes me feel better. I went ahead and bought the Cytron because of the better documentation and that it just felt like it was a better company. It came in today so Ill probably start playing with it on Sunday. Ill make sure to post something about after I spent a couple of days with it.
if you wanna learn more about the droid I did a few presentations at a local maker space for the robotics club. The main presentation is on my github site: https://github.com/CountDeMonet/Arduino ... -21-18.pdf

and also a post on my blog https://vineripesoftware.wordpress.com/ ... ech-droid/

TBH that droid wiring is SOOOO much easier than the proton pack. While the code/hardware design for the pack was like 85% of the work it was only like 20% for the droid. I spent way more time building and designing 3d parts for it which was what was fun for me. Making me stretch my skills :)
That is SUPER cool! I think that an astromech may have to be my next big build when I have more space (pun not intended) to work in. Looks like youve got some good ground work info in the presentation too.
PS: I have the minimal version in several spirit packs as well and one hangs on my wall behind me while I work plugged into the wall running non stop :) while the full thing is a ton of fun I don't need my spirit packs to be fully functional.
I sometimes do that jus to watch my pack run. Its very soothing and it makes me smile with pride :)
#4916183
One big difference with Emmet is that all of the audio is OGG and not wav but he also does not have any sounds that loop. With the proton pack there were a number of audio clips that were looping and I found that the compression caused the looping to have hiccups or audio pops. To counter this I just made everything wav as I had plenty of space for the clips I was using. I probably went over board making everything wav on the proton pack and not OGG. If someone wants to make some space any sound that isn't looped you can convert it and update the filename in the header. I bet a ton of space is saved. I learned a lot from the proton pack to the droid.
#4916425
I generally use a mix of 5 minute epoxy for areas that need a really strong bond (like the grips or the doodads to the pvc) and thin super glue. I like the gorilla brand. The thin stuff really wicks in. There are a lot of screws used with the thrower as well. I think it was 2.5mm cap heads for the base to the bottom and then #8 for the clippard and front box. I think it was #8.
randomZERO liked this
#4916439
I generally use a mix of 5 minute epoxy for areas that need a really strong bond (like the grips or the doodads to the pvc) and thin super glue. I like the gorilla brand. The thin stuff really wicks in. There are a lot of screws used with the thrower as well. I think it was 2.5mm cap heads for the base to the bottom and then #8 for the clippard and front box. I think it was #8.
This. I agree on all parts.
#4916475
There are other sound board options you can use but you would have to update the code to use them.

One option to free up space on the fx board is to use audacity (https://www.audacityteam.org/) to encode any sound that is not used as a loop to OGG and then update the file and code. I think you will save a lot of space. The only ones that need to be wav are the looped ones like the background hum and firing sounds. The decode of ogg is a little much for the looping sounds I found. On my droid I have near 100 sounds and 7 full stereo songs and still have space on the board.
  • 1
  • 13
  • 14
  • 15
  • 16
  • 17

The shot where the kids get pulled over for not dr[…]

Looks to me like the same part. If you're not a […]

Fitzhume - Proton Pack Build

Yes. I think the template shows the motherboard li[…]

We're bringing you the latest updates to the Ghost[…]