GameMaker
Published © GPL3+

Snowy Tree

Simulated snow on a line of LEDs wrapped around a tree

AdvancedWork in progress2,218
Snowy Tree

Things used in this project

Hardware components

Arduino UNO
Arduino 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

Software apps and online services

Visual Studio 2015
Microsoft Visual Studio 2015

Story

Read more

Schematics

Basic Uno / LED String setup with capacitor for spike protection

Simple setup for LED Strings

LED String w/ capacitor for spike protection - image

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);
}

Credits

GameMaker

GameMaker

0 projects • 0 followers

Comments