Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- This code is for controlling a digital LED strip using an IR remote, the strip responds
- to audio using the MSGEQ7 audio frequency multiplexer. The infrared remote used is from
- some crappy stereo remote but some tips for using an xbox remote are included and commented
- out.
- fastSPI_LED:
- http://code.google.com/p/fastspi/
- IR library and code based off Ken Sheriff:
- http://www.arcfn.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html
- The fast SPI library uses an interrupt timer to do its own pushing of signals to the strip.
- An array with an RGB strucure is populated with values then you tell it to send that to the
- strip.
- The problem is IR-decode also runs in the background based on interrupts, so it will return
- junk data for some reason unelss you comment out cli() in the fast SPI library (the .cpp
- file, there is an instance for each chip type). cli() is used to turn off background
- interrupts. I held down the remote button for a while, nothing bad happened to the strip
- display, guess its ok.
- Special thanks to Andrew Tuline for confirming this tip, his project is pretty much
- the same thing as this.
- -James Congdon http://hackaday.com/author/jcongdon/
- Project Writeup: http://hackaday.com/2012/09/11/disco-planet-a-massive-rgbw-led-array-in-a-6-globe/
- 9/13/12: fixed some comment typos and deleted commented notes on stuff that I had already
- fixed. Also moved the section divider that was out of place. -James
- */
- #include <FastSPI_LED.h>
- #include <IRremote.h>
- //------------------ Definitions ---------------------
- //IR Remote
- // XBOX: buttons that are held down send separate codes, long long used for xbox
- // NOTE: codes currently are for cheap POS stereo remote.
- //#define IRCode_UP 0x0800ff41eLL
- #define IRCode_PWR 0xDCC8B2BE
- #define IRCode_PREV 0xD2E3379E
- #define IRCode_NEXT 0xF200A55A
- #define IRCode_STOP 0x72BECA1F
- #define IRCode_PLAY 0x99EE44DF
- //LED Strip
- // Clock is set at Pin 13, Data at Pin 11, this can not be changed.
- #define PixelCount 240 //Set you number of LED pixels here
- #define PixelCountZB PixelCount-1 //zero biased values since the array's 0 position is the first pixel
- #define halfStrip PixelCount/2
- #define halfStripZB halfStrip-1
- //MSGEQ7
- #define noiseLvl 100 // the eq output is 0-5v, so 0-1024, this sets a cutoff level below which noise is ignored
- //functions
- #define billyD .15 //for the smooth function
- //------------------ Pin assignments -----------------------
- //MSGEQ7
- const int analogPin = 0; // read from multiplexer using analog input
- const int analogPin2 = 1; // read random from this, maybe something else?
- const int strobePin = 4; // MSGEQ7 strobe pin
- const int resetPin = 5; // MSGEQ7 reset pin
- const int buttonPin = 8; // Pin a button (active high) is connected to
- const int statusPin = 7; //3 watt status LED, PWM not supported on this pin, bad
- //IR Receiver
- int RECV_PIN = 6;
- //LED Strip
- //const int fSPI_PIN = 3; //pin only used for TM1809 strip (may be missing other initializations for this strip)
- //------------------ Global Variables -----------------------
- //Infrared
- IRrecv irrecv(RECV_PIN);
- //int IRstate = HIGH; //dunno what this does
- //iIRrecv blink13(0); //pin 13 disabled since the led strip is wont to use it
- decode_results results;
- //Function
- int ledRed = 0; //using these so functions can output 3 values
- int ledGreen = 0;
- int ledBlue = 0;
- int mode = 0; // Mode index
- int buttonState = 0;
- int LoopCnt = 0; // Loop Counter used in several functions, should not be less than 0 but can be large
- unsigned long LoopStart = 0; // Loop Timestamp used in slowWheel
- boolean StrobeOn = true; // Strobe state
- int irscale = 0; //so IR remote can mess with delay times
- //MSGEQ7
- int spectrumValue[7]; // to hold a2d values 0-1024
- //LED Strip
- struct CRGB { unsigned char r; unsigned char g; unsigned char b; }; //sets the bit order of the LED values, so if strip is grb you just order these differently
- struct CRGB *leds; //defines pointer to CRGB as *leds
- //------------- SETUP ------------------------
- void setup() {
- //------------------ Debug -----------------------
- // Serial.begin(9600);
- // Serial.println("Starting up...");
- //------------------ Setup IR Remote -----------------------
- irrecv.enableIRIn(); // Start the IR receiver
- irrecv.blink13(false);
- //------------------ Setup FastSPI -----------------------
- FastSPI_LED.setCPUPercentage(50); // how often to update leds: start with 50% CPU usage. up this if the strand flickers or is slow
- FastSPI_LED.setLeds(PixelCount);
- FastSPI_LED.setChipset(CFastSPI_LED::SPI_WS2801); //other drivers supported: TM1809, LPD6803, HL1606, 595, WS2801
- //FastSPI_LED.setPin(fSPI_PIN); //pin only used for TM1809
- FastSPI_LED.setDataRate(3); //if random flashing occurs (ws2801s i'm looking at you) set to 1-7 to slow data rate
- FastSPI_LED.init();
- FastSPI_LED.start();
- leds = (struct CRGB*)FastSPI_LED.getRGBData(); //magical array of LED, idk what this line does but it's required
- // Update the strip to clear wonkily powerup drivers
- memset(leds, 0, PixelCount * 3); //memset blanks without writing
- FastSPI_LED.show(); //but we write next anyway, oh well.
- //------------------ Setup MSGEQ7 -----------------------
- pinMode(buttonPin, INPUT);
- pinMode(analogPin, INPUT);
- pinMode(strobePin, OUTPUT);
- pinMode(resetPin, OUTPUT);
- digitalWrite(resetPin, LOW);
- digitalWrite(strobePin, HIGH);
- digitalWrite(statusPin, HIGH);
- //------------------ Other Setup -----------------------
- analogReference(DEFAULT); //change to default to use 1v, though this is fine (eq is 0-5 though)
- //pinMode(analogPin2, INPUT); //use this for random
- }
- void loop() {
- //------------------ Check Button -----------------------
- checkButton(300);
- // Serial.println(buttonState);
- //------------------ Check IR Remote ----------------------
- checkIR();
- // ------------------ Execute the current effect based on value of mode ----------------------
- //Serial.println(mode, DEC);
- switch (mode) {
- case 0:
- EQcolorOrgan();
- delay(20);
- break;
- case 1:
- EQcenterBar();
- delay(15);
- break;
- case 2:
- SlowWheel(100 + (irscale * 10)); //SLOW wheel
- break;
- case 3:
- FullFollow();
- delay(5 + irscale);
- break;
- case 4:
- EQsingleColorStep();
- delay(5 + irscale);
- break;
- case 5:
- EQvolbar();
- delay(20);
- break;
- case 6:
- Strobe(30 + irscale); //30 is probably a functional minimum before it becomes just a dimmed light bulb, also I hate this mode
- break;
- case 7:
- rainbow();
- delay(25);
- break;
- case 8:
- rainbowCycle(2);
- delay(5 + irscale);
- break;
- case 9:
- rainbowCycleEQ(6);
- delay(5 + irscale);
- break;
- //more modes can go in here, but change power function and mode change function accordingly.
- default:
- //"off".. more or less.
- break;
- }
- }
- // End main loop
- //------------------ MODES ---------------------------
- void Strobe(int msDelay){
- // blasts out eyes with wanton disregard. No interaction
- if(LoopStart + msDelay < millis()){ //Set the number of milliseconds for strobe on here
- //Update the strip
- if(StrobeOn){
- colorFill(255, 255, 255);
- StrobeOn = false;
- }else{
- colorFill(0, 0, 0);
- StrobeOn = true;
- }
- //Setup for next loop
- LoopStart = millis();
- }
- }
- void SlowWheel(int cchanged){
- //SlowWheel uses a timer instead of a delay, fades can be imperceptibly slow
- //cchanged is a color fill delay int, should be a high number otherwise why not just use wheel function.
- if(LoopStart + cchanged < millis()){ //Set the number of milliseconds to wait for a color change here
- Wheel(LoopCnt);
- colorFill(ledRed, ledGreen, ledBlue);
- // Serial.print("CNT: "); // Debug -----------------------
- // Serial.println(LoopCnt); // Debug -----------------------
- LoopCnt++; // Increment the wheel counter
- if (LoopCnt >= 768){LoopCnt=0;} // Counter at the end of wheel, move it back to 0
- LoopStart = millis(); // Rest the timestamp
- }
- }
- void EQcenterBar(){
- // reads eq and plugs RGB values directly into each consecutive pixel, mirrored from center.
- if(LoopCnt <= PixelCountZB) {
- readEQ();
- //Add new color to array at pointer position
- leds[LoopCnt].r = ledRed;
- leds[LoopCnt].g = ledGreen;
- leds[LoopCnt].b = ledBlue;
- //now the opposite
- leds[PixelCountZB - LoopCnt].r = ledRed;
- leds[PixelCountZB - LoopCnt].g = ledGreen;
- leds[PixelCountZB - LoopCnt].b = ledBlue;
- FastSPI_LED.show();
- LoopCnt++;
- }else{LoopCnt=0;}
- }
- void EQcolorOrgan(){
- //reads eq, punches value into the whole strip at once
- readEQ();
- colorFill(ledRed, ledGreen, ledBlue);
- }
- void FullFollow(){
- //steps from one end of the array to the other updating EQ values pixel by pixel
- readEQ();
- if(LoopCnt <= PixelCountZB) {
- //Add new color to array at pointer position
- leds[PixelCountZB - LoopCnt].r = ledRed; //not really sure why it's from this side
- leds[PixelCountZB - LoopCnt].g = ledGreen;
- leds[PixelCountZB - LoopCnt].b = ledBlue;
- LoopCnt++; // Increment the array position
- FastSPI_LED.show();
- }else{LoopCnt = 0;}
- }
- void EQsingleColorStep(){//mod to include color RGB scales?
- // this takes the three eq values and just shoves them into a single color's values three at a time.
- // kinda lame, ill write it anyway. flag it for a remote control option to change colors or something.
- readEQ();
- if (LoopCnt <= PixelCountZB ) {
- leds[LoopCnt].r = 0;
- leds[LoopCnt].g = ledRed;
- leds[LoopCnt].b = 0;
- LoopCnt++;
- leds[LoopCnt].r = 0;
- leds[LoopCnt].g = ledGreen;
- leds[LoopCnt].b = 0;
- LoopCnt++;
- leds[LoopCnt].r = 0;
- leds[LoopCnt].g = ledBlue;
- leds[LoopCnt].b = 0;
- LoopCnt++;
- } else {LoopCnt=0;}
- FastSPI_LED.show();
- }
- void EQvolbar() {
- //EQ volume bar, each color has its own bar from the strip's center
- //I want the color to fade out towards the end, to keep center from just being white
- //something to do later.
- readEQ();
- memset(leds, 0, PixelCount * 3); //quickly wipe the array without touching variables
- int rngR = map(ledRed, 0, 256, 0, halfStripZB); //map the average to strip length, a bit sloppy, but i'm not smart enough to calculate once
- int rngG = map(ledGreen, 0, 256, 0, halfStripZB);
- int rngB = map(ledBlue, 0, 256, 0, halfStripZB);
- for(int i=0; i <= rngR; i++){
- leds[halfStripZB-i].r = ledRed;
- leds[halfStripZB+i].r = ledRed;
- }
- for(int i=0; i <= rngG; i++){
- leds[halfStripZB-i].g = ledGreen;
- leds[halfStripZB+i].g = ledGreen;
- }
- for(int i=0; i <= rngB; i++){
- leds[halfStripZB-i].b = ledBlue;
- leds[halfStripZB+i].b = ledBlue;
- }
- FastSPI_LED.show();
- }
- void rainbow() {
- // rainbow accepts a color change delay value and fades the wheel color by color pixel by pixel
- // I pulled out the loop so that the IR and button checks were performed every cycle
- // also uses LoopCnt global variable. the wheel equation scales the entire strip to a 0-255 value based
- // on the position of the pixel and the position of the loop count (which offsets the wheel's start position)
- if(LoopCnt <= 767) {
- for (int i=0; i < PixelCountZB; i++) {
- Wheel( (i + LoopCnt) % 767 );
- leds[i].r = ledRed;
- leds[i].g = ledGreen;
- leds[i].b = ledBlue;
- }
- LoopCnt++;
- FastSPI_LED.show();
- }else{LoopCnt=0;}
- }
- void rainbowCycle(long stripFades) { //stripfades should be an int, long keeps big number errors at bay
- //The confusingly named rainbowcycle attempts to evenly split the rainbow fade values along the LED strip
- //stripFades stripfades is the number of fades across the strip, I don't fully understand the equation here
- //and I had to make it used floating point calculation, so it runs slow. Adapted from adafruit's library.
- if(LoopCnt <= 767) {
- for (int i=0; i < PixelCountZB; i++) {
- Wheel( ((i * (256 * stripFades) / PixelCount) + LoopCnt) % 767); //loopcnt handles the offset, while i splits 255 evenly among the pixel count
- leds[i].r = ledRed;
- leds[i].g = ledGreen;
- leds[i].b = ledBlue;
- }
- LoopCnt++;
- FastSPI_LED.show();
- }else{LoopCnt=0;}
- }
- void rainbowCycleEQ(long stripFades) {
- // Lowers the brightness values of rainbow cycle and uses the EQ color values to make up the difference.
- // short strips of colors travel down the pixels, and get larger as volume increases fading into
- // each other. it may look better if the effect is massively slowed or augmented in some way, color
- // interaction is hard to see.
- int red, green, blue;
- if(LoopCnt <= 767) {
- readEQ(); //one eq value per loop, keeps things from gettng wacky.
- red = ledRed - 200; //instead of halving and then adding im taking these into the negatives.
- green = ledGreen - 200;
- blue = ledBlue - 200;
- for (int i=0; i < PixelCountZB; i++) {
- Wheel( ((i * (256 * stripFades) / PixelCount) + LoopCnt) % 767 ); //the fade number is float otherwise int is overloaded... probably very slow
- leds[i].r = constrain(ledRed + red, 0, 255); //this is a dumb way to boost bass, I just need a better input
- leds[i].g = constrain(ledGreen + green, 0, 255);
- leds[i].b = constrain(ledBlue + blue, 0, 255);
- }
- LoopCnt++;
- FastSPI_LED.show();
- }else{LoopCnt=0;}
- }
- // ----------------------- Color Support Functions -----------------------
- void colorWipe(int red, int green, int blue) {
- // Used to open up loops, RGB values write to pixel set by loop count, strip is then displayed
- // Each call of the function advances pixel count.
- if(LoopCnt <= PixelCountZB) {
- leds[LoopCnt].r = red;
- leds[LoopCnt].g = green;
- leds[LoopCnt].b = blue;
- LoopCnt++; // Increment the counter
- FastSPI_LED.show(); // write all the pixels out
- }else{LoopCnt=0;}
- }
- void colorWipeBack(int red, int green, int blue, uint8_t wait) {
- // Used to open up loops, RGB values write to pixel set by loop count, strip is then displayed
- // Each call of the function LOWERS pixel count.
- if(LoopCnt <= PixelCountZB) {
- leds[PixelCountZB - LoopCnt].r = red;
- leds[PixelCountZB - LoopCnt].g = green;
- leds[PixelCountZB - LoopCnt].b = blue;
- LoopCnt++; // Increment the counter
- FastSPI_LED.show(); // write all the pixels out
- }else{LoopCnt=0;}
- }
- void colorFill(int red, int green, int blue) {
- //makes a filled array of the chosen color all at once (resets pixel count), then writes it.
- for (int i=0; i < PixelCount; i++) { //is there a more direct way to pass these? this works.
- leds[i].r = red;
- leds[i].g = green;
- leds[i].b = blue;
- }
- FastSPI_LED.show();
- }
- void Wheel(int WheelPos){
- //wheel accepts 0-767 (0 biased I guess) position int, it sets the global ledRed, ledGreen and ledBlue
- //values based on this position value. each color gets 256 positions. wheelpos binary number is
- //split to thirds by the bit shift.
- switch(WheelPos >> 8)
- {
- case 0:
- ledRed = 255 - WheelPos % 256; //Red down
- ledGreen = WheelPos % 256; // Green up
- ledBlue = 0; //blue off
- break;
- case 1:
- ledGreen = 255 - WheelPos % 256; //green down
- ledBlue = WheelPos % 256; //blue up
- ledRed = 0; //red off
- break;
- case 2:
- ledBlue = 255 - WheelPos % 256; //blue down
- ledRed = WheelPos % 256; //red up
- ledGreen = 0; //green off
- break;
- }
- }
- void readEQ() {
- //read the values from the MSGEQ7
- digitalWrite(resetPin, HIGH);
- digitalWrite(resetPin, LOW);
- for (int q = 0; q < 7; q++)
- {
- digitalWrite(strobePin, LOW);
- delayMicroseconds(30); // to allow the output to settle
- // spectrumValue[q] = smooth(analogRead(analogPin), billyD, spectrumValue[q]); //may take too long
- spectrumValue[q] = analogRead(analogPin);
- // Serial.print(q);
- // Serial.print(" ");
- // Serial.println(spectrumValue[q]);
- digitalWrite(strobePin, HIGH);
- }
- //Set the LED Colors
- //this takes the analog frequency values and averages some bands
- //the values are then set zero if not above a noise level, then mapped
- //from their 0-1024 value to 1-255
- ledBlue = max(spectrumValue[6], spectrumValue[5]);
- if (ledBlue <= noiseLvl){
- ledBlue=0;
- }else{
- ledBlue = map(ledBlue, noiseLvl, 1024, 1, 255);
- }
- ledGreen = spectrumValue[4]; //4 is crazy, its like 3x higher than everything else... probably my mic.
- if (ledGreen <= noiseLvl){
- ledGreen=0;
- }else{
- ledGreen = map(ledGreen, noiseLvl, 1024, 1, 255);
- }
- ledRed = max(spectrumValue[3], max(spectrumValue[2], max(spectrumValue[1], spectrumValue[0]))); //kind of out of control with the max functions, but red isnt showing enough!
- if (ledRed <= noiseLvl){
- ledRed=0;
- }else{
- ledRed = map(ledRed, noiseLvl, 1024, 1, 255);
- }
- }
- void checkButton(int debounce){
- // read the pushbutton input pin:
- buttonState = digitalRead(buttonPin);
- if (buttonState == HIGH) {
- // Wait a bit then check again to be sure
- delay(debounce);
- buttonState = digitalRead(buttonPin);
- if (buttonState == HIGH) {
- ChangeMode(true); //The button was pressed, change the mode
- }
- }
- }
- void ChangeMode(boolean ModeUP) {
- //incrament the mode and recycle if at full
- //I can't wait to get the xbox remote running and delete this function
- if (ModeUP){
- digitalWrite(statusPin, LOW);
- mode++;
- if (mode > 9) {digitalWrite(statusPin, LOW); mode = 0;}
- }else{
- digitalWrite(statusPin, LOW);
- mode--;
- if (mode < 0) {digitalWrite(statusPin, LOW); mode = 9;}
- }
- // Clear the strip
- memset(leds, 0, PixelCount * 3);
- FastSPI_LED.show();
- LoopStart = 0; //reset time
- delay(20); //dont like it, but otherwise the LED might not be visible, and modes already change too quickly
- digitalWrite(statusPin, HIGH);
- }
- void checkIR(){
- if (irrecv.decode(&results)) {
- // Serial.println(results.value); // Debug
- // Serial.println(results.value >> 32, HEX); //Debug Xbox
- // Serial.println(results.value, HEX); //Debug Xbox
- // in the ifs the IR code also gets & ~0x8000LL in order to handle the flipped bit from every other press, haven't tested though.
- switch (results.value) {
- case IRCode_NEXT:
- ChangeMode(true);
- break;
- case IRCode_PREV:
- ChangeMode(false);
- break;
- case IRCode_PWR:
- mode = 10; //bump it out to default
- memset(leds, 0, PixelCount * 3);
- FastSPI_LED.show();
- break;
- case IRCode_PLAY:
- irscale = constrain(irscale + 5, 0, 1000);
- break;
- case IRCode_STOP:
- irscale = constrain(irscale - 5, 0, 1000);
- break;
- }
- irrecv.resume(); // Receive the next value
- }
- }
- int smooth(int data, float filterVal, float smoothedVal){
- //with all the glorious computational overhead I decide to swipe a value smoother from
- //http://arduino.cc/playground/Main/Smooth I guess this could help out.
- // filterVal = constrain(filterVal, 0, .99); //make sure param's are within range
- smoothedVal = (data * (1 - filterVal)) + (smoothedVal * filterVal);
- return (int)smoothedVal;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement