#include <FastLED.h>
#define NUM_LEDS 100
#define TOTAL_LEDS 200
#define SWITCH_LEFT 4
#define SWITCH_RIGHT 6
#define LED_PIN_L 9
#define LED_PIN_R 10
#define BRIGHTNESS 50
#define POTENTIOMETER_PIN_WIND A0
#define POTENTIOMETER_PIN_FADE A3
#define POTENTIOMETER_PIN_BOTTOM A4
#define POTENTIOMETER_PIN_TOP A5
CRGB ledsL[NUM_LEDS];
CRGB ledsR[NUM_LEDS];
uint8_t starIntensity[TOTAL_LEDS]; // Array to hold the intensity of the stars
uint8_t starIncreasing[TOTAL_LEDS];
uint8_t windNoise = 120;
uint16_t windX = 0;
uint16_t x = 0;
uint16_t y = 0; // Position along the noise function
uint8_t bloodHue = 96; // Blood color [hue from 0-255]
uint8_t bloodSat = 255; // Blood staturation [0-255]
int flowDirection = -1; // Use either 1 or -1 to set flow direction
uint16_t cycleLength = 3000; // Lover values = continuous flow, higher values = distinct pulses.
uint16_t pulseLength = 500; // How long the pulse takes to fade out. Higher value is longer.
uint16_t pulseOffset = 750; // Delay before second pulse. Higher value is more delay.
uint8_t baseBrightness = 10; // Brightness of LEDs when not pulsing. Set to 0 for off.
// Case vVriables
enum State { STARS,
NOISE,
HEARTBEAT
};
State currentState = NOISE;
State targetState = NOISE;
unsigned long effectStartTime = millis();
int caseEffectsDuration = 60000; // Duration of case effects in milliseconds
// Transition Variables
uint8_t currentBrightness = BRIGHTNESS;
bool transitionInProgress = false;
int transitionDuration = 4000; // Duration of the fade in/out milliseconds
unsigned long transitionStartTime;
// Define a palette.
DEFINE_GRADIENT_PALETTE(NorthernLightsPalette) {
0, 0, 207, 82, // Colder colors
62, 3, 46, 62,
143, 25, 100, 106,
192, 0, 198, 144,
255, 0, 223, 150
};
DEFINE_GRADIENT_PALETTE(WarmNorthernLightsPalette) {
0, 255, 40, 5, // Warmer reds and oranges
62, 255, 60, 5,
143, 255, 80, 0,
180, 255, 100, 0,
200, 255, 0, 0,
255, 255, 0, 0
};
CRGBPalette16 currentPalette = CRGBPalette16(NorthernLightsPalette);
CRGBPalette16 warmPalette = CRGBPalette16(WarmNorthernLightsPalette);
void setup() {
FastLED.addLeds<WS2811, LED_PIN_L, BRG>(ledsL, NUM_LEDS);
FastLED.addLeds<WS2811, LED_PIN_R, BRG>(ledsR, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
Serial.begin(57600);
pinMode(POTENTIOMETER_PIN_WIND, INPUT);
pinMode(POTENTIOMETER_PIN_FADE, INPUT);
pinMode(POTENTIOMETER_PIN_BOTTOM, INPUT);
pinMode(POTENTIOMETER_PIN_TOP, INPUT);
pinMode(SWITCH_LEFT, INPUT_PULLUP);
pinMode(SWITCH_RIGHT, INPUT_PULLUP);
memset(starIntensity, 0, TOTAL_LEDS); // Initialize the starIntensity array
memset(starIncreasing, false, TOTAL_LEDS);
}
void loop() {
// Read the potentiometer values
int potValueFade = map(analogRead(POTENTIOMETER_PIN_FADE), 0, 1023, 0, 50);
int potValueBottom = map(analogRead(POTENTIOMETER_PIN_BOTTOM), 0, 1023, 0, NUM_LEDS - 1);
int potValueTop = map(analogRead(POTENTIOMETER_PIN_TOP), 0, 1023, 0, NUM_LEDS - 1);
int potValueWind = map(analogRead(POTENTIOMETER_PIN_WIND), 0, 1023, 120, 250);
int switchLeft = digitalRead(SWITCH_LEFT) == 0 ? true : false;
int switchRight = digitalRead(SWITCH_RIGHT) == 0 ? true : false;
if (switchRight) {
windNoise = inoise8(windX * 0.5, 0, 0);
windX++;
} else {
windNoise = potValueWind;
}
// Ensure top is always above bottom
if (potValueTop < potValueBottom) {
potValueTop = potValueBottom;
}
// Fade in and out between cases
/* if (currentState != targetState && transitionInProgress) {
if (transitionStartTime == 0) {
transitionStartTime = millis();
}
EVERY_N_MILLISECONDS(40) {
unsigned long elapsedTime = millis() - transitionStartTime;
float progress = (float)elapsedTime / transitionDuration;
if (progress >= 0.95) {
progress = 1.0;
currentState = targetState;
transitionStartTime = 0;
}
float easedValue = easeInQuart(progress);
currentBrightness = map(easedValue * BRIGHTNESS, 0, 50, 50, 0);
FastLED.setBrightness(currentBrightness);
}
} else if (currentState == targetState && transitionInProgress) {
if (transitionStartTime == 0) {
transitionStartTime = millis();
}
EVERY_N_MILLISECONDS(40) {
unsigned long elapsedTime = millis() - transitionStartTime;
float progress = (float)elapsedTime / transitionDuration;
if (progress >= 0.95) {
progress = 1.0;
transitionStartTime = 0;
transitionInProgress = false;
}
float easedValue = easeOutQuart(progress);
currentBrightness = easedValue * BRIGHTNESS;
FastLED.setBrightness(currentBrightness);
}
} */
if (currentState != targetState && transitionInProgress) {
EVERY_N_MILLISECONDS(40) {
FastLED.setBrightness(currentBrightness);
currentBrightness--;
if (FastLED.getBrightness() <= 0) {
currentState = targetState;
currentBrightness = 0;
}
}
}
if (currentState == targetState && transitionInProgress) {
EVERY_N_MILLISECONDS(40) {
FastLED.setBrightness(currentBrightness);
currentBrightness++;
if (FastLED.getBrightness() >= BRIGHTNESS) {
currentBrightness = BRIGHTNESS;
effectStartTime = millis();
transitionInProgress = false;
}
}
}
// State management
switch (currentState) {
case HEARTBEAT:
// Heartbeat/pulse effect code
heartBeat(switchLeft);
if (millis() - effectStartTime > caseEffectsDuration && transitionInProgress == false) { // 1 minute
transitionInProgress = true;
targetState = NOISE;
}
break;
case STARS:
// Stars effect code
EVERY_N_MILLISECONDS(20) {
int windStars;
if (windNoise <= 120) {
windStars = 250;
} else if (windNoise > 120 && windNoise <= 200) {
windStars = map(windNoise, 121, 201, 250, 300);
} else {
windStars = 300;
}
addRandomStars(windStars);
}
if (millis() - effectStartTime > caseEffectsDuration && transitionInProgress == false) { // 1 minute
transitionInProgress = true;
targetState = NOISE;
}
break;
case NOISE:
// Noise effect code
EVERY_N_SECONDS(15) {
if (!transitionInProgress) {
uint8_t rNumber = random8();
if (rNumber <= 4) { // Adjust this value as needed
currentState = STARS;
} else if (rNumber > 4 && rNumber <= 8) { // Adjust this value as needed
targetState = HEARTBEAT;
transitionInProgress = true;
}
}
}
EVERY_N_MILLISECONDS(10) {
int windSpeed;
int hueValue;
if (windNoise <= 120) {
hueValue = 0;
windSpeed = 3;
} else if (windNoise > 120 && windNoise <= 200) {
hueValue = map(windNoise, 121, 201, 0, 255);
windSpeed = map(windNoise, 121, 201, 3, 10);
} else {
windSpeed = 10;
hueValue = 255;
}
fill_noise(potValueBottom, potValueTop, hueValue, potValueFade);
x += windSpeed;
}
break;
}
FastLED.show();
}
// Function for generating a nothern light effect
void fill_noise(int startLED, int endLED, int valueHue, int fade) {
uint8_t blendFactor = valueHue;
int fadeLength = fade; // Number of LEDs to fade in and out
for (int i = 0; i < NUM_LEDS; i++) {
if (i >= startLED && i <= endLED) {
uint8_t noise = inoise8(i * 15 + x, 0, i * 15 + x);
CRGB color1 = ColorFromPalette(currentPalette, noise);
CRGB color2 = ColorFromPalette(warmPalette, noise);
CRGB blendedColor = blend(color1, color2, blendFactor);
// Calculate fade factor for start and end
int fadeFactor = 255;
if (i - startLED < fadeLength) { // Fade in logic
fadeFactor = map(i - startLED, 0, fadeLength - 1, 0, 255);
} else if (endLED - i < fadeLength) { // Fade out logic
fadeFactor = map(endLED - i, 0, fadeLength - 1, 0, 255);
}
// Apply the fade factor
blendedColor.nscale8(fadeFactor);
ledsL[i] = blendedColor;
ledsR[i] = blendedColor;
} else {
ledsL[i] = CRGB::Black;
ledsR[i] = CRGB::Black;
}
}
}
// Function for generating a shimmering star effect
void addRandomStars(int speed) {
for (int i = 0; i < TOTAL_LEDS; i++) {
// Start a new star with a low probability and only if it's not already a star
if (random16() < speed && starIntensity[i] == 0) {
starIntensity[i] = 1;
starIncreasing[i] = true;
}
// Increase or decrease the intensity of the star
if (starIntensity[i] > 0) {
if (starIncreasing[i]) {
// Increase intensity (fade in)
if (starIntensity[i] < 245) {
starIntensity[i] += 10;
} else {
starIntensity[i] = 255;
starIncreasing[i] = false; // Peak reached, start decreasing
}
} else {
// Decrease intensity (fade out)
if (starIntensity[i] > 10) {
starIntensity[i] -= 10;
} else {
starIntensity[i] = 0; // Star has faded out
if (i < NUM_LEDS) {
ledsL[i] = CRGB::Black; // Ensure LED is turned off
} else {
ledsR[i % 100] = CRGB::Black;
}
}
// Update the LED color
if (starIntensity[i] > 0) {
if (i < NUM_LEDS) {
ledsL[i] = CHSV(0, 0, starIntensity[i]);
} else {
ledsR[i % 100] = CHSV(0, 0, starIntensity[i]);
}
}
}
}
}
}
// Function for generating a heartbeat/pulse effect
void heartBeat(int changeSwitch) {
if (!changeSwitch) {
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t bloodVal = sumPulse((5 / NUM_LEDS / 2) + (NUM_LEDS / 2) * i * flowDirection);
ledsL[i] = CHSV(bloodHue, bloodSat, bloodVal);
ledsR[i] = CHSV(bloodHue, bloodSat, bloodVal);
}
} else {
for (int i = 0; i < TOTAL_LEDS; i++) {
if (i < NUM_LEDS) {
uint8_t bloodVal = sumPulse((5 / TOTAL_LEDS / 2) + (TOTAL_LEDS / 2) * i * flowDirection);
ledsL[i] = CHSV(bloodHue, bloodSat, bloodVal);
} else {
uint8_t bloodVal = sumPulse((5 / TOTAL_LEDS / 2) + (TOTAL_LEDS / 2) * i * -flowDirection);
ledsR[i % 100] = CHSV(bloodHue, bloodSat, bloodVal);
}
}
}
}
int sumPulse(int time_shift) {
//time_shift = 0; //Uncomment to heart beat/pulse all LEDs together
int pulse1 = pulseWave8(millis() + time_shift, cycleLength, pulseLength);
int pulse2 = pulseWave8(millis() + time_shift + pulseOffset, cycleLength, pulseLength);
return qadd8(pulse1, pulse2); // Add pulses together without overflow
}
uint8_t pulseWave8(uint32_t ms, uint16_t cycleLength, uint16_t pulseLength) {
uint16_t T = ms % cycleLength;
if (T > pulseLength) return baseBrightness;
uint16_t halfPulse = pulseLength / 2;
if (T <= halfPulse) {
return (T * 255) / halfPulse; //first half = going up
} else {
return ((pulseLength - T) * 255) / halfPulse; //second half = going down
}
}
float easeInQuart(float x) {
return x * x * x * x;
}
float easeOutQuart(float x) {
return 1 - pow(1 - x, 4);
}