📬❄️📦Adafruit Holiday Shipping Deadlines 2019: Please place all UPS 3 Day orders by 11am ET Friday December 13 📬❄️📦
0

XRAD'S HOLIDAY TREE TOPPER
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

XRAD'S HOLIDAY TREE TOPPER

by XRAD on Sun Nov 24, 2019 3:07 pm

Wanted to make something for the holidays so made a tree topper. Uses 11 neopixel jewels in a string mounted to a 3D printed start. The jewels are perfect for lighting up the star arms reflecting and diffusing through each facet. I frankencoded a bunch of fastLED code to get the effects I wanted. The coolest one is the cylon style display as each arm lights up with cool effects.

The neopixel jewels are wired with the first in line at the start apex and then spiral down clockwise through the next two rows of ten arms. I was going to use a PIR sensor, but mine pooped out and was giving me garbage reads, so went with random.

There were a few stumbling blocks like using randomseed and how it effected the flow of the code, but I have it completed and working.

I used 10 of my favorite fastLed displays and modified some and created some. Looks really great. Will post vid when on top o the tree. The links are in the code header for the 3D print.

Code: Select all | TOGGLE FULL SIZE
/////////////////////XRAD'S FRANKENCODE HOLIDAY STAR//////////////////////////////////////
//   I built a tree topper for my girl. She loves the holidays and neopixels!
//   It's a mix of 10 main fastLED display patterns, some with multiple
//   sub-patterns.  These are found all over the net. I added some of my own
//   and of course some 'random' display action.  Print a 3D design from
//   https://www.thingiverse.com/thing:3240708 at 150%. 11 neopixel jewels
//   https://www.adafruit.com/product/2226 and https://www.adafruit.com/product/2756
//////////////////////////////////////////////////////////////////////////////////////////




#include "FastLED.h" //Requires FastLED 3.1 or later; check github for latest code

#define DATA_PIN    7
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS    77
#define BRIGHTNESS  200

#define FRAMES_PER_SECOND  120

CRGB leds[NUM_LEDS];



//lightning variables
uint8_t frequency = 50;     // interval between strikes
uint8_t flashes = 8;        //the upper limit of flashes per strike
unsigned int dimmer = 1;

uint8_t ledstart;
uint8_t ledlen;

//violetSparkles
#define DELAYVAL  50
uint32_t now = 0;
uint8_t  mode = 0;


//crossFadeStar
#define UPDATES_PER_SECOND 100
CRGBPalette16 currentPalette( CRGB::Black);
CRGBPalette16 targetPalette( PartyColors_p );



void setup() {
  Serial.begin(9600);
  delay(3000);
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  randomSeed(analogRead(20));//SAMD21 pin 20
}



// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti,
                                sinelon, bpm, juggle, lightning,
                                violetSparkles, cylonStar, crossFadeStar
                              };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns



void loop() {
  // Call the current pattern function once, updating the 'leds' array
  gPatterns[gCurrentPatternNumber]();

  // send the 'leds' array out to the actual LED strip
  FastLED.show();
  // insert a delay to keep the framerate modest
  FastLED.delay(1000 / FRAMES_PER_SECOND);

  // do some periodic updates
  EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
  EVERY_N_SECONDS( 60 ) { nextPattern(); } // change patterns periodically
}




#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern() {
  //have to put 'random' here or it log jams the code
  unsigned long randomSelection = random(0, 10);//let's get some random display action!
  delay(10);
  Serial.println(randomSelection);

  //randomSelection is the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (randomSelection) % ARRAY_SIZE( gPatterns);
}


void rainbow() {
  // FastLED's built-in rainbow generator
  fill_rainbow( leds, NUM_LEDS, gHue, 7);
}


void rainbowWithGlitter() {
  // built-in FastLED rainbow, plus some random sparkly glitter
  rainbow();
  addGlitter(80);
}


void addGlitter( fract8 chanceOfGlitter) {
  if ( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}


void confetti() {
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}


void sinelon() {
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16(13, 0, NUM_LEDS);
  leds[pos] += CHSV( gHue, 255, 192);
}

void bpm()
{
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

void juggle() {
  // eight colored dots, weaving in and out of sync with each other
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16(i+7,0,NUM_LEDS)] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}


void lightning() {
  ledstart = random8(NUM_LEDS);       // random8 fastLED semi random selection
  ledlen = random8(NUM_LEDS - ledstart);

  for (int flashCounter = 0; flashCounter < random8(3, flashes); flashCounter++) {
    if (flashCounter == 0) dimmer = 5;                        // the brightness of the leader is scaled down by a factor of 5
    else dimmer = random8(1, 3);                              // return strokes are brighter than the leader

    fill_solid(leds + ledstart, ledlen, CHSV(255, 0, 255 / dimmer));
    FastLED.show();
    delay(random8(4, 10));
    fill_solid(leds + ledstart, ledlen, CHSV(255, 0, 0));     // Clear the section of LED's
    FastLED.show();

    if (flashCounter == 0) delay (150);                       // longer delay until next flash after the leader

    delay(50 + random8(100));                                 // shorter delay between strokes
  }
  delay(random8(frequency) * 10);                             // delay between strikes, 100 medium, 10 faster
}


void violetSparkles() {
  renderFire(now, leds, NUM_LEDS);
  addGlitter(100);
  FastLED.show();
  delay(DELAYVAL);
  now++;
}

void renderFire(uint32_t now, CRGB* leds, int numpixels) {
  // Set color from Hue, Saturation, and Value.
  CRGB violet( 240, 0, 240);
  CRGB red( 240, 0,  0);
  for (int i = 0; i < NUM_LEDS; i++) {
    CRGB dest = blend(  violet, red, inoise8((now  * 17), i * 60));
    leds[i] = dest;
  }
  fadeToBlackBy(leds, numpixels, map(inoise8(now * 17), 0, 240, 240, 150));
}


//cylon Star
void fadeall() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i].nscale8(250);
  }
}

void cylonStar() {
  static uint8_t hue = 0;
  Serial.print("x");
  // First slide the led in one direction
  for (int i = 0; i < NUM_LEDS; i++) {
    // Set the i'th led to red
    leds[i] = CHSV(hue++, 255, 255);
    // Show the leds
    FastLED.show();
    // now that we've shown the leds, reset the i'th led to black
    // leds[i] = CRGB::Black;
    fadeall();
    // Wait a little bit before we loop around and do it again
    delay(10);
  }
  Serial.print("x");

  // Now go in the other direction.
  for (int i = (NUM_LEDS) - 1; i >= 0; i--) {
    // Set the i'th led to red
    leds[i] = CHSV(hue++, 255, 255);
    // Show the leds
    FastLED.show();
    // now that we've shown the leds, reset the i'th led to black
    // leds[i] = CRGB::Black;
    fadeall();
    // Wait a little bit before we loop around and do it again
    delay(10);
  }
}



//crossFadeStar
void crossFadeStar() {
  ChangePalettePeriodically();
  // Crossfade current palette slowly toward the target palette
  // Each time that nblendPaletteTowardPalette is called, small changes
  // are made to currentPalette to bring it closer to matching targetPalette.
  // You can control how many changes are made in each call:
  //   - the default of 24 is a good balance
  //   - meaningful values are 1-48.  1=veeeeeeeery slow, 48=quickest
  //   - "0" means do not change the currentPalette at all; freeze
  uint8_t maxChanges = 24;
  nblendPaletteTowardPalette( currentPalette, targetPalette, maxChanges);
  static uint8_t startIndex = 0;
  startIndex = startIndex + 1; /* motion speed */
  FillLEDsFromPaletteColors( startIndex);
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
}

void FillLEDsFromPaletteColors( uint8_t colorIndex) {
  uint8_t brightness = 255;
  for ( int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette( currentPalette, colorIndex + sin8(i * 16), brightness);
    colorIndex += 3;
  }
}

void ChangePalettePeriodically() {
  uint8_t secondHand = (millis() / 1000) % 60;
  static uint8_t lastSecond = 99;
  if ( lastSecond != secondHand) {
    lastSecond = secondHand;
    CRGB p = CHSV( HUE_PURPLE, 255, 255);
    CRGB g = CHSV( HUE_GREEN, 255, 255);
    CRGB b = CRGB::Black;
    CRGB w = CRGB::White;
    if ( secondHand ==  0)  {
      targetPalette = RainbowColors_p;
    }
    if ( secondHand == 10)  {
      targetPalette = CRGBPalette16( g, g, b, b, p, p, b, b, g, g, b, b, p, p, b, b);
    }
    if ( secondHand == 20)  {
      targetPalette = CRGBPalette16( b, b, b, w, b, b, b, w, b, b, b, w, b, b, b, w);
    }
    if ( secondHand == 30)  {
      targetPalette = LavaColors_p;
    }
    if ( secondHand == 40)  {
      targetPalette = CloudColors_p;
    }
    if ( secondHand == 50)  {
      targetPalette = PartyColors_p;
    }
  }
}
//end :) !!



IMG_3611.JPG
IMG_3611.JPG (161.01 KiB) Viewed 77 times


IMG_3612.JPG
IMG_3612.JPG (175.39 KiB) Viewed 77 times


IMG_3613.JPG
IMG_3613.JPG (158.34 KiB) Viewed 77 times

XRAD
 
Posts: 567
Joined: Sat Nov 19, 2016 3:28 pm

Re: XRAD'S HOLIDAY TREE TOPPER

by XRAD on Sun Nov 24, 2019 3:58 pm

I printed a post hole base, which houses the electronics and power connector. This makes mounting easy.

IMG_3617.JPG
IMG_3617.JPG (179.89 KiB) Viewed 70 times



of 16777216 possible colors, these are the least spectacular......
IMG_3614.JPG
IMG_3614.JPG (107.79 KiB) Viewed 70 times

XRAD
 
Posts: 567
Joined: Sat Nov 19, 2016 3:28 pm

Re: XRAD'S HOLIDAY TREE TOPPER

by XRAD on Mon Nov 25, 2019 10:47 am

of course you could leave out some of the code such as :
Code: Select all | TOGGLE FULL SIZE
//any of the Serial.prints


and wrap around is unnecessary as the limits of the array element choices are also set by two arguments in random(x,y)
Code: Select all | TOGGLE FULL SIZE
//#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern() {
  //have to put 'random' here or it log jams the code
  long randomSelection = random(0, 10);//let's get some random display action! don't really need 'unsigned'
  delay(10);
  Serial.println(randomSelection);

  //randomSelection is the current pattern number
  gCurrentPatternNumber = (randomSelection);
}



but works fine as is.....

XRAD
 
Posts: 567
Joined: Sat Nov 19, 2016 3:28 pm

Re: XRAD'S HOLIDAY TREE TOPPER

by Disciple on Tue Nov 26, 2019 2:41 am

Nice effect. Thanks for showing. Looks perfect for a manger scene.

Hallelujah!
Disciple

Disciple
 
Posts: 707
Joined: Tue Jan 06, 2015 8:13 pm

Please be positive and constructive with your questions and comments.