#include <FastLED.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ARM_LEDS 100
#define PULSE_LEDS 100
#define RING_LEDS 12
#define BRIGHTNESS 200
#define ARM_1_LED_PIN 8
#define ARM_2_LED_PIN 9
#define ARM_3_LED_PIN 10
#define RING_LED_PIN 12
#define PULSE_LED_PIN 11
#define TEMP_BASE 7
#define TEMP_CHANGE 8
#define POTENTIOMETER_PIN_BRIGHTNESS A5
#define POTENTIOMETER_PIN_FADE A4
#define POTENTIOMETER_PIN_HUE A3
#define POTENTIOMETER_PIN_TEMP A4
CRGB armLeds1[ARM_LEDS];
CRGB armLeds2[ARM_LEDS];
CRGB armLeds3[ARM_LEDS];
CRGB tempArmLeds[ARM_LEDS];
CRGB pulseLeds[PULSE_LEDS];
CRGB noisePulseLeds[PULSE_LEDS];
CRGB ringLeds[RING_LEDS];
OneWire oneWireBase(TEMP_BASE);
OneWire oneWireChange(TEMP_CHANGE);
DallasTemperature sensorsBase(&oneWireBase);
DallasTemperature sensorsChange(&oneWireChange);
uint8_t maxBrightness = BRIGHTNESS;
uint8_t currentBrightness = maxBrightness;
uint8_t windNoise = 120;
uint16_t windX = 0;
uint16_t x1 = 0;
uint16_t x2 = 0;
uint16_t x3 = 0;
float zeroPoint = 0; // Variable to store the zero point temperature
float lastTemp = 0; // Variable to store the last temperature of sensor two
float accumulatedDifference = 0; // Variable to store the accumulated temperature difference
uint8_t bloodHue = 255; // 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 = 2000; // 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 = 500; // Delay before second pulse. Higher value is more delay.
uint8_t baseBrightness = 0; // Brightness of LEDs when not pulsing. Set to 0 for off.
// 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, ARM_1_LED_PIN, BRG>(armLeds1, ARM_LEDS);
FastLED.addLeds<WS2811, ARM_2_LED_PIN, BRG>(armLeds2, ARM_LEDS);
FastLED.addLeds<WS2811, ARM_3_LED_PIN, BRG>(armLeds3, ARM_LEDS);
FastLED.addLeds<WS2811, PULSE_LED_PIN, BRG>(pulseLeds, PULSE_LEDS);
FastLED.addLeds<WS2811, RING_LED_PIN, BRG>(ringLeds, RING_LEDS);
FastLED.setBrightness(BRIGHTNESS);
sensorsBase.begin();
sensorsChange.begin();
setZeroPoint(); // Set the initial zero point
sensorsChange.requestTemperatures();
lastTemp = sensorsChange.getTempCByIndex(0); // Initialize the last temperature
Serial.begin(57600);
pinMode(POTENTIOMETER_PIN_FADE, INPUT);
pinMode(POTENTIOMETER_PIN_BRIGHTNESS, INPUT);
}
void loop() {
// Read the potentiometer values
int potValueFade = map(analogRead(POTENTIOMETER_PIN_FADE), 0, 1023, 0, 50);
/* int potValueHue = map(analogRead(POTENTIOMETER_PIN_HUE), 0, 1023, 0, 255); */
int potValueTemp = map(analogRead(POTENTIOMETER_PIN_TEMP), 0, 1023, 100, 500);
maxBrightness = map(analogRead(POTENTIOMETER_PIN_BRIGHTNESS), 0, 1023, 0, BRIGHTNESS);
cycleLength = map(analogRead(POTENTIOMETER_PIN_HUE), 0, 1023, 2500, 1500);
pulseOffset = map(analogRead(POTENTIOMETER_PIN_HUE), 0, 1023, 500, 800);
bloodSat = map(analogRead(POTENTIOMETER_PIN_HUE), 0, 1023, 255, 100);
Serial.println(pulseLeds[10].red);
if (bloodSat > 101 || accumulatedDifference > 0.1) {
flowDirection = 1;
} else {
flowDirection = -1;
}
currentBrightness = maxBrightness;
FastLED.setBrightness(currentBrightness);
tempChange();
EVERY_N_MILLISECONDS(10) {
int hueValue;
int speed1;
int speed2;
int speed3;
/* hueValue = map(accumulatedDifference * 100, 0, potValueTemp, potValueHue, 255); */
hueValue = 50;
speed1 = calculateSpeed(accumulatedDifference * 100, 1, potValueTemp);
speed2 = calculateSpeed(accumulatedDifference * 100, 2, potValueTemp);
speed3 = calculateSpeed(accumulatedDifference * 100, 3, potValueTemp);
CRGB pulseMaxBrightness[ARM_LEDS];
heartBeat();
tempHeartBeat(pulseMaxBrightness);
fill_noise(armLeds1, x1, hueValue, potValueFade);
fill_noise(armLeds2, x2, hueValue, potValueFade);
fill_noise(armLeds3, x3, hueValue, potValueFade);
if (bloodSat > 101 || accumulatedDifference > 1) {
fill_noise(noisePulseLeds, -x1, hueValue, 0);
} else {
fill_noise(noisePulseLeds, x1, hueValue, 0);
}
// CODE HERE
for (int i = 0; i < PULSE_LEDS; i++) {
uint8_t r = pulseLeds[i].red;
pulseLeds[i] = blend(noisePulseLeds[i].nscale8(40), pulseLeds[i], r); // Example: 50% blend
}
// Get the average color of armLeds1
CRGB averageColor = getAverageColor(armLeds1, ARM_LEDS);
// Set all ringLeds to the average color
for (int i = 0; i < 12; i++) {
ringLeds[i] = averageColor;
}
// Update offsets for each strip
x1 += speed1;
x2 += speed2;
x3 += speed3;
}
FastLED.show();
}
uint8_t getBrightness(const CRGB& color) {
return max(color.r, max(color.g, color.b));
}
void fill_noise(CRGB* leds, int x_offset, int valueHue, int fade) {
uint8_t blendFactor = valueHue;
int fadeLength = fade; // Number of LEDs to fade in and out
for (int i = ARM_LEDS - 1; i >= 0; i--) { // Reverse iteration
uint8_t noise = inoise8(i * 15 + x_offset, 0, i * 15 + x_offset);
CRGB color1 = ColorFromPalette(currentPalette, noise);
CRGB color2 = ColorFromPalette(warmPalette, noise);
CRGB blendedColor = blend(color1, color2, blendFactor);
int reverseIndex = ARM_LEDS - i - 1; // Calculate the reverse index
uint8_t linearFadeFactor = 255;
bool isFading = false;
// Fade out logic at the end of the strip
if (i < fadeLength) {
linearFadeFactor = map(i, fadeLength - 1, 0, 255, 0);
isFading = true;
}
if (isFading) {
// Apply dripping effect only in fade areas
uint8_t noiseFade = inoise8(reverseIndex * 125 - x_offset, 0, reverseIndex * 125 + x_offset);
int noiseFadeFactor = map(noiseFade, 0, 255, 0, linearFadeFactor);
blendedColor.nscale8(noiseFadeFactor);
} else {
// No dripping effect outside fade areas
blendedColor.nscale8(linearFadeFactor);
}
leds[reverseIndex] = blendedColor; // Assigning color in reverse
}
}
CRGB getAverageColor(CRGB* leds, int numLeds) {
unsigned long totalR = 0, totalG = 0, totalB = 0;
for (int i = 0; i < numLeds; i++) {
totalR += leds[i].r;
totalG += leds[i].g;
totalB += leds[i].b;
}
CRGB averageColor(totalR / numLeds, totalG / numLeds, totalB / numLeds);
return averageColor;
}
int calculateSpeed(int difference, int stripNumber, int potValue) {
switch (stripNumber) {
case 1: return map(difference, 0, potValue, 10, 40);
case 2: return map(difference, 0, potValue, 15, 45);
case 3: return map(difference, 0, potValue, 20, 60);
default: return 3;
}
}
// Function for generating a heartbeat/pulse effect
void heartBeat() {
for (int i = 0; i < PULSE_LEDS; i++) {
uint8_t bloodVal = sumPulse((5 / PULSE_LEDS / 2) + (PULSE_LEDS / 2) * i * flowDirection);
pulseLeds[i] = CHSV(bloodHue, bloodSat, bloodVal);
}
}
void tempHeartBeat(CRGB* pulseMaxBrightness) {
for (int i = 0; i < ARM_LEDS; i++) {
uint8_t bloodVal = sumPulse((5 / ARM_LEDS / 2) + (ARM_LEDS / 2) * i * -1);
tempArmLeds[i] = CHSV(bloodHue, bloodSat, bloodVal);
pulseMaxBrightness[i] = tempArmLeds[i]; // Store the maximum brightness value
}
}
int sumPulse(int time_shift) {
//time_shift = 0; //Uncomment to heart beat/pulse all LEDs together
int pulse1 = pulseWave8(millis() / 3 + time_shift, cycleLength, pulseLength);
int pulse2 = pulseWave8(millis() / 3 + 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
}
}
// Function to set the zero point
void setZeroPoint() {
sensorsBase.requestTemperatures();
zeroPoint = sensorsBase.getTempCByIndex(0);
}
void tempChange() {
sensorsBase.requestTemperatures();
sensorsChange.requestTemperatures();
float CelsiusBase = sensorsBase.getTempCByIndex(0);
float CelsiusChange = sensorsChange.getTempCByIndex(0);
// Calculate the temperature difference with sensor one as the main temperature
float tempDifferenceC = CelsiusChange - zeroPoint;
// Check if the temperature difference is negative
if (tempDifferenceC < 0) {
setZeroPoint(); // Reset the zero point
tempDifferenceC = 0; // Reset the temperature difference
accumulatedDifference = 0;
} else {
// Calculate the difference since the last reading
float differenceSinceLast = CelsiusChange - lastTemp;
// Accumulate the difference if the temperature is rising
if (differenceSinceLast > 0) {
accumulatedDifference += differenceSinceLast;
}
// Decrease the accumulated difference if the temperature is dropping
else if (differenceSinceLast < 0) {
accumulatedDifference += differenceSinceLast; // This will subtract if differenceSinceLast is negative
if (accumulatedDifference < 0) accumulatedDifference = 0; // Prevents the accumulated difference from going negative
}
}
Serial.println(accumulatedDifference);
// Update the last temperature and time
lastTemp = CelsiusChange;
}