#include <FastLED.h>
#include "shapes.h"
////////////////////////////////////////////////////////////
// Pin and LED definitions
////////////////////////////////////////////////////////////
#define LED_PIN_STRIP_1 4
#define LED_PIN_STRIP_2 5
#define LED_PIN_STRIP_3 6
#define LED_PIN_STRIP_4 7
#define LED_PIN_STRIP_5 8
#define LED_PIN_STRIP_6 9
#define LED_PIN_STRIP_7 10
#define LED_PIN_BLOW_RING_1 11
#define LED_PIN_BLOW_RING_2 12
#define LED_PIN_TAIL_1 2
#define LED_PIN_TAIL_2 3
#define SMALL_RING_LEDS 28
#define BASE_RING_LEDS 42
#define LARGE_RING_LEDS 52
#define LED_GROUPS 7
#define LEDS_IN_TAIL 50
#define LEDS_IN_BLOW_RING 16
const uint16_t ledsInGroup = SMALL_RING_LEDS + BASE_RING_LEDS + LARGE_RING_LEDS;
CRGB leds_1[ledsInGroup];
CRGB leds_2[ledsInGroup];
CRGB leds_3[ledsInGroup];
CRGB leds_4[ledsInGroup];
CRGB leds_5[ledsInGroup];
CRGB leds_6[ledsInGroup];
CRGB leds_7[ledsInGroup];
CRGB leds_blow_ring_1[LEDS_IN_BLOW_RING];
CRGB leds_blow_ring_2[LEDS_IN_BLOW_RING];
CRGB leds_tail_1[LEDS_IN_TAIL];
CRGB leds_tail_2[LEDS_IN_TAIL];
CRGB* ledGroups[LED_GROUPS] = { leds_1, leds_2, leds_3, leds_4, leds_5, leds_6, leds_7 };
////////////////////////////////////////////////////////////
// COPY FROM HERE
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Global Animation State
////////////////////////////////////////////////////////////
// Wind
unsigned long lastRPMTime = 0; // Time when RPM was last calculated
const unsigned long rpmInterval = 50; // RPM calculation interval in milliseconds
volatile unsigned long revolutions_1 = 0; // Volatile because it's modified in an ISR
unsigned int rpm_1 = 0;
volatile unsigned long revolutions_2 = 0; // Volatile because it's modified in an ISR
unsigned int rpm_2 = 0;
const Animation* currentAnimSet = nullptr;
uint8_t currentAnimIndex = 0;
uint8_t currentFrame = 0;
uint8_t nextFrame = 0;
uint8_t framesInCurrentAnim = 0;
uint8_t partlyLeftAnimIndex = 0;
uint8_t partlyRightAnimIndex = 0;
uint8_t activeAnimIndex = 0;
uint16_t totalRuns = 0;
uint8_t randomRuns = 3;
bool updateSet = true;
static int currentMode = -1;
static int desiredMode = -1;
static uint16_t sensor1 = 0, sensor2 = 0;
uint8_t morphStep = 0;
bool morphInProgress = false;
float currentGlobalAlpha = 0;
static uint16_t morphID = 0;
bool reverseAnimation = false;
const Animation* transitionAnimFrom = nullptr;
const Animation* transitionAnimTo = nullptr;
uint8_t transitionMorphStep;
uint8_t setTransitionSteps = 15;
bool transitionInProgress = false;
uint8_t prevAnimIndex = 0;
uint8_t nextAnimIndex = 0;
// Delay
bool frameDelayActive = false;
uint32_t frameDelayStartTime = 0;
int8_t currentReverseDirection = 1;
bool animationCycleCompleted = false;
// Noise variables
uint16_t noiseX = 0;
const uint8_t NOISE_SCALE = 20;
const uint8_t NOISE_SPEED = 10;
const uint8_t NOISE_BLEND = 100;
const uint8_t DUAL_NOISE_BLEND = 255;
// Tail variables
static uint16_t tailOffset1 = 0;
static uint16_t tailOffset2 = 0;
const uint8_t TAIL_NOISE_SCALE = 35;
const uint8_t TAIL_NOISE_SPEED = 20;
// Blow ring variables
static uint16_t blowRingSineTime1 = 0;
static uint16_t blowRingSineTime2 = 0;
static float blowRingSineOffset1 = 0;
static float blowRingSineOffset2 = 0;
// Blow sensor variables
uint8_t maxBlowValue = 255;
uint16_t idleTime = 5000;
uint8_t accumulatedDifference1 = 0;
unsigned long lastPositiveReadingTime1 = 0;
uint8_t accumulatedDifference2 = 0;
unsigned long lastPositiveReadingTime2 = 0;
// Gradients
DEFINE_GRADIENT_PALETTE(NorthernLightsPalette) {
0, 0, 207, 82, // Colder colors
62, 3, 46, 62,
143, 25, 0, 50,
192, 0, 100, 144,
255, 0, 223, 150
};
DEFINE_GRADIENT_PALETTE(WarmNorthernLightsPalette) {
0, 255, 40, 5, // Warmer reds and oranges
62, 255, 60, 5,
143, 80, 20, 20,
180, 255, 100, 0,
200, 20, 10, 0,
255, 255, 100, 0
};
CRGBPalette16 northernLightsPal = NorthernLightsPalette;
CRGBPalette16 warmNorthernLightsPal = WarmNorthernLightsPalette;
////////////////////////////////////////////////////////////
// Get color from current frame
////////////////////////////////////////////////////////////
CRGB getColorInAnimationFrame(const Animation& anim, uint8_t frameIndex, uint8_t group, uint16_t led) {
if (frameIndex >= anim.numFrames) return CRGB::Black;
const Shape& shape = anim.frames[frameIndex];
for (uint8_t i = 0; i < shape.numSegments; i++) {
const LedSegment& seg = shape.segments[i];
if (seg.group == group && led >= seg.startIndex && led <= seg.endIndex) {
// Generate noise color
uint16_t noise = inoise8(led * NOISE_SCALE - noiseX + (group * ledsInGroup), 0);
CRGB brightNoiseColor = ColorFromPalette(northernLightsPal, noise);
CRGB warmNoiseColor = ColorFromPalette(warmNorthernLightsPal, noise);
CRGB noiseColor = blend(brightNoiseColor, warmNoiseColor, anim.paletteBlendAmount);
// Blend between base color and noise
return applyNoiseColor(seg.color, noiseColor, anim.blendType, anim.noiseBlendAmount);
}
}
return CRGB::Black;
}
////////////////////////////////////////////////////////////
// Choose blend type
////////////////////////////////////////////////////////////
CRGB applyNoiseColor(CRGB baseColor, CRGB noiseColor, uint8_t blendType, uint8_t blendAmount) {
if (blendType == 0) {
// Use the original blending method
return blend(baseColor, noiseColor, blendAmount);
} else if (blendType == 1) {
// Multiplicative Overlay
baseColor.r = (noiseColor.r < 128) ? (baseColor.r * noiseColor.r) / 128
: 255 - ((255 - baseColor.r) * (255 - noiseColor.r) / 128);
baseColor.g = (noiseColor.g < 128) ? (baseColor.g * noiseColor.g) / 128
: 255 - ((255 - baseColor.g) * (255 - noiseColor.g) / 128);
baseColor.b = (noiseColor.b < 128) ? (baseColor.b * noiseColor.b) / 128
: 255 - ((255 - baseColor.b) * (255 - noiseColor.b) / 128);
return baseColor;
} else if (blendType == 2) {
// Bitwise XOR Overlay
baseColor.r ^= noiseColor.r;
baseColor.g ^= noiseColor.g;
baseColor.b ^= noiseColor.b;
return baseColor;
} else if (blendType == 3) {
baseColor.r = (baseColor.r * (255 - (255 - noiseColor.r) / 2)) / 255;
baseColor.g = (baseColor.g * (255 - (255 - noiseColor.g) / 2)) / 255;
baseColor.b = (baseColor.b * (255 - (255 - noiseColor.b) / 2)) / 255;
return baseColor;
} else if (blendType == 4) {
return CRGB(noiseColor.g, baseColor.r, noiseColor.b);
}
return baseColor; // Default case (failsafe)
}
////////////////////////////////////////////////////////////
// Blends from frameFrom to frameTo by "alpha" (0.0-1.0)
////////////////////////////////////////////////////////////
void blendAnimationFrames(const Animation& anim, uint8_t frameFrom, uint8_t frameTo) {
switch (currentAnimSet[currentAnimIndex].transition) {
case 0:
// SMOOTH TRANSITION
for (uint8_t g = 0; g < LED_GROUPS; g++) {
for (uint16_t led = 0; led < ledsInGroup; led++) {
CRGB colorA = getColorInAnimationFrame(anim, frameFrom, g, led);
CRGB colorB = getColorInAnimationFrame(anim, frameTo, g, led);
uint8_t blendAmount = (uint8_t)(currentGlobalAlpha * 255);
CRGB crossFaded = blend(colorA, colorB, blendAmount);
ledGroups[g][led] = crossFaded;
}
}
break;
case 1:
// NOISE TRANSITION
const float maxShift = 0.75f;
for (uint8_t g = 0; g < LED_GROUPS; g++) {
for (uint16_t led = 0; led < ledsInGroup; led++) {
// Base colors
CRGB colorA = getColorInAnimationFrame(anim, frameFrom, g, led);
CRGB colorB = getColorInAnimationFrame(anim, frameTo, g, led);
uint16_t seed = (uint16_t)(g * 37 + led * 13 + morphID * 17);
uint8_t rnd = simpleHash8(seed);
float rnd01 = rnd / 255.0f;
float shift = rnd01 * maxShift;
float localTimeRaw = (currentGlobalAlpha - shift) / (1.0f - shift);
if (localTimeRaw < 0.0f) localTimeRaw = 0.0f;
if (localTimeRaw > 1.0f) localTimeRaw = 1.0f;
uint8_t blendAmount = (uint8_t)(localTimeRaw * 255);
ledGroups[g][led] = blend(colorA, colorB, blendAmount);
}
}
break;
}
}
////////////////////////////////////////////////////////////
// Keep current frame active (to not stop noise)
////////////////////////////////////////////////////////////
void playCurrentFrame() {
for (uint8_t g = 0; g < LED_GROUPS; g++) {
for (uint16_t led = 0; led < ledsInGroup; led++) {
ledGroups[g][led] = getColorInAnimationFrame(currentAnimSet[currentAnimIndex], currentFrame, g, led);
}
}
}
////////////////////////////////////////////////////////////
// Initializes a new morph process
////////////////////////////////////////////////////////////
void startMorph(const Animation& anim, uint8_t fromFrame, uint8_t toFrame) {
morphID++;
morphStep = 0;
morphInProgress = true;
currentGlobalAlpha = 0.0;
blendAnimationFrames(anim, fromFrame, toFrame);
}
////////////////////////////////////////////////////////////
// Call this frequently; does 1 step if it's time
////////////////////////////////////////////////////////////
void updateMorph() {
if (!morphInProgress) return;
morphStep++;
if (morphStep >= currentAnimSet[currentAnimIndex].steps) {
currentFrame = nextFrame;
morphInProgress = false;
} else {
const Animation& anim = currentAnimSet[currentAnimIndex];
currentGlobalAlpha = (float)morphStep / (float)currentAnimSet[currentAnimIndex].steps;
blendAnimationFrames(anim, currentFrame, nextFrame);
}
}
////////////////////////////////////////////////////////////
// Decide nextFrame based on reverseAnimation or not
////////////////////////////////////////////////////////////
uint8_t pickNextFrame(uint8_t current, uint8_t totalFrames, bool reverseFlag) {
if (!reverseFlag) {
animationCycleCompleted = (current + 1) >= totalFrames;
if (animationCycleCompleted) {
totalRuns++;
if (totalRuns % randomRuns == randomRuns - 1) {
randomRuns = random(1, 4);
Serial.println(randomRuns);
updateSet = true;
changeAnimationSet();
}
}
return (current + 1) % totalFrames;
}
int next = current + currentReverseDirection;
// Handle direction changes
if (next >= totalFrames) {
next = totalFrames - 2;
currentReverseDirection = -1;
animationCycleCompleted = false;
} else if (next <= 0) {
next = 1;
currentReverseDirection = 1;
animationCycleCompleted = true; // Full cycle completed
totalRuns++;
if (totalRuns % randomRuns == randomRuns - 1) {
randomRuns = random(1, 4);
Serial.println(randomRuns);
updateSet = true;
changeAnimationSet();
}
} else {
animationCycleCompleted = false;
}
return static_cast<uint8_t>(next);
}
////////////////////////////////////////////////////////////
// Crossfade between animation sets
////////////////////////////////////////////////////////////
void crossfadeAnimations(const Animation& animFrom, uint8_t frameFrom, const Animation& animTo, uint8_t frameTo) {
for (uint8_t g = 0; g < LED_GROUPS; g++) {
for (uint16_t led = 0; led < ledsInGroup; led++) {
CRGB colorA = getColorInAnimationFrame(animFrom, frameFrom, g, led);
CRGB colorB = getColorInAnimationFrame(animTo, frameTo, g, led);
uint8_t blendAmount = (uint8_t)(currentGlobalAlpha * 255);
ledGroups[g][led] = blend(colorA, colorB, blendAmount);
}
}
}
////////////////////////////////////////////////////////////
// Update transition between animetion sets
////////////////////////////////////////////////////////////
void updateTransition() {
if (!transitionInProgress) return;
transitionMorphStep++;
if (transitionMorphStep >= setTransitionSteps) {
// Transition is complete
transitionInProgress = false;
morphInProgress = false;
// Swap to the new set
currentAnimSet = transitionAnimTo;
currentAnimIndex = nextAnimIndex;
// Reset frames for the new animation
currentFrame = 0;
nextFrame = 0;
} else {
// Crossfading: alpha from 0..1
currentGlobalAlpha = (float)transitionMorphStep / setTransitionSteps;
// Pick the old/new animations by index
const Animation& animFrom = transitionAnimFrom[prevAnimIndex];
const Animation& animTo = transitionAnimTo[nextAnimIndex];
crossfadeAnimations(
animFrom,
currentFrame,
animTo,
0);
}
}
////////////////////////////////////////////////////////////
// Switch to idle, partly, or active if needed
////////////////////////////////////////////////////////////
void setAnimationSet(const Animation* newSet, uint8_t index) {
// If no change, do nothing
if (newSet == currentAnimSet && !updateSet) return;
currentReverseDirection = 1;
animationCycleCompleted = false;
// If there is no current animation set, assign immediately
if (currentAnimSet == nullptr) {
currentAnimSet = newSet;
currentAnimIndex = index;
return;
}
updateSet = false;
// Otherwise, set up a transition
transitionInProgress = true;
// Store the old animation in transitionAnimFrom
transitionAnimFrom = currentAnimSet;
prevAnimIndex = currentAnimIndex;
// Store the new animation in transitionAnimTo
transitionAnimTo = newSet;
nextAnimIndex = index;
transitionMorphStep = 0;
currentGlobalAlpha = 0.0;
}
////////////////////////////////////////////////////////////
// Update animation set
////////////////////////////////////////////////////////////
void changeAnimationSet() {
if (!transitionInProgress && !morphInProgress) {
bool currentIsPartlyOrActive = isPartlyOrActive(currentMode);
// round(random(activeAnimationsCount))
if (currentIsPartlyOrActive) {
if (desiredMode != currentMode || updateSet) {
if (desiredMode < currentMode) {
// Transitioning down: require cycle completion
if (animationCycleCompleted) {
currentMode = desiredMode;
switchHelper();
}
} else if (updateSet) {
if (animationCycleCompleted) {
switchHelper();
}
} else {
// Transitioning up: allow immediately
currentMode = desiredMode;
switchHelper();
}
}
} else {
// From idle, allow transitions
if (desiredMode != currentMode) {
currentMode = desiredMode;
switchHelper();
}
}
}
}
////////////////////////////////////////////////////////////
// Mouthpiece functionality
////////////////////////////////////////////////////////////
CRGB getTailNoiseColor(uint16_t position, uint16_t offset, uint8_t speed) {
uint16_t noiseValue = inoise8(position * TAIL_NOISE_SCALE + offset, 0);
CRGB brightNoiseColor = ColorFromPalette(northernLightsPal, noiseValue);
CRGB warmNoiseColor = ColorFromPalette(warmNorthernLightsPal, noiseValue);
CRGB noiseColor = blend(brightNoiseColor, warmNoiseColor, speed);
return noiseColor;
}
void updateSingleTail(CRGB* tailLeds, uint8_t numLeds, uint16_t sensorValue, uint16_t& offset) {
const uint8_t blowThreshold = 15;
bool isBlowing = (sensorValue > blowThreshold);
uint8_t addSensorValue = map(sensorValue, 0, 255, 0, 25);
// Increase or decrease offset depending on blow input
offset = isBlowing ? offset + TAIL_NOISE_SPEED + addSensorValue : offset - TAIL_NOISE_SPEED - addSensorValue;
const uint8_t fadeEndIndex = (numLeds > 25) ? 25 : numLeds;
for (uint8_t i = 0; i < numLeds; i++) {
uint8_t reversedIndex = (numLeds - 1) - i;
CRGB color = getTailNoiseColor(reversedIndex, offset, sensorValue);
if (reversedIndex < fadeEndIndex) {
float fadeFactor = float(reversedIndex) / 25.0;
color.nscale8(uint8_t(fadeFactor * 255));
}
tailLeds[i] = color;
}
}
////////////////////////////////////////////////////////////
// Blow ring
////////////////////////////////////////////////////////////
void updateSingleBlowRing(CRGB* ringLeds, uint8_t numLeds, uint16_t sensorValue, uint16_t& sineTimeOffset, float& sinePosOffset, uint16_t& offset) {
const uint8_t indicatorLeds = 3;
const float blowRingSpeed = mapFloat(sensorValue, 0, 255, 0.4, 1.05);
uint16_t noiseValue = inoise8(1 * (TAIL_NOISE_SCALE - 10) + offset, 0);
CRGB brightNoiseColor = ColorFromPalette(northernLightsPal, noiseValue);
CRGB warmNoiseColor = ColorFromPalette(warmNorthernLightsPal, noiseValue);
CRGB ringColor = blend(brightNoiseColor, warmNoiseColor, sensorValue);
const int8_t mappedSine = map(sin8(sineTimeOffset), 0, 255, -3, 3);
if (sensorValue <= 0) {
sineTimeOffset += 10;
} else {
sinePosOffset += blowRingSpeed;
}
// Clear all LEDs
for (uint8_t i = 0; i < numLeds; i++) {
ringLeds[i] = ringLeds[i].nscale8(200);
}
// Update the indicator LEDs
for (uint8_t j = 0; j < indicatorLeds; j++) {
uint8_t pos = (j * numLeds) / indicatorLeds;
int newPos = pos + mappedSine + sinePosOffset;
newPos = (newPos + numLeds) % numLeds;
ringLeds[newPos] = ringColor;
}
}
////////////////////////////////////////////////////////////
// Read blow value and set for 10 seconds
////////////////////////////////////////////////////////////
void updateWindValue(uint16_t sensorValue, uint8_t& accumulatedDifference, unsigned long& lastPositiveReadingTime) {
uint8_t currentBlowValue = map(sensorValue, 0, 1024, 0, 15);
uint8_t addValue = 15;
uint8_t subValue = 25;
if (accumulatedDifference >= maxBlowValue || accumulatedDifference + addValue >= maxBlowValue) {
accumulatedDifference = maxBlowValue;
} else if (currentBlowValue > 0) {
lastPositiveReadingTime = millis();
accumulatedDifference += addValue;
}
if (currentBlowValue == 0 && (millis() - lastPositiveReadingTime > idleTime)) {
if (accumulatedDifference <= 0 || accumulatedDifference - subValue <= 0) {
accumulatedDifference = 0;
} else {
accumulatedDifference -= subValue;
}
}
}
////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////
bool isPartlyOrActive(int mode) {
return (mode == 1 || mode == 2);
}
static uint8_t simpleHash8(uint16_t x) {
x ^= (x >> 4);
x *= 0x27d4;
x ^= (x >> 5);
return (uint8_t)x;
}
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void switchHelper() {
switch (currentMode) {
case 0: setAnimationSet(idleAnimations, round(random(idleAnimationsCount))); break;
case 1:
{
if (accumulatedDifference2 > 0) {
partlyLeftAnimIndex++;
setAnimationSet(partlyAnimationsLeft, partlyLeftAnimIndex % partlyAnimationsLeftCount);
} else {
partlyRightAnimIndex++;
setAnimationSet(partlyAnimationsRight, partlyRightAnimIndex % partlyAnimationsRightCount);
}
break;
}
case 2: {
activeAnimIndex++;
setAnimationSet(activeAnimations, activeAnimIndex % activeAnimationsCount);
break;
}
}
}
////////////////////////////////////////////////////////////
// COPY TO HERE
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Setup
////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN_STRIP_1, GRB>(leds_1, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_2, GRB>(leds_2, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_3, GRB>(leds_3, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_4, GRB>(leds_4, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_5, GRB>(leds_5, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_6, GRB>(leds_6, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_STRIP_7, GRB>(leds_7, ledsInGroup);
FastLED.addLeds<WS2812, LED_PIN_BLOW_RING_1, GRB>(leds_blow_ring_1, LEDS_IN_BLOW_RING);
FastLED.addLeds<WS2812, LED_PIN_BLOW_RING_2, GRB>(leds_blow_ring_2, LEDS_IN_BLOW_RING);
FastLED.addLeds<WS2812, LED_PIN_TAIL_1, GRB>(leds_tail_1, LEDS_IN_TAIL);
FastLED.addLeds<WS2812, LED_PIN_TAIL_2, GRB>(leds_tail_2, LEDS_IN_TAIL);
}
////////////////////////////////////////////////////////////
// Loop
////////////////////////////////////////////////////////////
void loop() {
updateWindValue(analogRead(A0), accumulatedDifference1, lastPositiveReadingTime1);
updateWindValue(analogRead(A1), accumulatedDifference2, lastPositiveReadingTime2);
EVERY_N_MILLISECONDS(20) {
noiseX += NOISE_SPEED;
updateSingleBlowRing(leds_blow_ring_1, LEDS_IN_BLOW_RING, accumulatedDifference1, blowRingSineTime1, blowRingSineOffset1, noiseX);
updateSingleBlowRing(leds_blow_ring_2, LEDS_IN_BLOW_RING, accumulatedDifference2, blowRingSineTime2, blowRingSineOffset2, noiseX);
updateSingleTail(leds_tail_1, LEDS_IN_TAIL, accumulatedDifference1, tailOffset1);
updateSingleTail(leds_tail_2, LEDS_IN_TAIL, accumulatedDifference2, tailOffset2);
}
// Decide newMode from sensors
uint8_t diff = abs(accumulatedDifference1 - accumulatedDifference2);
uint8_t newMode = -1;
if (accumulatedDifference1 < 50 && accumulatedDifference2 < 50) {
newMode = 0; // idle
} else if (accumulatedDifference1 > 50 && accumulatedDifference2 > 50 && diff < 25) {
newMode = 2; // active
} else {
newMode = 1; // partly
}
if (newMode != desiredMode) {
desiredMode = newMode;
}
changeAnimationSet();
// Continue with ring morphing / transitions
if (transitionInProgress) {
updateTransition();
} else {
if (!morphInProgress && !frameDelayActive) {
const Animation& anim = currentAnimSet[currentAnimIndex];
nextFrame = pickNextFrame(currentFrame, anim.numFrames, anim.reverse);
startMorph(anim, currentFrame, nextFrame);
} else if (morphInProgress) {
updateMorph();
} else {
playCurrentFrame();
}
if (!morphInProgress && !frameDelayActive) {
frameDelayActive = true;
frameDelayStartTime = millis();
}
if (frameDelayActive && (millis() - frameDelayStartTime >= currentAnimSet[currentAnimIndex].frameDelay)) {
frameDelayActive = false;
}
}
FastLED.show();
}