NaMaLiCS Tutorial Part 8, G_Fade_OnOff

Well...it's been an interesting week of development for NaMaLiCS.  When I added the fade effects, the wheels fell off and I've had to rethink, redesign and recode core parts of the program.

Change #1:

The biggest change was made in the loop() section. To keep pixel effects from crashing into each other, I had to isolate the effects function calls so one isn't still running when another one starts.

Previously I had a series of for-loops where the delay start time got larger for subsequent loops and overwrote the effects of earlier loops.

  // Sequentially turn off each pixel
  for( pixelNumber = 1; pixelNumber <= totalPixels; pixelNumber++ ) {
    toggleOff( pixelNumber, fxEffectStart + (pixelNumber * fxOffset) );
  }

  // Update the start time for the next effect
  fxEffectStart += (pixelNumber - 1) * fxOffset;

  // Sequentially turn on each pixel
  for( pixelNumber = 1; pixelNumber <= totalPixels; pixelNumber++ ) {
    toggleOn( pixelNumber, fxEffectStart + (pixelNumber * fxOffset) );
  }

  // Update the start time for the next effect
  fxEffectStart += (pixelNumber - 1) * fxOffset;

  //  Sequentially turn off each pixel in reverse
  for( pixelNumber = 1; pixelNumber <= totalPixels; pixelNumber++ ) {
    toggleOff( ((totalPixels+1) - pixelNumber),
                fxEffectStart + (pixelNumber * fxOffset) );
  }

This worked fine when switching pixels on and off with the blink and toggle effects, because the changes to the pixels were instantaneous.  But when I added the fade effect, which occurs over a period of time, you would still be in the middle of a fade when a toggle effect would kick in.  Then it became a competition for control of the pixel...fade, toggle, fade, toggle...but it was happening so fast, it merely looked like a very dim light.

Now, each effect is isolated by an if statement that provides time boundaries for when the effect can occur.  If the current time is outside the time slot, the effect function call gets passed over.

  // sequentially toggle off the pixels
  if( currentMillis >= fxDelay && currentMillis < fxDelay +(fxInterval * 9) ) {
    for( uint8_t i = 0; i <= 8; i++ ) {
      toggleOff( i+1, fxDelay + (fxInterval * i) );
    }
  }

  // update the start time for the next effect
  fxDelay += fxInterval * 9;

  // reverse sequential toggle on
  if( currentMillis >= fxDelay && currentMillis < fxDelay +(fxInterval * 9) ) {
    for( uint8_t i = 0; i <= 8; i++ ) {
      toggleOn( 9-i, fxDelay + (fxInterval * i) );
    }
  }

  // update the start time for the next effect
  fxDelay += fxInterval * 9;

  // reverse sequential toggle off
  if( currentMillis >= fxDelay && currentMillis < fxDelay +(fxInterval * 9) ) {
    for( uint8_t i = 0; i <= 8; i++ ) {
      toggleOff( 9-i, fxDelay + (fxInterval * i) );
    }
  }

  // update the start time for the next effect
  fxDelay += fxInterval * 9;

  // sequentially fade on the pixels
  if( currentMillis >= fxDelay && currentMillis < fxDelay +(fxInterval * 9) ) {
    for( uint8_t i = 0; i <= 8; i++ ) {
      fadeOn( i+1, fxDelay + (fxInterval * i) );
    }
  }

Change #2:

Previously with the blink effect, the amount of time a pixel was off was the same as the time it was on.  Easier to code, but not near as interesting to see or flexible to use.

The blink code would flip the pixel brightness from on to off and off to on after a certain amount of time had elapsed.  The values for the blink interval were held in a global array, blinkInterval{}.

  // Check to see if enough time as elapsed to change the state of the
  //  pixel from on to off or off to on
  if( currentMillis - previousPixelMillis[plugIdentifier - 1] >=
      blinkInterval[(plugIdentifier - 1) % (totalPixels/2)] )
  {
    // Change the state of the pixel
    if( pixelNumber == 0 ) pixel01 ^= preferredBrightness;

The code update adds a second array.  Now there is a set of values for how long a pixel will be illuminated and a different set of values for how long it will be off.  The two arrays are onDuration{} and offDuration{}.  The trick here is you add the on and off times together to get the period, or amount of time for a full blink.  Then, when checking a blink, you use modulo arithmatic to calculate if current time would be in the on portion or the off portion of the blink and set the brightness value accordingly.

  // get the amount of time for a full on/off blink cycle
  unsigned long period = onDuration[plugIdentifier - 1] +
             offDuration[plugIdentifier - 1];

  if( pixelNumber == 0 ) {
    // Check if the current time is in the on part of the blink
    //  and if the pixel is on
    if( ((millis() % period) >= onDuration[plugIdentifier - 1] ) &&
            (pixel01 == preferredBrightness) ) {
      // turn the pixel off
      pixel01 = minBrightness;
    }
    // Else check if the current time is in the off part of the blink
    //  and if the pixel is off
    else if( ((millis() % period) >= offDuration[plugIdentifier - 1] ) &&
            (pixel01 == minBrightness) ) {
      // turn the pixel on
      pixel01 = preferredBrightness;
    }
  }

The new blink effect in action.  You notice the pixels are off longer than they are on.

 

 

New code:

Once I implemented isolating the pixel effects in the loop(), writing the fade on and off became extremely simple.  The fade on code:

  if( pixelNumber == 0 && pixel01 < preferredBrightness) pixel01++;
  if( pixelNumber == 1 && pixel02 < preferredBrightness) pixel02++;
  if( pixelNumber == 2 && pixel03 < preferredBrightness) pixel03++;

checks to see if the pixel is at its brightest value, if not, it increments the value by one.

The fade off code:

  if( pixelNumber == 0 && pixel01 > minBrightness) pixel01--;
  if( pixelNumber == 1 && pixel02 > minBrightness) pixel02--;
  if( pixelNumber == 2 && pixel03 > minBrightness) pixel03--;

checks to see if the pixel is off, if not, it decrements the value by one.

The entire fade on / fade off function is below.  You will notice at the end of each effect there is a one millisecond delay,

delay(1);

I added that because the fade happened so fast, it looked no different than a toggle on or off.  I will probably change it to a timing loop to remove the blocking a delay causes, but for now, I can live with a one millisecond delay.

The fade effect in action.

 

Full fade code:

// -------- FADE ON --------------

void fadeOn( uint8_t plugIdentifier, unsigned long delayEffectStart ) {

  // Calculate the equivalent NeoPixel triplet the plug is in
  uint8_t pixelSetNumber = (plugIdentifier - 1) / 3;

  // Calculate the equivalent LED in the NeoPixel triplet the plug
  //  corresponds to
  uint8_t pixelNumber = (plugIdentifier - 1) % 3;

  // Get the current brightness of the pixels in the triplet
  uint32_t color = pixels.getPixelColor( pixelSetNumber );

  // Get the individual brightness values of the three pixels in the triplet
  uint8_t pixel01 = (uint8_t)( (color >> 16) & 0xff ),
          pixel02 = (uint8_t)( (color >> 8) & 0xff ),
          pixel03 = (uint8_t)( color & 0xff) ;

  // Check to see if enough time has elapsed to start the blink effect
  if( currentMillis - startMillis >= delayEffectStart ) {

    if( pixelNumber == 0 && pixel01 < preferredBrightness) pixel01++;
    if( pixelNumber == 1 && pixel02 < preferredBrightness) pixel02++;
    if( pixelNumber == 2 && pixel03 < preferredBrightness) pixel03++;

    // Update the pixel triplet with the new values
    color = pixels.Color( pixel01, pixel02, pixel03 );
    pixels.setPixelColor( pixelSetNumber, color );
  }

  delay( 1 );
}

// -------- FADE OFF --------------

void fadeOff( uint8_t plugIdentifier, unsigned long delayEffectStart ) {

  // Calculate the equivalent NeoPixel triplet the plug is in
  uint8_t pixelSetNumber = (plugIdentifier - 1) / 3;

  // Calculate the equivalent LED in the NeoPixel triplet the plug
  //  corresponds to
  uint8_t pixelNumber = (plugIdentifier - 1) % 3;

  // Get the current brightness of the pixels in the triplet
  uint32_t color = pixels.getPixelColor( pixelSetNumber );

  // Get the individual brightness values of the three pixels in the triplet
  uint8_t pixel01 = (uint8_t)( (color >> 16) & 0xff ),
          pixel02 = (uint8_t)( (color >> 8) & 0xff ),
          pixel03 = (uint8_t)( color & 0xff) ;

  // Check to see if enough time has elapsed to start the blink effect
  if( currentMillis - startMillis >= delayEffectStart ) {

    if( pixelNumber == 0 && pixel01 > minBrightness) pixel01--;
    if( pixelNumber == 1 && pixel02 > minBrightness) pixel02--;
    if( pixelNumber == 2 && pixel03 > minBrightness) pixel03--;

    // Update the pixel triplet with the new values
    color = pixels.Color( pixel01, pixel02, pixel03 );
    pixels.setPixelColor( pixelSetNumber, color );
  }
  delay( 1 );
}

Where we stand now:

Blink, Toggle on, Toggle off, reverse Toggle on, reverse Toggle off, Fade on, Fade off, reverse Fade on, reverse Fade off...and a glitch that I didn't notice when making the video.  Oh well...

 

 

Next tutorial, the last of the effects...flickering on and off.  H_FLICKER_OnOff.

-wrtyler-

3replies Oldest first
  • Oldest first
  • Newest first
  • Active threads
  • Popular
    • Josh
    • BCFR, BCEMS, CEFR, NMCSO
    • Josh
    • 3 yrs ago
    • Reported - view

    That fade effect is very smooth, looks awesome!

    Like
    • WRTyler
    • -the National Mall guy-
    • wrTyler
    • 3 yrs ago
    • Reported - view

    Thanks!  It means a lot coming from someone who knows about what goes on behind-the-scenes.

    All this work, which looks good in the testbed, has to look good in the actual MOC.  That'll be the ultimate test.  That's where Rob and Brickstuff have it over me.  They produce good looking, working products.

    Maybe...one day...

    Like
    • Josh
    • BCFR, BCEMS, CEFR, NMCSO
    • Josh
    • 3 yrs ago
    • Reported - view

    WRTyler Programming lighting systems is not easy that's for sure...It is just the beginning though, wait till you start designing your own circuit boards. I went down that rabbit trail and I tell ya what, programming is the easy part.

    Like
Like1 Follow
  • 1 Likes
  • 3 yrs agoLast active
  • 3Replies
  • 15Views
  • 2 Following