Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <FastLED.h>
- #include <elapsedMillis.h>
- #include "RunningAverage.h"
- #define STROBE_PIN 4
- #define RESET_PIN 5
- #define AUDIO_PIN A5
- #define SPL_PIN A4
- #define BANDEQ_PIN A0
- #define LED_POWER 6
- #define LED_PIN 7
- #define POWER_SWITCH 2
- #define POT_INPUT A3
- #define COLOR_ORDER GRB
- #define CHIPSET WS2811
- #define NUM_LEDS 144
- #define BRIGHTNESS 255
- #define FRAMES_PER_SECOND 60
- #define NUMPARTS 28 // note that blobs recycles the particle array but only NUMBLOBS of them
- #define NUMBLOBS 7
- byte COOLING = 55;
- byte SPARKING = 120;
- bool sleepmode = 0;
- bool lastpower = 1;
- byte lastvizval = 255; // Never have this many viz so this forces an init
- typedef struct PARTICLE {
- float accel;
- float pos;
- bool alive;
- };
- PARTICLE parts[NUMPARTS];
- CRGB leds[NUM_LEDS];
- RunningAverage soundlevel(20);
- elapsedMillis splTimer;
- elapsedMillis splTest;
- int tspechigh[7]; // high water marks for MSGEQ7 bands
- float spectrumdata[7]; // where 0 is no spectral energy in the MSGEQ7 band and 1 is maximum
- RunningAverage spectrumhigh[7] = {RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30)};
- void setup() {
- Serial.begin(115200);
- pinMode(SPL_PIN, INPUT); // For general audio level
- pinMode(AUDIO_PIN, INPUT); // For raw analog in such as FFTs, dunno if the Leonardo has the grunt...
- pinMode(STROBE_PIN, OUTPUT); // MSGEQ7 reset
- pinMode(RESET_PIN, OUTPUT); // MSGEQ7strobe
- pinMode(BANDEQ_PIN, INPUT); // MSGEQ7 analog in, multiplexed based on reset and strobe
- analogReference(DEFAULT);
- pinMode(LED_POWER, OUTPUT); // For WS2812 strip - currently not working
- pinMode(POWER_SWITCH, INPUT); // For WS2812 strip - currently not working
- digitalWrite(LED_POWER, HIGH); // if it did work, this is what we'd do to switch them on.
- FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
- FastLED.setBrightness( BRIGHTNESS ); // One must be very careful if attempting to power 144 leds over USB...
- FastLED.setCorrection( CRGB(180,160,255));
- //
- for (int i = 0;i<7;i++) {
- tspechigh[i]=750;
- }
- initparticles();
- }
- // Function to read 7 band equalizers
- // You control the decay on an MSGEQ7 by the read rate, so essentially
- // feeding this function a number will just loop through 'decay' times
- // saving the value from the first iteration only. This was more important
- // for other visualisations, here the low decay rate is okay.
- void readMSGEQ7(byte decay)
- {
- for (byte d = 0; d < decay; d++)
- {
- digitalWrite(RESET_PIN, HIGH);
- digitalWrite(RESET_PIN, LOW);
- for (byte band=0; band <7; band++)
- {
- digitalWrite(STROBE_PIN,LOW); // strobe pin on the chip - kicks the IC up to the next band
- delayMicroseconds(30);
- if (d == 0)
- {
- int bandac = analogRead(BANDEQ_PIN);
- spectrumdata[band] = scaleadc(band,bandac);
- }
- digitalWrite(STROBE_PIN,HIGH);
- }
- }
- }
- int readpot()
- {
- return analogRead(POT_INPUT);
- }
- bool getpowerswitch()
- {
- return !digitalRead(POWER_SWITCH);
- }
- // This scales the input adc value in MSEQ7 spectrum band 'band'
- // and puts in global variables
- float scaleadc(byte band,int bandac)
- {
- static byte scaleiter = 0;
- #define tickval 30 // this controls speed of the auto scaling effect on the MSGEQ7
- #define noisefloor 80
- scaleiter++;
- if (bandac > tspechigh[band]) tspechigh[band] = bandac; // Either we have a high water mark
- else tspechigh[band] -= tickval; // Or we don't so we reduce the high water mark slightly
- if ((scaleiter % 2) == 0) // only grab averages averages half as often
- {
- spectrumhigh[band].addValue(tspechigh[band]);
- }
- float bandwidth = spectrumhigh[band].getAverage();
- if (bandwidth < 200) bandwidth = 200; // 1/3rd scale is the minimum
- float headroom = 1;
- if (bandwidth < 820) headroom = 1.2; // 820 is 80% of 1024
- float scalebandwidth = (1024.0 / (bandwidth*headroom)); // allow a little headroom
- int scaleout = ((bandac-noisefloor) * scalebandwidth);
- float floatscale = (scaleout / 1024.0) * (0.9+(band * 0.05)); // bit on the end is a bit of emphasis for higher freqs
- //float floatscale = (scaleout / 1024.0);
- //if (band == 6) floatscale = floatscale * 1.2;
- if (band == 0)
- {
- //Serial.print(bandwidth);
- //Serial.print(" ");
- //Serial.print(bandac);
- //Serial.print(" ");
- //Serial.print(floatscale);
- //Serial.println();
- }
- if (floatscale < 0) floatscale = 0; // we can get negatives because of the noise floor value
- if (floatscale > 1) floatscale = 1;
- return floatscale;
- }
- void loop()
- {
- #define NUMVIZ 6
- if (getpowerswitch() == 0 and lastpower == 1)
- {
- fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) ); // blank the leds
- FastLED.show();
- digitalWrite(LED_POWER, LOW); // Turn off power to LED strip (which doesn't really work)
- sleepmode = 1;
- lastpower = 0;
- }
- if (getpowerswitch() == 1 and lastpower == 0)
- {
- digitalWrite(LED_POWER, HIGH); // Turn off power to LED strip
- delayMicroseconds(50); // Wait for WS2812 to power up
- sleepmode = 0;
- lastpower = 1;
- }
- if (!sleepmode) {
- // do for all viz
- readMSGEQ7(10);
- random16_add_entropy( random() );
- int potval = readpot();
- byte vizval = potval / (1024/NUMVIZ); // get a viz number based on the pot position
- bool newviz = 0;
- if (vizval != lastvizval) newviz = 1; // if it's changed, trigger the initialisations
- switch (vizval)
- {
- case 0:
- if (newviz) initparticles();
- moveparticles();
- renderparticles();
- break;
- case 1:
- if (newviz) initblobs();
- moveblobs();
- renderblobs();
- break;
- case 2:
- Fire2012();
- break;
- case 3:
- if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
- specrain();
- break;
- case 4:
- if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
- specrain2();
- break;
- case 5:
- if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
- randomspec();
- break;
- }
- lastvizval = vizval;
- newviz = 0;
- FastLED.show(); // display this frame
- FastLED.delay(1000 / FRAMES_PER_SECOND);
- if (splTimer > 100) // 20 samples over 100ms = two seconds
- {
- int rawspl = analogRead(SPL_PIN);
- soundlevel.addValue(rawspl);
- splTimer = 0;
- //Serial.println(soundlevel.getAverage());
- //Serial.println(calcpower(leds,NUM_LEDS,BRIGHTNESS));
- //Serial.println(getpowerswitch());
- }
- if (splTest > 1000)
- {
- int curav = soundlevel.getAverage();
- curav = constrain(curav,0,200);
- COOLING = map(curav,0,200,250,20);
- splTest=0;
- SPARKING = curav;
- }
- } else {
- // if we're in sleep mode
- delay(100);
- }
- }
- void initparticles()
- {
- for (byte b = 0; b<NUMPARTS;b++)
- {
- parts[b].pos = 0; // at the bottom
- parts[b].accel = 0; // not moving
- parts[b].alive = 0; // and dead...
- }
- }
- void moveparticles()
- {
- for (byte b = 0; b<NUMPARTS;b++)
- {
- if (parts[b].alive)
- {
- parts[b].pos += parts[b].accel; // more accurately accel is velocity
- // parts[b].accel += 0.01; // adds a little acceleration upwards
- // if (parts[b].pos > (NUM_LEDS-1)) parts[b].alive = 0; // reached the end of the line
- if (parts[b].pos > (NUM_LEDS-1))
- {
- parts[b].accel = -parts[b].accel;
- parts[b].pos = NUM_LEDS-1;
- }
- else if (parts[b].pos < 0)
- {
- parts[b].alive = 0;
- }
- else parts[b].accel -= 0.02;
- }
- else
- {
- // one-in-ten chance (meaning on average
- if (random16(3,400) < (100*spectrumdata[b%7])) // if basing spawn chance on spectrum energy
- //if (random(5,1023) < soundlevel.getAverage()) // or overall sound level
- {
- parts[b].pos = 0;
- //parts[b].accel = 1+(spectrumdata[b%7]*2.5*(0.75+(soundlevel.getAverage()/750))); // sets particle speed based on that particle's spectrum energy
- parts[b].accel = 1+(spectrumdata[b%7]*1.75);
- parts[b].alive = 1;
- }
- }
- }
- }
- void renderparticles()
- {
- //for (byte l = 0; l < 144; l++) leds[l].setRGB(0,0,0); // black out all the leds
- fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) ); // do it properly :)
- for (byte b = 0; b<NUMPARTS;b++)
- {
- if (parts[b].alive) // only render shit that's alive
- {
- CRGB tempcol;
- byte lpos = parts[b].pos; // converts float position to a byte which is our led number
- float specval = spectrumdata[b%7]; // particles 0-6 are MSGEQ bands, 7-13 the same bands etc
- byte lumval = (32+(223*specval)); // calculates base luminance
- //byte hueval = 160-(160*specval)+(10*(b%7));
- byte hueval = (26*(b%7)); // new fixed hue per band starting from 0 (red) for bass up to blue for treble
- tempcol.setHSV(hueval,255,lumval); // static or slow
- int direction;
- if (parts[b].accel > 0) direction = -1;
- else direction = 1;
- setled(lpos,tempcol);
- if (spectrumdata[b%7] > 0.1) setled(lpos-direction,tempcol);
- if (abs(parts[b].accel) > 0.25) // slow
- {
- tempcol %=160;
- setled(lpos+(direction),tempcol);
- }
- if (abs(parts[b].accel) >0.75) // fast trail
- {
- tempcol %=160;
- setled(lpos+(direction*2),tempcol);
- }
- if (abs(parts[b].accel) >1) // really fast trail
- {
- tempcol %=160;
- setled(lpos+(direction*3),tempcol);
- }
- if (abs(parts[b].accel) >1.5) // really fast trail
- {
- tempcol %=160;
- setled(lpos+(direction*4),tempcol);
- }
- }
- }
- }
- // Easy way to make sure led positions are in bounds
- void setled(byte ledpos, CRGB ledval)
- {
- if (ledpos >=0 and ledpos <= (NUM_LEDS-1))
- {
- leds[ledpos] += ledval;
- }
- }
- void Fire2012()
- {
- // Array of temperature readings at each simulation cell
- static byte heat[NUM_LEDS];
- // Step 1. Cool down every cell a little
- for( int i = 0; i < NUM_LEDS; i++) {
- heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
- }
- // Step 2. Heat from each cell drifts 'up' and diffuses a little
- for( int k= NUM_LEDS - 3; k > 0; k--) {
- heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
- }
- // Step 3. Randomly ignite new 'sparks' of heat near the bottom
- if( random8() < SPARKING ) {
- int y = random8(7);
- heat[y] = qadd8( heat[y], random8(160,255) );
- }
- // Step 4. Map from heat cells to LED colors
- for( int j = 0; j < NUM_LEDS; j++) {
- leds[j] = HeatColor( heat[j]);
- }
- }
- // CRGB HeatColor( uint8_t temperature)
- // [to be included in the forthcoming FastLED v2.1]
- //
- // Approximates a 'black body radiation' spectrum for
- // a given 'heat' level. This is useful for animations of 'fire'.
- // Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
- // This is NOT a chromatically correct 'black body radiation'
- // spectrum, but it's surprisingly close, and it's extremely fast and small.
- //
- // On AVR/Arduino, this typically takes around 70 bytes of program memory,
- // versus 768 bytes for a full 256-entry RGB lookup table.
- CRGB HeatColor( uint8_t temperature)
- {
- CRGB heatcolor;
- // Scale 'heat' down from 0-255 to 0-191,
- // which can then be easily divided into three
- // equal 'thirds' of 64 units each.
- uint8_t t192 = scale8_video( temperature, 192);
- // calculate a value that ramps up from
- // zero to 255 in each 'third' of the scale.
- uint8_t heatramp = t192 & 0x3F; // 0..63
- heatramp <<= 2; // scale up to 0..252
- // now figure out which third of the spectrum we're in:
- if( t192 & 0x80) {
- // we're in the hottest third
- heatcolor.r = 255; // full red
- heatcolor.g = 255; // full green
- heatcolor.b = heatramp; // ramp up blue
- } else if( t192 & 0x40 ) {
- // we're in the middle third
- heatcolor.r = 255; // full red
- heatcolor.g = heatramp; // ramp up green
- heatcolor.b = 0; // no blue
- } else {
- // we're in the coolest third
- heatcolor.r = heatramp; // ramp up red
- heatcolor.g = 0; // no green
- heatcolor.b = 0; // no blue
- }
- return heatcolor;
- }
- // from the WS2812 data sheet http://www.adafruit.com/datasheets/WS2812.pdf
- // leds draw 20ma per channel. This is lies. Measured current suggests 12.7ma
- // per channel when all are white and with mimimal voltage drop, e.g. distance from power.
- // Voltage drop happens even with 60leds/m at 3m in length
- // Therefore one should power led strip from both ends. This calculation is also 'worst case'
- // power consumption assuming you have done this.
- float calcpower(CRGB leds[], int numleds, byte globalbright)
- {
- #define maperled 12.7
- float totalcurrent = 0;
- for (int l = 0; l<numleds; l++)
- {
- totalcurrent += ((float)leds[l].r/255.0) * maperled;
- totalcurrent += ((float)leds[l].g/255.0) * maperled;
- totalcurrent += ((float)leds[l].b/255.0) * maperled;
- }
- return (totalcurrent/1000) * ((float)globalbright/255.0) * 5;
- }
- //
- // Blobs viz
- //
- void initblobs()
- {
- for (byte b = 0; b<NUMBLOBS;b++)
- {
- parts[b].pos = 0; // at the bottom
- parts[b].accel = 0.5+(b*0.25); // not moving
- parts[b].alive = 1; // and alive...
- }
- }
- void moveblobs()
- {
- for (byte b = 0; b<NUMBLOBS;b++)
- {
- parts[b].pos += parts[b].accel; // more accurately accel is velocity
- if (parts[b].pos > (NUM_LEDS-1))
- {
- parts[b].accel = -0.25-(4*spectrumdata[b%7]);
- parts[b].pos = NUM_LEDS-1;
- }
- else if (parts[b].pos < 0)
- {
- parts[b].accel = 0.25+(4*spectrumdata[b%7]);
- parts[b].pos = 0;
- }
- else {
- if (parts[b].pos > (NUM_LEDS/2)) parts[b].accel -= 0.04+(spectrumdata[b%7]/9);
- else parts[b].accel += 0.04-(spectrumdata[b%7]/9);
- }
- }
- }
- void renderblobs()
- {
- //for (byte l = 0; l < 144; l++) leds[l].setRGB(0,0,0); // black out all the leds
- fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) ); // do it properly :)
- for (byte b = 0; b<NUMBLOBS;b++)
- {
- CRGB tempcol;
- byte lpos = parts[b].pos; // converts float position to a byte which is our led number
- byte specval = 1+spectrumdata[b%7]*30; // particles 0-6 are MSGEQ bands, 7-13 the same bands etc
- //byte hueval = 160-(160*specval)+(10*(b%7));
- byte hueval = (35*(b%7)); // new fixed hue per band starting from 0 (red) for bass up to blue for treble
- byte lumval = (64+(191*spectrumdata[b%7])); // calculates base luminance
- tempcol.setHSV(hueval,255,lumval); // static or slow
- bool hitbound = 0;
- for (byte l=0; l<specval; l++)
- {
- tempcol %=128;
- setled(lpos+l,tempcol);
- setled(lpos-1-l,tempcol);
- }
- }
- }
- // spectral rain
- void specrain()
- {
- for (int i = 0; i<7; i++)
- {
- // move each one down
- memmove( &leds[(i*20)+0], &leds[(i*20)+1], (19) * sizeof(CRGB) );
- byte hueval = 160-(160*spectrumdata[i]);
- byte lumval = 255*spectrumdata[i];
- leds[(i*20)+19].setHSV(hueval,255,lumval);
- }
- }
- void specrain2()
- {
- static byte noflicker = 0;
- static byte maxband = 0;
- fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) ); // do it properly :)
- noflicker++;
- if (noflicker > 4)
- {
- noflicker = 0;
- float maxbandval = 0;
- for (int i = 0; i<7; i++)
- {
- if (spectrumdata[i] > maxbandval)
- {
- maxband = i;
- maxbandval = spectrumdata[i];
- }
- }
- }
- CRGB newcol;
- byte hueval = maxband*26;
- byte lumval = 255*spectrumdata[maxband];
- newcol.setHSV(hueval,255,lumval);
- for (int d = 0; d<12; d++)
- {
- for (int y = 0; y<6;y++)
- {
- if (spectrumdata[maxband] >= ((1.0/6)*(y+1)))
- {
- leds[(d*12)+y] = newcol;
- leds[(d*12)+11-y] = newcol;
- }
- }
- }
- }
- void randomspec()
- {
- for (int i = 0; i<7; i++)
- {
- byte lpos = random8(0,47);
- if (random8(100) < spectrumdata[i]*100)
- {
- byte hueval = i*26;
- byte lumval = 255*spectrumdata[i];
- CRGB setcol;
- setcol.setHSV(hueval,255,lumval);
- leds[(lpos*3)+1] = setcol;
- setcol %= 64;
- leds[(lpos*3)] = setcol;
- leds[(lpos*3)+2] = setcol;
- } else {
- leds[(lpos*3)] = CRGB::Black;
- leds[(lpos*3)+1] = CRGB::Black;
- leds[(lpos*3)+2] = CRGB::Black;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement