Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to have a smooth fade effect? #9

Closed
Jannomag opened this issue Oct 14, 2021 · 2 comments
Closed

Any way to have a smooth fade effect? #9

Jannomag opened this issue Oct 14, 2021 · 2 comments

Comments

@Jannomag
Copy link

Jannomag commented Oct 14, 2021

Hi,
I'm looking for a project like this for weeks and I like your concept very much...it's nearly exactly what I wanted to do but I never figured it out by myself (I'm not a programmer).

I want to use SK6812 RGBWW strips and already modified your sketch to have real white in stead of rgb-white.

Now it would be really cool, if the color wipe would be done with smooth fade effects instead of going from 0 to 1 and back to 0 immediately.

Do you have any idea how I can do this?

EDIT: I retyped your whole sketch to fit for my needs and just found out that you added a fading effect by using "turnOnSpeed" and "turnOffSpeed". But somehow this doesn't work for me at all.

@Vinz68
Copy link
Owner

Vinz68 commented Nov 23, 2021

Hi Jannomag,

yes there is a smoothing (breathe) function. Currently it smooth (breathe) a number of leds (2 left side and 2 right side) of each stair/step. Smoothing/breathe takes some CPU performance. You can experiment first by increasing the setting BREATHELEDS ; then more leds will be used and you can see if your board is fast enough. If everything works fine you could start modifying your code when stairs are turned on/off. Hope this helps a bit. Sorry for the late response , it seems I missed a notification on this repository. Good luck.

@Jannomag
Copy link
Author

Hi, thanks for the reply.

I modified the script nearly completely for my needs and it works very good now. Every step fades in and out and this effect gets slower each step for a nice effect.


// LED-Staircase by Jannomag
// Modified code, original made by Vinz68! Thanks for this!


#include <Adafruit_NeoPixel.h> // Base library for LED controlling
#include <WS2812FX.h> // Additional library for easy effects in "Partymode"

#ifdef __AVR__
#include <avr/power.h>
#endif

///////////////////////////////////////////////////////////////////////////
// Neopixel configuration
#define PIN           9   // Pin used for the LEDs
#define LEDSTRIPS     14   // Number of stairs
#define LEDSPERSTAIR  30   // Number of leds per stair
#define NUMPIXELS     LEDSTRIPS*LEDSPERSTAIR   // Calculate the total number of LEDs
#define BRIGHTNESS    35  // Brightness of the LEDs
#define STEPBRIGHTNESS 255 // brightness of the steps when on

///////////////////////////////////////////////////////////////////////////
// Additional input (switches)
#define CLEANINGMODE  4   // Switch for turning all LEDs on 
#define PARTYMODE     5   // Switch for enabling fancy RGB party mode

///////////////////////////////////////////////////////////////////////////
// Setup of NeoPixel and WS2812FX
// I was using SK6812 RGBW LEDs. If using RGB-only LED strips like WS2812, change "NEO_GRBW" to "NEO_GRB"
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);
WS2812FX ws2812fx = WS2812FX(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);


///////////////////////////////////////////////////////////////////////////
// Configuration of PIR sensors
int alarmPinTop = 2;              // PIR at the top of the stairs
int alarmPinBottom = 3;           // PIR at the bottom of the stairs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ;          // PIR at the bottom of the stairs
int alarmValueTop = LOW;          // Variable to hold the PIR status
int alarmValueBottom = LOW;       // Variable to hold the PIR status

///////////////////////////////////////////////////////////////////////////
// Configuration of the Light dependent resistor (LDR)
bool useLDR = true;               // flag, when true the program uses the LDR, set to "false" if you don't have a LDR sensor.
int LDRSensor = A6;               // Light dependent resistor, Analog Input line 
long LDRValue = 0;                // Variable to hold the current measured LDR value
long LDRThreshold = 600;          // Only switch on LED's at night when LDR senses low light conditions - you may have to change this value for your circumstances!
// Define the number of samples to keep track of. The higher the number, the more the readings will be smoothed, but the slower the output will respond to the input. 
// For our use case (determine the ammout of light) smoothing is good, so walk-by the LDR sensor or a sensor read spike is ingnored. 
const int numReadings = 100;
int readings[numReadings];        // the readings from the analog input
int readIndex = 0;                // the index of the current reading
long total = 0;                   // the running total
long average = 0;                 // the average

///////////////////////////////////////////////////////////////////////////
// configuration for fading
int turnOnSpeed = 100;                // speed to turn on next led-strip, in msec between next strip
int turnOffSpeed = 10;               // speed to turn off next led-strip, in msec between next strip
int keepLedsOnTime = 5000;            // keep leds on for at least .. msec.
int keepLedsOffTime = 0;              // keep leds off for at least .. msec.
int stepFade = 0;                     // Variable for fading the step

///////////////////////////////////////////////////////////////////////////
// constants won't change:
const long LEDinterval = 300;
// Set up Variables for the needed program logic (DO NOT TOUCH THESE!)
unsigned long previousLEDMillis = 0;        // will store last time LED was updated
unsigned long timeOut = 0;        // timestamp to remember when the PIR was triggered.
unsigned long timeLoopStart = 0;  // timestamp to remember when loop has started. used to determine the end-delay (keeps loop running in same intervals)
unsigned long timeTemp = 0;       // temp. var used in time calculations
unsigned long timeDiff = 0;       // temp. var used in time calculations
bool readPIRInputs = true;        // flag, when true, reads the PIR sensors. Disabled (false) by the program when LDR indicated that there is enough light.
int downUp = 0;                   // main program mode, The possible values are:
                                  //  0   =   Idle mode, waiting for PIR trigger to turn stairs on.
                                  //  1   =   Going down, Turning stairs on (direction up to down)
                                  //  2   =   Going up, Turnurning stairs on (direction down to up)
                                  //  3   =   Turning leds off, from top to down
                                  //  4   =   Turninf leds off, from bottom to up
                                  //  5   =   All leds just turned off. After short time, automatically mode will be set to "0".
                                  
///////////////////////////////////////////////////////////////////////////
//WS2812FX LED Effects für Partymode
#define DEFAULT_SPEED         1000  // Speed of the effects
#define DEFAULT_BRIGHTNESS    50    // Brightness of the effects
#define TIMER_MS              5000  // Defines the time in ms for each mode
unsigned long last_change = 0;      // Variable for looping
unsigned long now = 0;              // Variable for looping               
// Filter array for WS2812FX mode counters which should be disabled, not working atm.
const int forbiddenModes[] = {0, 1, 2, 3, 24, 25, 26, 27, 28, 29, 34, 35, 45, 48, 50 };

///////////////////////////////////////////////////////////////////////////
// Configuration of first steps variables
int bottomStep[] = {0,1};   // Array of pixel numbers for the first step, count from 0 to the number on your first step at the bottom
int topStep[] = {8,9};      // Array of pixel numbers for the first step, count backwards from your last pixel number 
int bFade = 0;              // Fade state
bool useFirstStepsAtDaylight = true;  // flag, when true the program will use the firstStep function also during daylight (when useLDR=true)


///////////////////////////////////////////////////////////////////////////
// Configuration of the cleaning light and partymode
int cleaningModeActive = 0;       // Variable for storing the state of the cleaning light
int partyModeActive = 0;          // Variable for storing the state of party mode
int arduinoRunsLED = 12;          // status LED, to show the Arduino is up and running
int cleaningModeLED = 11;         // status LED for cleaning mode
int partyModeLED = 10;            // status LED for party mode
int arduinoRunsLEDState = LOW;  // ledState used to set the running LED


void setup() {
  // Initiate WS2812FX
  ws2812fx.init();
  ws2812fx.setBrightness(DEFAULT_BRIGHTNESS);
  ws2812fx.setSpeed(DEFAULT_SPEED);
  ws2812fx.setColor(0xFF000000); // only for RGBW! If using RGB, use 0x000000. FF000000 sets the color to the white LEDs
  ws2812fx.start();

  strip.begin();                    // This initializes the NeoPixel library.

  strip.setBrightness(BRIGHTNESS);  // Sets the brightness of the LEDs (change 'BRIGHTNESS' defined value when you need to change it)
  clearStrip();                     // Initialize all pixels to 'off', and do strip.show()

  // Configure the used digital input & output
  pinMode(alarmPinTop, INPUT_PULLUP);     // for PIR at top of stairs initialise the input pin and use the internal restistor
  pinMode(alarmPinBottom, INPUT_PULLUP);  // for PIR at bottom of stairs initialise the input pin and use the internal restistor

  pinMode(CLEANINGMODE, INPUT_PULLUP);    // sets the internal pullup resistor for input. Connect the switches to the Arduino and Ground!
  pinMode(PARTYMODE, INPUT_PULLUP);       // sets the internal pullup resistor for input. Connect the switches to the Arduino and Ground!

  pinMode(arduinoRunsLED, OUTPUT);        // Sets the LED as output
  pinMode(cleaningModeLED, OUTPUT);       // Sets the LED as output
  pinMode(partyModeLED, OUTPUT);          // Sets the LED as output
                                                                           
  Serial.begin(9600);      // only required for debugging. Output some settings in the Serial Monitor Window 
  Serial.println("-------------------------------------------------"); 
  Serial.print("NeoPixel used on outout-pin [");  
  Serial.print(PIN); 
  Serial.print("] with ");  
  Serial.print(NUMPIXELS); 
  Serial.println(" Pixels"); 
  Serial.print("Number of LED-strips: ");
  Serial.println(LEDSTRIPS); 
  Serial.print("Number of LEDs per strip: ");
  Serial.println(LEDSPERSTAIR); 
  
  if (useLDR) {
    // initialize all the LDR-readings to current values...
    for (int thisReading = 0; thisReading < numReadings; thisReading++) {
      readings[thisReading] = analogRead(LDRSensor);
      total = total + readings[thisReading];
    }
    // ... and calculate the average value of [numReadings] samples.
    LDRValue = total / numReadings;

    Serial.print("LDR used on analog input pin [");
    Serial.print(LDRSensor);
    Serial.println("]");
    Serial.print("Determine number of samples for average value: ");
    Serial.println( numReadings );
    Serial.print("LDR average value = ");
    Serial.println( LDRValue );
    Serial.print("Stairs will work when LDR average value <= ");
    Serial.println(LDRThreshold);
  }
  Serial.println("-------------------------------------------------"); 


  delay (2000); // it takes the sensor 2 seconds to scan the area around it before it can detect infrared presence.
  digitalWrite(arduinoRunsLED, HIGH); // Turns on the Arduino-Runs LED after the setup is completed
  
}

void loop() {

  // While loop for cleaning and party modes
  while ( ( cleaningModeActive == 0 ) && ( partyModeActive == 0 ) ) {    // While both modes are inactive...
    if ( digitalRead(CLEANINGMODE) == LOW ) {   // if the switch for cleaning mode is ON (==LOW)...
      cleaningModeActive=1;                     // set the cleaningModeActive variable to 1
      digitalWrite(arduinoRunsLED, LOW);        // and the running LED off
    }
    if ( digitalRead(PARTYMODE) == LOW ) {      // if the switch for party mode is ON (==LOW)...
      partyModeActive=1;                        // set the cleaningModeActive variable to 1
      digitalWrite(arduinoRunsLED, LOW);        // and the running LED off
    }
    
    // register the current time (in msec, for later use to optimize the execution loop)
    timeLoopStart=millis();

    // By default read the PIR inputs (unless LDR sensors are used and it is light enough...)
    readPIRInputs = true;
    if ( useLDR ) {
      // Read the (new) average value of the LDR (we use a sample array to filter out walk-by and sensor spikes)
      LDRValue = readAverageLDR();

      // For finetuning, show the LDR value, so the LDRThreshold can be determined ; once it works disable the next statement with '//'
      // Serial.println(LDRValue); 

      // Check if LDR senses low light conditions ...
      if ( LDRValue >= LDRThreshold ) {   
        // There is enough light, the stairs/ledstips will not be activated (readPIRInputs = false)
        readPIRInputs = false;

        unsigned long currentLEDMillis = millis();

        // let the running LED blink when the LDR detects light
        if ( currentLEDMillis - previousLEDMillis >= LEDinterval ) {
          // save the last time you blinked the LED
          previousLEDMillis = currentLEDMillis;

          // if the LED is off turn it on and vice-versa:
          if ( arduinoRunsLED == LOW ) {
            arduinoRunsLEDState = HIGH;
          } else {
            arduinoRunsLEDState = LOW;
          }

          // set the LED with the ledState of the variable:
          digitalWrite(arduinoRunsLED, arduinoRunsLEDState);
        }

        // Show that stair will not turn on / on PIR detection, due to LDR logic (daylight mode detected)
        // NOTE: These "Serial" statement can be deleted when everything works fine.
        //       its here for finetuning the LDRThreshold value, which you should configure in the begin of this file.
        Serial.println("LDR detected Daylight according to LDRThreshold configuration.");
        Serial.print("LDR Average value = ");
        Serial.println(LDRValue);
      }
      else {
        // It is dark enough. The stairs/ledstips will be activated. (readPIRInputs = true)
        alarmValueTop = LOW;
        alarmValueBottom = LOW;
        digitalWrite(arduinoRunsLED, HIGH);   // Turns on running LED
      }
    }
    
    
    // Read the PIR inputs ?
    if (readPIRInputs) {
      alarmValueTop = digitalRead(alarmPinTop); // Constantly poll the PIR at the top of the stairs
      alarmValueBottom = digitalRead(alarmPinBottom); // Constantly poll the PIR at the bottom of the stairs
    }
    
    // Check if PIR Top was triggered an leds must be turned on
    if ( (alarmValueTop == HIGH) && (downUp == 0) ) { // the 2nd term indicates that there is currently no activity (up or down)
      timeOut=millis(); // Timestamp when the PIR is triggered. The LED cycle wil then start.
      downUp = 1;
      colourWipeDown( turnOnSpeed ); // Warm White,led-light from top to down. If using RGB-strips just use 3 color values like (255,255,255) for white
    }
  
    // Check if PIR Bottom was triggered an leds must be turned on
    if ( (alarmValueBottom == HIGH) && (downUp == 0)) { // the 2nd term indicates that there is currently no activity (up or down)
      timeOut=millis(); // Timestamp when the PIR is triggered. The LED cycle wil then start.
      downUp = 2;
      colourWipeUp( turnOnSpeed ); // Warm White,led-light from bottom to top. If using RGB-strips just use 3 color values like (255,255,255) for white
    }
  
    // Restart on-timer when a PIR sensor gets HIGH again while the lights are on
    if ( ( downUp != 0 ) && ( ( alarmValueTop == HIGH ) || ( alarmValueBottom == HIGH ) ) ) {
      timeOut=millis();
    }
    
    // Logic to turn the leds off (determines the turn-off direction)
    if ( (downUp!=0) && (timeOut+keepLedsOnTime < millis()) ) { //switch off LED's in the direction of travel.
      if (downUp == 1) {
        downUp = 3;     // mode = turn off leds from top to down
        colourWipeDownOff( turnOffSpeed ); // Off
        downUp=5;      // Stairs are just turned off    
      }
      if (downUp == 2) {
        downUp = 4;  // mode = turn off leds from bottom to top      
        colourWipeUpOff( turnOffSpeed ); // Off
        downUp=5;      // Stairs are just turned off          
      }
  }
    // Enable firstSteps Effect when needed.
    if (downUp==0) {          // Currently no activity on the stairs ? (in idle mode, not turned (or turning) on or off ?) 
  
      // Check if PIRs are read (not when it is dark), or when breathe function must also work during the day
      if ( ( readPIRInputs==true ) && ( (readPIRInputs) || (useFirstStepsAtDaylight) ) ) {    
        firstSteps(); // set fader for firstSteps to 0
      } 
      else {
        bFade = 0;
        clearStrip();          // during daylight all leds turned off.
      }
    }
    else if (downUp==5) {     // eventually the stairs led lights will be turned off again (mode=5)
      delay(keepLedsOffTime); // allow small delay/pause and then activate the stairs again with the breathe and motion detection function
      downUp=0;               // set to 0 to allow breathe/motion detection (-1 for debugging one run)
      bFade = 0; // set fader for firstSteps to 0
    }
  }
  // while loops for cleaning and party mode. If the variables are ==1, run the voids.
  while ( cleaningModeActive == 1 ) {
    cleaningMode();
  }
  while ( partyModeActive == 1 ) {
    partyMode();
  }

}

long readAverageLDR() {

  // Subtract the last reading:
  total = total - readings[readIndex];
  
  // Read the current value of the Light Sensor, and store in samples array
  readings[readIndex] = analogRead(LDRSensor);

  // Add the reading to the total:
  total = total + readings[readIndex];
  
  // Advance to the next position in the array:
  readIndex = readIndex + 1;

  // If we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // Calculate the average:
  average = total / numReadings;

  return (average);
}

void cleaningMode() {
  digitalWrite(cleaningModeLED, HIGH);                // turns on the cleaning mode LED
  for (int l=0; l<NUMPIXELS; l++){                    // for every LED
    strip.setBrightness(255);                         // set additional brightness
    strip.setPixelColor(l, strip.Color(0,0,0,255));   // set white as color, note RGB strips need three color values instead of four!
  }
  strip.show();                                       // This sends the updated pixel's to the hardware.  
  if ( digitalRead(CLEANINGMODE) == HIGH ) {          // if the cleaning mode switch is OFF
    digitalWrite(arduinoRunsLED, HIGH);               // turn on the arduino runing LED
    digitalWrite(cleaningModeLED, LOW);               // turn off the cleaning mode LED
    clearStrip();                                     // clear the strip
    downUp=5;                                         // set the stairs variable to 5
    cleaningModeActive=0;                             // set the cleaning mode variable to 0
    strip.setBrightness(BRIGHTNESS);                  // set brightness to the value defined on top
    delay(100);                                       // delay for debouncing the switch
  }
}

void partyMode() {
  digitalWrite(partyModeLED, HIGH);   // turn the partyModeLED on
  now = millis();   // start millis() 
  ws2812fx.service();   // initiate ws2812fx
  if(now - last_change > TIMER_MS) {    // if the timer reaches TIMER_MS value (default 5000ms)
    uint8_t nextMode = ws2812fx.getMode() + 1;    // add 1 to the mode counter
    // Filter for WS2812FX mode counters. Couldn't get the array from the top working
    // If the nextMode variable equals one of those numbers, set nextMode to the next number which is allowed
    if ( ( nextMode == 1 ) || ( nextMode == 2 ) || ( nextMode == 3 ) ) nextMode = 4;
    if ( ( nextMode == 24 ) || ( nextMode == 25 ) || ( nextMode == 26 ) || ( nextMode == 27 ) || ( nextMode == 28 ) || ( nextMode == 29 ) ) nextMode = 30;
    if ( ( nextMode == 34 ) || ( nextMode == 35 ) ) nextMode = 36;
    if ( ( nextMode == 45 ) ) nextMode = 46;
    if ( ( nextMode == 48 ) ) nextMode = 49;
    if ( ( nextMode == 50 ) ) nextMode = 51;
    
    // 2 tries for filter using the array...doesn't work, so commented out
    /*if ( nextMode == forbiddenModes[nextMode] ) {
      nextMode+1;
    } */
    /*if ( nextMode == forbiddenModes[nextMode] ) {
      nextMode = nextMode +1 ;
    }*/
    ws2812fx.setMode(nextMode % ws2812fx.getModeCount());   // let WS2812FX show the mode
    last_change = now;    // set timer to 0
   
  }
  
  if ( digitalRead(PARTYMODE) == HIGH ) {   // if the partymode switch is HIGH, means it's turned OFF!
    digitalWrite(arduinoRunsLED, HIGH);     // turn on the arduino running LED
    digitalWrite(partyModeLED, LOW);        // turn off the partymode led
    clearStrip();                           // "clear" the strip by using the clearStrip() function
    downUp=5;                               // set the stair mode to 5
    partyModeActive=0;                       // set partyMode to 0
    delay(100);                             // delay for debouncing the switch
  }
}

// fades in the first steps, defined by bottomSteps and topSteps. bFade is the brightness of the white color
void firstSteps() { 
  for (bFade; bFade < 30; bFade++) {
    for (int i = 0; i < LEDSPERSTAIR; i++) {
      strip.setPixelColor(bottomStep[i], strip.Color(0,0,0,bFade));
      strip.setPixelColor(topStep[i], strip.Color(0,0,0,bFade));
      strip.show();
      delay(0);
    }
    delay(6);
  }
}

// Fade in each step, up to down
void colourWipeDown(uint16_t wait) {                              // get the wait variable from the loop
  for (uint16_t k = 0; k < LEDSTRIPS; k++){                       // count up the numbers of strip in a loop
    int start = (NUMPIXELS/LEDSTRIPS) *k;                         // set the LEDs per strip
    for ( stepFade = 0; stepFade < STEPBRIGHTNESS; stepFade++) {  // fade from 0 to STEPBRIGHTNESS
      for (uint16_t j = start; j < start + LEDSPERSTAIR; j++){    // set the lights for each pixel on each step
        strip.setPixelColor(j, strip.Color(0,0,0,stepFade));      // using j as variable for each step, color it to the white LED using stepFade variable (if using RGB LEDs remove a color byte)
        strip.show();                                             // let the strip show the awesome light effect
        }
        delay(0);                                                 // needed delay, even with 0, for fading
    }
    delay(wait);                                                  // wait until the LEDs turn off
    wait = wait +50;                                              // adds a bit time each step to have a nice effect
  }
}

// Fade out each step, up to down
void colourWipeDownOff(uint16_t wait) {                           // Same as above, just vice versa
  for (uint16_t k = 0; k < LEDSTRIPS; k++) {
    int start = ( NUMPIXELS / LEDSTRIPS ) * k;
    for ( stepFade = STEPBRIGHTNESS; stepFade > 0; stepFade-- ) {
      for ( uint16_t j = start; j < start + LEDSPERSTAIR; j++ ) {
        strip.setPixelColor(j, strip.Color(0,0,0,stepFade));
        strip.show();
      }
      delay(0);
    }
    delay(wait);
    wait = wait +50;
   }
}

// Fade in each step, down to up
void colourWipeUp(uint16_t wait) {
  for (uint16_t k = LEDSTRIPS; k > 0; k--){
    int start = (NUMPIXELS/LEDSTRIPS) *k;
    for ( stepFade = 0; stepFade < STEPBRIGHTNESS; stepFade++) {
      for ( uint16_t j = start; j < start + LEDSPERSTAIR; j++){
        strip.setPixelColor(j-LEDSPERSTAIR, strip.Color(0,0,0,stepFade));
        strip.show();
        }
        delay(0);
    }
    delay(wait);
    wait = wait +20; // adds a bit time each step to have a nice effect
  }
}

// Fade out each step, down to up
void colourWipeUpOff(uint16_t wait) {
  for (uint16_t k = LEDSTRIPS; k > 0; k--){
    int start = (NUMPIXELS/LEDSTRIPS) *k;
    for ( stepFade = STEPBRIGHTNESS; stepFade > 0; stepFade--) {
      for ( uint16_t j = start; j < start + LEDSPERSTAIR; j++){
        strip.setPixelColor(j-LEDSPERSTAIR, strip.Color(0,0,0,stepFade));
        strip.show();
        }
        delay(0);
    }
    delay(wait);
    wait = wait +20; // adds a bit time each step to have a nice effect
  }
}


void clearStrip(){
  // All pixels off
  for (int l=0; l<NUMPIXELS; l++){
    strip.setPixelColor(l, strip.Color(0,0,0,0));
  }
  strip.show(); // This sends the updated pixel's to the hardware.  
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants