This was covered a while back.
Assumption: there is a pixel animation running; the user sends a new command to change to a different pixel animation.
There are two basic parts,
- If a interrupt is received (from an IR code), all the pixels are made yellow.
- When you see the pixels turn yellow, you send the same IR command, a 2nd time, which then starts the new animation.
//********************************************^************************************************
//IR_remote_and_Neo_Pixels.ino
//
//A program that integrates an IR remote with a WS2812 LED strip.
//The 1st button press on the remote stops the current animation running on the LED strip.
//All LEDs on the strip turn Yellow for 2 seconds while waiting for a 2nd remote button to be pressed.
//A 2nd button press is evaluated and the new animation associated to the button starts running.
//No 2nd button press results in the current animation starting again, this happens after a 2
//second time-out.
//
// By: LarryD
// Version YY/MM/DD Description
// 1.00 19 10 01 working sketch; you have 2 seconds after the 1st button push for the 2nd push
//
//
//********************************************^************************************************
#define DECODE_NEC
#include <Arduino.h>
#include <IRremote.h> //version 3.3.0 (using a TSOP4838 receiver in this project)
#include <Adafruit_NeoPixel.h>
//********************************************^************************************************
//adjust to give the most pleasing results
#define NUM_PIXELS 15 //Rainbow gives ~520ma (i.e. one string of 31 LEDs)
#define KnightRiderSpeed 28 //
#define inOutOutInDelay 48 //
#define Cycles 1 //
#define Width 2 //
#define rainbowSpeed 6 //
#define zylonSpeed 28 //used in Zylon
#define theaterChaseDelay 100 //used with theaterChase
#define rainbowDelay 20 //used with rainbow
#define colorWipeDelay 50 //used with colorWipe
//increase to get narrow spots, decrease to get wider spots
#define FOCUS 65
//max LED brightness (1 to 255) – start with low values!
//high brightness requires a more power
#define BRIGHTNESS 255
//decrease to speed up, increase to slow down (it's not a delay actually)
#define DELAY 1500
//we have 3 colour spots (reg, green, blue) oscillating along the strip with different speeds
float spdr, spdg, spdb;
float offset;
int pos = 0; //used in Zylon
int dir = 1; //used in Zylon
//********************************************^************************************************
const byte IR_RECEIVE_PIN = 2; //must use pin 2
const byte interruptPin = 2; //must use pin 2
const byte feedBackLED = 8;
const byte PixelPin = 12;
const byte buzzerPin = 13;
byte mode = 12; //the current display mode
volatile byte IRflag = 0;
volatile unsigned long lastMillis;
//********************************************^************************************************
//17 button IR remote
//https://store.qkits.com/media/catalog/product/cache/de6f4998e28d20e4664ef81588e1bf0a/i/r/ir-transmitter-17-button_1.jpg
//
const byte buttonCode[18] =
{
0x46, 0x44, 0x40,
0x43, 0x15, 0x00, //0x00 is not defined (used as a place holder only)
0x16, 0x19, 0x0D,
0x0C, 0x18, 0x5E,
0x08, 0x1C, 0x5A,
0x42, 0x52, 0x4A
}; //END of buttonCode Array
const byte ASCIIcode[18] =
{
// ^ < OK (LEDs OFF)
0x5E, 0x3F, 0x3C,
// > v nul
0x3E, 0x76, 0x00, //0x00 is not defined (used as a place holder only)
// 1 2 3
0x31, 0x32, 0x33,
// 4 5 6
0x34, 0x35, 0x36,
// 7 8 9
0x37, 0x38, 0x39,
// * 0 #
0x3A, 0x30, 0x3B
}; //END of ASCIIcode Array
//define the Neo-Pixel strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PixelPin, NEO_GRB + NEO_KHZ800);
//********************************************^************************************************
// s e t u p ( )
void setup()
{
//Serial.begin(115200);
pinMode(buzzerPin, OUTPUT);
pinMode(feedBackLED, OUTPUT);
pinMode(interruptPin, INPUT);
IrReceiver.begin(IR_RECEIVE_PIN);
IrReceiver.stop();
//ignore a queued interrupt (UNO pin D02)
EIFR = 1;
//or
//EIFR = bit (INTF0); //clear flag for interrupt 0
attachInterrupt(digitalPinToInterrupt(interruptPin), firstIRcode, HIGH);
//********************************
strip.begin();
//Initialize all pixels to 'OFF'
clearStrip();
strip.show();
//********************************
//initialize pseudo-random number generator with some random value
randomSeed(analogRead(A0));
// assign random speed to each spot
spdr = 1.0 + random(200) / 100.0;
spdg = 1.0 + random(200) / 100.0;
spdb = 1.0 + random(200) / 100.0;
// set random offset so spots start in random locations
offset = random(10000) / 100.0;
} //END of setup()
//********************************************^************************************************
// l o o p ( )
void loop()
{
//********************************
//if we are not waiting for IR remote action, run animations
if (IRflag == 0)
{
checkStateMachine();
}
//********************************
//check for IR remote action
checkIRinterrupt();
} //END of loop()
//********************************************^************************************************
// f i r s t I R c o d e ( ) (ISR, TSOP4838 1st press)
void firstIRcode()
{
//set 1st code detected
IRflag = 1;
//restart the time-out TIMER
lastMillis = millis();
} //END of firstIRcode()
//********************************************^************************************************
// c h e c k I R i n t e r r u p t ( )
void checkIRinterrupt()
{
//*****************************************************
//is there an IR remote request for attention ?
if(IRflag == 0)
{
//no
return;
}
//*****************************************************
//when the 1st IR code is detected, has the time-out TIMER expired ?
if (IRflag == 1 && millis() - lastMillis >= 200)
{
//disable the ISR
detachInterrupt(digitalPinToInterrupt(interruptPin));
digitalWrite(feedBackLED, HIGH);
//setup 2nd IR coding
IRflag = 2;
//enable IR decoding
IrReceiver.start();
IrReceiver.resume();
//*********************
//after the 1st IR remote button press, all the pixels are turn to YELLOW
//this will let the user know they can now select the 'new' desired animation with a 2nd button press
for (int count = 0; count < NUM_PIXELS; count++)
{
strip.setPixelColor(count, 0x7F3F00); //yellow
}
strip.show();
//restart the time-out TIMER
lastMillis = millis();
} //END of if (IRflag == 1 && millis() - lastMillis >= 200)
//*****************************************************
//if we are waiting for the 2nd IR code, has this time-out TIMER expired ?
if (IRflag == 2 && millis() - lastMillis >= 2000)
{
IrReceiver.decode();
byte byteRX = IrReceiver.decodedIRData.command;
clearStrip();
delay(500);
//ignore a queued interrupt (UNO pin D02)
EIFR = 1;
//or
//EIFR = bit (INTF0); //clear flag for interrupt 0
//finished with the 2nd IR code, enable the ISR
IRflag = 0;
attachInterrupt(digitalPinToInterrupt(interruptPin), firstIRcode, HIGH);
IrReceiver.stop();
digitalWrite(feedBackLED, LOW);
}
//*****************************************************
//if we are waiting for the 2nd IR code, have we received it ?
if (IRflag == 2 && IrReceiver.decode())
{
//Make a buzzer sound
IrReceiver.stop();
tone(buzzerPin, 4000, 10);
delay(50);
IrReceiver.start();
//print a short summary of received data
//IrReceiver.printIRResultShort(&Serial);
byte byteRX = IrReceiver.decodedIRData.command;
//*********************
//convert the button code to it's ASCII code.
if (byteRX)
{
//Scan through the buttonCode Array.
for (int i = 0; i < 18; i++)
{
if (buttonCode[i] == byteRX)
{
//Serial.println(char(ASCIIcode[i])); //get the ASCII code
mode = ASCIIcode[i] - '0';
//keypad arrow buttons default to button 1 press
if (mode > 12)
{
//default
mode = 1;
}
//We found it, no need to continue looking.
break;
}
}
} //END of if(byteRX)
//*********************
clearStrip();
delay(500);
//ignore a queued interrupt (UNO pin D02)
EIFR = 1;
//or
//EIFR = bit (INTF0); //clear flag for interrupt 0
//we are finished with decoding the 2nd IR receive code, enable the ISR
IRflag = 0;
attachInterrupt(digitalPinToInterrupt(interruptPin), firstIRcode, HIGH);
IrReceiver.stop();
digitalWrite(feedBackLED, LOW);
} //END of if (IRflag == 2 && IrReceiver.decode())
} //END of checkIR()
//********************************************^************************************************
// c h e c k S t a t e M a c h i n e ( )
void checkStateMachine()
{
//check machine state and run animations accordingly
switch (mode)
{
//***************************
case 0:
for (int count = 0; count < NUM_PIXELS; count++)
{
//All LEDs white (0x7F7F7F draws ~700mA for 31 LEDs ~22mA per pixel X 5 = 3.5A)
//All LEDs white (0x5F5F5F draws ~520mA for 31 LEDs ~16mA per pixel X 5 = 2.6A)
strip.setPixelColor(count, 0x5F5F5F);
}
strip.show();
checkIRinterrupt();
break;
//END case stateStart:
//***************************
case 1:
//All LEDs frozen draws ~520mA for 31 LEDs ~16mA per pixel X 5 = 2.6A)
rainbowCycle(rainbowSpeed, 1);
checkIRinterrupt();
break;
//END case stateRainbow:
//***************************
case 2:
rainbow(rainbowDelay);
if (IRflag)
{
break;
}
break;
//END case stateZylonRed:
//***************************
case 3:
//All LEDs frozen draws ~350mA for 31 LEDs ~11mA per pixel X 5 = 1.75A)
XMAS(300);
checkIRinterrupt();
break;
//END case stateRainbow:
//***************************
case 4:
twoSides(0x0000FF); //Blue
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0x7F7F00); //Yellow
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0x7F0010); //Pink
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0x00FF00); //Green
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0x5F5F5F); //White
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0xFF0000); //Red
checkIRinterrupt();
if (IRflag)
{
break;
}
twoSides(0x7F007F); //Purple
checkIRinterrupt();
break;
//END case color twoSides:
//***************************
case 5:
middleOut(0x0000FF); //Blue
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0x5F5F5F); //White
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0x7F0010); //Pink
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0x00FF00); //Green
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0xFF0000); //Red
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0x7F7F00); //Yellow
checkIRinterrupt();
if (IRflag)
{
break;
}
middleOut(0x7F007F); //Purple
checkIRinterrupt();
break;
//END case color middleOut
//***************************
case 6:
knightRider(Cycles, KnightRiderSpeed, Width, 0x0000FF); // Cycles, Speed, Width, RGB Colour (blue)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0x5F5F5F); // Cycles, Speed, Width, RGB Colour (white)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0x00FF00); // Cycles, Speed, Width, RGB Colour (green)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0x7F0010); // Cycles, Speed, Width, RGB Colour (Pink)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0xFFFF00); // Cycles, Speed, Width, RGB Colour (yellow)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0xFF0000); // Cycles, Speed, Width, RGB Colour (red)
checkIRinterrupt();
if (IRflag)
{
break;
}
knightRider(Cycles, KnightRiderSpeed, Width, 0xFF00FF); // Cycles, Speed, Width, RGB Colour (purple)
checkIRinterrupt();
break;
//END case Blue twoSides:
//***************************
case 7:
theaterChase(strip.Color(0x5F, 0x5F, 0x5F), theaterChaseDelay); // White
checkIRinterrupt();
if (IRflag)
{
break;
}
theaterChase(strip.Color(0x7F, 0, 0), theaterChaseDelay); // Red
checkIRinterrupt();
if (IRflag)
{
break;
}
theaterChase(strip.Color( 0, 0, 0x7F), theaterChaseDelay); // Blue
checkIRinterrupt();
if (IRflag)
{
break;
}
theaterChase(strip.Color( 0xFF, 0xFF, 0), theaterChaseDelay); // Yellow
checkIRinterrupt();
if (IRflag)
{
break;
}
theaterChase(strip.Color( 0x7F, 0, 0x10), theaterChaseDelay); // Pink
checkIRinterrupt();
if (IRflag)
{
break;
}
theaterChase(strip.Color( 0, 0x7F, 0), theaterChaseDelay); // Green
checkIRinterrupt();
break;
//END case theaterChase:
//***************************
case 8:
theaterChaseRainbow(theaterChaseDelay);
checkIRinterrupt();
break;
//END case theaterChaseRainbow:
//***************************
case 9:
colorWipe(strip.Color(0xFF, 0, 0), colorWipeDelay); //Red
colorWipe(strip.Color(0x5F, 0x5F, 0x5F), colorWipeDelay); //White
colorWipe(strip.Color(0x7F, 0, 0x10), colorWipeDelay); //Pink
colorWipe(strip.Color(0, 0xFF, 0), colorWipeDelay); //Green
colorWipe(strip.Color(0, 0, 0xFF), colorWipeDelay); //Blue
colorWipe(strip.Color(0x7F, 0x7F, 0), colorWipeDelay); //Yellow
colorWipe(strip.Color(0x7F, 0, 0x7F), colorWipeDelay); //Magenta
checkIRinterrupt();
break;
//END case stateKnightRiderBlue:
//***************************
case 10:
knightRider(Cycles, KnightRiderSpeed, Width, 0x0000FF); //Cycles, Speed, Width, RGB Color (Blue)
checkIRinterrupt();
break;
//END case case knightRider Blue:
//***************************
case 11:
ZylonRed();
if (pos - 1 < 0)
{
checkIRinterrupt();
if (IRflag)
{
break;
}
}
break;
//END case state ZylonRed:
//***************************
case 12:
clearStrip();
checkIRinterrupt();
break;
//END case LEDs OFF:
} //END of switch(mode)
} //END of checkStateMachine()
//********************************************^************************************************
// k n i g h t R i d e r ( )
// Cycles - one cycle is scanning through all pixels left then right (or right then left)
// Speed - how fast one cycle is (32 with 16 pixels is default KnightRider speed)
// Width - how wide the trail effect is on the fading out LEDs. The original display used
// light bulbs, so they have a persistence when turning off. This creates a trail.
// Effective range is 2 - 8, 4 is default for 16 pixels. Play with this.
// Colour - 32-bit packed RGB colour value. All pixels will be this colour.
// knightRider(cycles, speed, width, colour);
void knightRider(uint16_t cycles, uint16_t speed, uint8_t width, uint32_t color)
{
uint32_t old_val[NUM_PIXELS]; // up to 256 lights!
for (unsigned int i = 0; i < cycles; i++)
{
for (int count = 1; count < NUM_PIXELS; count++)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(count, color);
old_val[count] = color;
for (int x = count; x > 0; x--)
{
old_val[x - 1] = dimColor(old_val[x - 1], width);
strip.setPixelColor(x - 1, old_val[x - 1]);
}
strip.show();
delay(speed);
}
for (int count = NUM_PIXELS - 1; count >= 0; count--)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(count, color);
old_val[count] = color;
for (int x = count; x <= NUM_PIXELS ; x++)
{
old_val[x - 1] = dimColor(old_val[x - 1], width);
strip.setPixelColor(x + 1, old_val[x + 1]);
}
strip.show();
delay(speed);
}
}
} //END of knightRider()
//********************************************^************************************************
// c l e a r S t r i p ( )
void clearStrip()
{
for ( int i = 0; i < NUM_PIXELS; i++)
{
strip.setPixelColor(i, 0x000000);
strip.show();
}
} //END of clearStrip()
//********************************************^************************************************
// d i m C o l o r ( )
uint32_t dimColor(uint32_t color, uint8_t width)
{
return (((color & 0xFF0000) / width) & 0xFF0000) + (((color & 0x00FF00) / width) & 0x00FF00) + (((color & 0x0000FF) / width) & 0x0000FF);
} //END of dimColor()
//********************************************^************************************************
// r a i n b o w C y c l e ( )
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait, unsigned int repeatNumber)
{
unsigned int i, j;
for (j = 0; j <= 255 * repeatNumber; j++)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
// repeatNumber of cycles of all colours on wheel
for (i = 0; i < strip.numPixels(); i++)
{
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
} //END of rainbowCycle()
//********************************************^************************************************
// W h e e l ( )
// Input a value 0 to 255 to get a colour value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos)
{
WheelPos = 255 - WheelPos;
if (WheelPos < 85)
{
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if (WheelPos < 170)
{
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
} //END of Wheel()
//********************************************^************************************************
// t h e a t e r C h a s e ( )
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait)
{
for (int j = 0; j < 10; j++) //do 10 cycles of chasing
{
checkIRinterrupt();
if (IRflag)
{
return;
}
for (int q = 0; q < 3; q++)
{
for (int i = 0; i < strip.numPixels(); i = i + 3)
{
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i = 0; i < strip.numPixels(); i = i + 3)
{
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
} //END of theaterChase()
//********************************************^************************************************
// t h e a t e r C h a s e R a i n b o w ( )
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait)
{
for (int j = 0; j < 256; j = j + 10) // cycle all 256 colours in the wheel
{
checkIRinterrupt();
if (IRflag)
{
return;
}
for (int q = 0; q < 3; q++)
{
for (int i = 0; i < strip.numPixels(); i = i + 3)
{
strip.setPixelColor(i + q, Wheel( (i + j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i = 0; i < strip.numPixels(); i = i + 3)
{
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
} //END of theaterChaseRainbow()
//********************************************^************************************************
// r a i n b o w ( )
void rainbow(uint8_t wait)
{
uint16_t i, j;
for (j = 0; j < 256; j++)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
for (i = 0; i < strip.numPixels(); i++)
{
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
delay(wait);
}
} //END of rainbow()
//********************************************^************************************************
// Z y l o n R e d ( )
void ZylonRed()
{
int j;
checkIRinterrupt();
if (IRflag)
{
return;
}
// Draw 5 pixels centered on pos. setPixelColor() will clip any
// pixels off the ends of the strip, we don't need to watch for that.
strip.setPixelColor(pos - 2, 0x100000); // Dark red
strip.setPixelColor(pos - 1, 0x400000); // Medium red
strip.setPixelColor(pos , 0xFF0000); // Center pixel is brightest was 0xFF0000
strip.setPixelColor(pos + 1, 0x400000); // Medium red
strip.setPixelColor(pos + 2, 0x100000); // Dark red
strip.show();
delay(zylonSpeed);
// Rather than being sneaky and erasing just the tail pixel,
// it's easier to erase it all and draw a new one next time.
for (j = -2; j <= 2; j++) strip.setPixelColor(pos + j, 0);
// Bounce off ends of strip
pos += dir;
if (pos < 0)
{
pos = 1;
dir = -dir;
}
else if (pos >= strip.numPixels())
{
pos = strip.numPixels() - 2;
dir = -dir;
}
} //END of ZylonRed()
//********************************************^************************************************
// Z y l o n B l u e ( )
void ZylonBlue()
{
int j;
// Draw 5 pixels centered on pos. setPixelColor() will clip any
// pixels off the ends of the strip, we don't need to watch for that.
strip.setPixelColor(pos - 2, 0x000010); // Dark blue
strip.setPixelColor(pos - 1, 0x000040); // Medium blue
strip.setPixelColor(pos , 0x0000FF); // Center pixel is brightest was 0xFF0000
strip.setPixelColor(pos + 1, 0x000040); // Medium blue
strip.setPixelColor(pos + 2, 0x000010); // Dark blue
strip.show();
delay(zylonSpeed);
// Rather than being sneaky and erasing just the tail pixel,
// it's easier to erase it all and draw a new one next time.
for (j = -2; j <= 2; j++) strip.setPixelColor(pos + j, 0);
//Bounce off ends of strip
pos += dir;
if (pos < 0)
{
pos = 1;
dir = -dir;
}
else if (pos >= strip.numPixels())
{
pos = strip.numPixels() - 2;
dir = -dir;
}
} //END of ZylonBlue()
//********************************************^************************************************
// t w o S i d e s ( )
void twoSides(uint32_t color)
{
for (uint16_t i = 0; i <= (strip.numPixels() / 2); i++) // fill strip from sides to middle
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(i, color);
strip.setPixelColor(strip.numPixels() - i, color);
strip.show();
delay(inOutOutInDelay);
}
for (uint16_t i = 0; i <= (strip.numPixels() / 2); i++) // reverse
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(strip.numPixels() / 2 + i, strip.Color(0, 0, 0));
strip.setPixelColor(strip.numPixels() / 2 - i, strip.Color(0, 0, 0));
strip.show();
delay(inOutOutInDelay);
}
} //END of twoSides()
//********************************************^************************************************
// m i d d l e O u t ( )
void middleOut(uint32_t color)
{
for (uint16_t i = 0; i <= (strip.numPixels() / 2); i++) // start from the middle, lighting an LED on each side
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(strip.numPixels() / 2 + i, color);
strip.setPixelColor(strip.numPixels() / 2 - i, color);
strip.show();
delay(inOutOutInDelay);
}
for (uint16_t i = 0; i < (strip.numPixels() / 2); i++) // reverse
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(i, strip.Color(0, 0, 0));
strip.setPixelColor(strip.numPixels() - i, strip.Color(0, 0, 0));
strip.show();
delay(inOutOutInDelay);
}
} //END of middleOut()
//********************************************^************************************************
// c o l o r W i p e ( )
//Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait)
{
for (uint16_t i = 0; i < strip.numPixels(); i++)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
} //END of colorWipe()
//********************************************^************************************************
// X M A S ( )
//https://github.com/smartynov/iotfun/blob/instructables/arduino/deco_lights/deco_lights.ino
//http://www.instructables.com/id/USB-NeoPixel-Deco-Lights-via-Digispark-ATtiny85/
void XMAS(int num)
{
for (int x = 0; x < num; x++)
{
checkIRinterrupt();
if (IRflag)
{
return;
}
// use real time to recalculate position of each colour spot
long ms = millis();
// scale time to float value
float m = offset + (float)ms / DELAY;
// add some non-linearity
m = m - 42.5 * cos(m / 552.0) - 6.5 * cos(m / 142.0);
// recalculate position of each spot (measured on a scale of 0 to 1)
float posr = 0.5 + 0.55 * sin(m * spdr);
float posg = 0.5 + 0.55 * sin(m * spdg);
float posb = 0.5 + 0.55 * sin(m * spdb);
// now iterate over each pixel and calculate it's colour
for (int i = 0; i < NUM_PIXELS; i++)
{
// pixel position on a scale from 0.0 to 1.0
float ppos = (float)i / NUM_PIXELS;
// distance from this pixel to the center of each color spot
float dr = ppos - posr;
float dg = ppos - posg;
float db = ppos - posb;
// set each colour component from 0 to max BRIGHTNESS, according to Gaussian distribution
strip.setPixelColor(
i, constrain(BRIGHTNESS * myexp(-FOCUS * dr * dr), 0, BRIGHTNESS),
constrain(BRIGHTNESS * myexp(-FOCUS * dg * dg), 0, BRIGHTNESS),
constrain(BRIGHTNESS * myexp(-FOCUS * db * db), 0, BRIGHTNESS)
);
}
//send data to LED strip
strip.show();
}
} //END of XMAS()
//********************************************^************************************************
// m y e x p ( )
// the real exponent function is too slow, so I created an approximation (only for x < 0)
float myexp(float x) // 1.12
{
return (1.0 / (1.0 - (0.634 - 1.344 * x) * x));
} //END of myexp()
//********************************************^************************************************
// E N D O F P R O G R A M
//********************************************^************************************************