Project in progress
Snowy Tree

Snowy Tree © GPL3+

Simulated snow on a line of LEDs wrapped around a tree

  • 725 views
  • 0 comments
  • 0 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
WS2811 Addressable LED String
×1
Power Supply 5V 10A
×1
Capacitor 1000 uF
Across the power supply to protect Arduino from spike when turned on
×1

Apps and online services

About this project

This year we put Arduino Uno-powered lights on our tree, and my crowning achievement (so far!) is to make it snow on the tree!

This display creates 'flakes' at the top of the tree, simulates them fluttering down, and piling up in snowdrifts at the bottom. The bottom row slowly melts, making way for more snow!

The hard part was figuring out the ratio of lights in each ring. To do that, I used a temporary bit of code that let me move a 'cursor' around the tree, lighting up one LED at a time. It used Serial.read() so I could use the '-' and '=' keys to move it around, and printed out the current light number so I could record it. This let me create a 'vertical' line up one side of the tree, starting at light 0.

Using those ratios, every time a snowflake needs to 'drop' down, I know how many lights to move it to give the illusion of vertical movement!

Code

doSnow()C/C++
This is the core function that handles the falling snow. Sorry, it's not cleaned up, but hopefully it's documented enough for others to use.
/**********************************************
*
* doSnow()
*    Let it snow, let it snow!
*
**********************************************/
int lightSpot = 0;
// ringCutoffs is the critical part. Starting with light 0, you need to identify the lights above
// one another in a straight line. If you light these all, you get a vertical line up one side of
// the tree. You need to use the Serial.available if statement at the start to experimentally
// figure out which light numbers those are and adjust the length and contents of the array
// to match YOUR tree. 
// Controls: Send '=' to the serial port to increment to the next light, '-' to go back a light
// You'll need to change your NUM_OF_RINGS to match the number of times your lights go around the tree.
#define NUM_RINGS 11
// Note that the first ring needs to start at 0!
int ringCutoffs[NUM_RINGS] = {0, 54, 95, 135, 167, 196, 218, 240, 262, 281, 295 };
int ringRatios[NUM_RINGS - 1];
char cBuff;
int snowTemp;
float snowPct;
#define SNOW_PWR 50
#define SNOW_MELT_HEAT 1
#define SNOW_MELT_HOW_MANY_RINGS_HIGH 3
#define SNOW_FULL_CUTOFF 230
#define NUM_FLAKES_TO_FALL_PER_FRAME 4
void doSnow() {
	// STEP 1 - SET UP YOUR TREE
	// THIS IS THE PART YOU USE THE FIRST TIME TO SET UP YOUR TREE
	// Uncommend this block (and comment the rest). Use '=' and '-' to
	// move your 'cursor' light around and experimentally get the 
	// values you need to put in the ringCutoffs array.
	//if (Serial.available() > 0) {
	//	cBuff = Serial.read();
	//	switch (cBuff) {
	//		case '=':
	//			lightSpot++;
	//			break;
	//		case '-':
	//			lightSpot--;
	//			break;
	//	}
	//	Serial.println(lightSpot);
	//}
	//fill_solid(leds, NUM_LEDS, CRGB::Black);
	//leds[lightSpot] = CRGB::White;
	//FastLED.delay(DELAY_TIME_FAST);

	// STEP 2 - DOUBLE-CHECK
	// When you're done filling in ringCutoffs, uncomment this block and check your line. See if it goes 
	// fairly straight up the tree.
	//fill_solid(leds, NUM_LEDS, CRGB::Black);
	//for (ringCounter = 0; ringCounter < NUM_OF_RINGS; ringCounter++) {
	//	leds[ringCutoffs[i]] = CRGB::White;
	//}
	//FastLED.delay(DELAY_TIME_FAST);

	// STEP 3 - ENJOY THE SNOW
	// Uncomment this code block and it will do the rest!
	// Algorithm:
	// Generate a random flake
	// Start at the bottom ring, and float them all down

	for (ring = 0; ring < NUM_RINGS - 1; ring++) {
		for (led = ringCutoffs[ring]; led < ringCutoffs[ring + 1]; led++) {
			if (snowFlakes[led] > 0) {
				if (ring > 0) {
					snowTemp = ((float)((float)(led - ringCutoffs[ring]) / (float)(ringCutoffs[ring + 1] - ringCutoffs[ring])) *
						(ringCutoffs[ring] - ringCutoffs[ring - 1])) +
						ringCutoffs[ring - 1] +
						(random8(3) - 1);
					if (snowFlakes[snowTemp] < SNOW_FULL_CUTOFF) {
						//Serial.print("Going from ");
						//Serial.print(led);
						//Serial.print(" to ");
						//Serial.println(snowTemp);
						snowFlakes[snowTemp] = max (snowFlakes[led], min (250, snowFlakes[snowTemp] + SNOW_PWR));
						snowFlakes[led] = 0;
					}
					//Serial.print("ON: ");
				}
			}
			//Serial.println(led);
		}
	}
	for (led = ringCutoffs[NUM_RINGS - 1]; led < NUM_LEDS; led++) {
		if (snowFlakes[led] > 0) {
			snowTemp = ((float)((float)(led - ringCutoffs[NUM_RINGS - 1]) / (float)(NUM_LEDS - ringCutoffs[NUM_RINGS - 1])) * 
				(ringCutoffs[NUM_RINGS - 1] - ringCutoffs[NUM_RINGS - 2])) + 
				ringCutoffs[NUM_RINGS - 2] + 
				(random8(3) - 1);
			//Serial.println(led - ringCutoffs[NUM_RINGS - 1]);
			//Serial.println(NUM_LEDS - ringCutoffs[NUM_RINGS - 1]);
			//Serial.println((float)((float)(led - ringCutoffs[NUM_RINGS - 1]) / (float)(NUM_LEDS - ringCutoffs[NUM_RINGS - 1])));
			//Serial.println(ringCutoffs[NUM_RINGS - 1] - ringCutoffs[NUM_RINGS - 2]);
			//Serial.println((float)((float)(led - ringCutoffs[NUM_RINGS - 1]) / (float)(NUM_LEDS - ringCutoffs[NUM_RINGS - 1])) * (ringCutoffs[NUM_RINGS - 1] - ringCutoffs[NUM_RINGS - 2]));
			//Serial.println(((float)((float)(led - ringCutoffs[NUM_RINGS - 1]) / (float)(NUM_LEDS - ringCutoffs[NUM_RINGS - 1])) * (ringCutoffs[NUM_RINGS - 1] - ringCutoffs[NUM_RINGS - 2])) + ringCutoffs[NUM_RINGS - 2]);
			if (snowFlakes[snowTemp] < SNOW_FULL_CUTOFF) {
				//Serial.print("Going from ");
				//Serial.print(led);
				//Serial.print(" to ");
				//Serial.println(snowTemp);
				snowFlakes[snowTemp] = min(250, snowFlakes[snowTemp] + SNOW_PWR);
				snowFlakes[led] = 0;
			}
			//Serial.print("ON: ");
		}
			//Serial.println(led);
	}
	for (led = 0; led < ringCutoffs[SNOW_MELT_HOW_MANY_RINGS_HIGH]; led++) {
		snowFlakes[led] = max(0, snowFlakes[led] - SNOW_MELT_HEAT);
	}
	for (snowTemp = 0; snowTemp < NUM_FLAKES_TO_FALL_PER_FRAME; snowTemp++) {
		led = ringCutoffs[NUM_RINGS - 1] + (random8((NUM_LEDS - ringCutoffs[NUM_RINGS - 1])));
		snowFlakes[led] = min (250, snowFlakes[led] + SNOW_PWR);
	}
	//fill_solid(leds, NUM_LEDS, CRGB::Black);
	for (led = 0; led < NUM_LEDS; led++) {
		//if (snowFlakes[led] > 0) {
			leds[led] = CRGB(snowFlakes[led], snowFlakes[led], snowFlakes[led]);
		//}
	}
	leds[0] = CRGB::Yellow;
	FastLED.delay(150);
}

Schematics

Basic Uno / LED String setup with capacitor for spike protection
Simple setup for LED Strings
xmaslights_H5oEYQFQh7.fzz
LED String w/ capacitor for spike protection - image
Xmaslights bb 1kpnr32wt8

Comments

Similar projects you might like

IOT Lighted Xmas Tree

Project tutorial by Sameer

  • 748 views
  • 0 comments
  • 2 respects

Alexa-Powered Christmas Tree

Project tutorial by Kay Lerch

  • 12,821 views
  • 6 comments
  • 25 respects

Wooden LED Christmas Tree

Project showcase by Arduino_Jarod

  • 940 views
  • 0 comments
  • 7 respects

Home Automation Using Raspberry Pi 2 And Windows 10 IoT

Project tutorial by Anurag S. Vasanwala

  • 297,267 views
  • 98 comments
  • 706 respects

MKR1000 RGB Strip Controller

Project showcase by Keith Mitchell

  • 6,751 views
  • 0 comments
  • 10 respects

Non Optical Solar Tracker (East Tower 2.4KW)

Project in progress by Team Trouble

  • 4,593 views
  • 8 comments
  • 29 respects
Add projectSign up / Login