NaMaLiCS Tutorial Part 6, E_BLINK
The Prologue:
If you've made it this far in the tutorial series…CONGRATULATIONS!! and…I apologize. The thought struck me today that the audience I've been writing for is…me…or at least folks like me. People who have a technical streak and some familiarity with math, physics, electronics, programming, but are really more comfortable creating art and building with LEGO.
If you don't happen to fall into that mindset, then these tutorials have either been far too basic and you've deemed them not worth your attention or far too difficult and you don't understand what I'm going on about. If you fall into the first category, I'm trying my darndest to catch up to your level. If you fall into the latter category...PLEASE get in touch with me and I'll try to explain more clearly because I want you to catch up (and surpass) my level and I want to improve these tutorials.
The Exposition:
Don't blink...you might miss it. Let's take a look at the E_BLINKS tab. Please keep in mind that there many different ways to make a pixel blink, this is just my implementation. Also keep in mind, I am NOT a professional programmer, so this is probably not the best approach. It just happens to work for me.
Here is the current configuration of the National Mall testbed:
The backstory:
Just to reiterate. In the first tutorial I talked about programming pixels, not lights. This shows the reason for doing that.
In the middle of the image is the High-power adapter board. It has 3 pixels, but you'll notice that there are banks of lights connected to each pixel. Pixel 1 has 9 lights attached. Pixel 3 has 13 lights attached.
The next board, the 9-LED Control Board has 9 pixels. Each one of those pixels has just 1 light attached. So a pixel can be one or more actual lights, but when programming the BrickPixel system, the lighting effects are sent to a pixel. In this case, if pixel 1 has a blink effect, the bank of nine lights will blink in unison. If pixel 3 has a fade up effect, all 13 lights will fade to full brightness simultaneously.
Okay, back to blinking pixels. This is the configuration of my BrickPixel testbed.
Act 1:
All the effects in NaMaLiCS are called in the same way:
[effectName]( plugIdentifier, delayEffectStart )
Effect names are things like toggleOn, toggleOff, fadeOn, flickerOn and the like. But each effect takes two parameters.
// Make a pixel blink // Values passed into the function are the number of the plug in the light // chain to which the pixel is attached and the amount of delay before // the blink effect starts void blinkPixel( uint8_t plugIdentifier, unsigned long delayEffectStart ) {
The first parameter (plugIdentifier) is the socket number of the pixel. The numbering start at 1, which is the pixel closest to the Arduino, and increases sequentially as you move down the string of pixels. The second parameter (delayEffectStart) is how long to delay (in milliseconds) the start of the lighting effect.
The function call in the loop() part of the program would look like:
blinkPixel( 7, 500);
The blink effect is called for pixel 7 and will be delayed by 500 milliseconds (1/2 second) before it starts.
The plugIdentifier parameter needs to be translated from the BrickPixel system to the Adafruit system. As I discussed in the NaMaLiCS Tutorial Part 3, B_GLOBALS tutorial, we are using the Adafruit NeoPixel library for controlling the pixels, and a single NeoPixel light contains 3 LEDs and is the equivalent of three pixels.
The translation comes in two parts. The first part determines which set of three LEDs (a single NeoPixel light) the pixel would be in if it were part of a NeoPixel. This is called a pixelSet.
IMPORTANT NOTE HERE! Remember that the BrickPixel system start counting at 1, but the Adafruit system starts counting at 0. That's why calculating the pixelSetNumber has (plugIdentifier - 1),
// Calculate which pixel triplet the plug is in uint8_t pixelSetNumber = (plugIdentifier - 1) / 3;
The second part is to determine which one of the three LEDs in a NeoPixel the pixel would correspond to. These are called pixel01, pixel02 and pixel03.
// Calculate which pixel it is in the triplet uint8_t pixelNumber = (plugIdentifier - 1) % 3;
By the way, the % operator is called modulo. A modulo operation returns the remainder left over from a divide operation. For example. 6/3 = 2 with no remainder. 7/3 = 2 with a remainder of 1. So 7%2 = 1.
To illustrate using the BrickPixel testbed layout, if the plugNumber is 6, the pixel is in pixelSet 2 and is the third pixel in the set (pixel03).
Then we grab the brightness value for the NeoPixel equivalent pixelSet and extract the individual brightness for each of the three LEDs.
// Get the current brightness of the pixels in the triplet uint32_t color = pixels.getPixelColor( pixelSetNumber );
And finally we assign a brightness value to each of the three pixels.
// 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);
Act 2:
Next, we check to see if enough time has gone by so we can start the effect.
// Check to see if enough time has elapsed to start the blink effect if( currentMillis - startMillis >= delayEffectStart ) {
If we are beyond the delayed start time, we need to check to see if its time to change the state of the pixel from off-to-on or on-to-off
// Check to see if enough time as elapsed to change the state of the // pixel from off to on or on to off. The duration of the on/off time // is defined in the blinkInterval variable found in the B_GLOBALS tab. if( currentMillis - previousPixelMillis[plugIdentifier - 1] >= blinkInterval[plugIdentifier - 1] ) {
If enough time has passed, flip the state of the pixel
// Change the state of the three pixels if( pixelNumber == 0 ) pixel01 ^= 0xff; if( pixelNumber == 1 ) pixel02 ^= 0xff; if( pixelNumber == 2 ) pixel03 ^= 0xff;
Combine the new brightness values for the pixels into a triplet so the Adafruit library can handle the information and send that number to the pixelSet. Then reset the timer for checking the blinkInterval.
// Update the pixel triplet with the new values pixels.setPixelColor( pixelSetNumber, pixels.Color(pixel01, pixel02, pixel03) ); // Reset the blink counter previousPixelMillis[plugIdentifier - 1] += (blinkInterval[(plugIdentifier - 1)%9] - delayEffectStart); } }
And as was discussed in NaMaLiCS Tutorial Supplement 2, pixelState we need to indicate that the pixels have successfully finished their initial animation, not that it makes a difference to the animation, because a blink effect has no begin or end point, it is always blinking.
// Update pixelState flag if( pixel01 == preferredBrightness ) pixelBitWrite( pixelState, (plugIdentifier-1), pixelOn)); if( pixel02 == preferredBrightness ) pixelBitWrite( pixelState, (plugIdentifier-1), pixelOn)); if( pixel03 == preferredBrightness ) pixelBitWrite( pixelState, (plugIdentifier-1), pixelOn)); }
Act 3:
The parameters to drive a blink effect are setup in two places. First, in the B_GLOBALS tab, there is an array of numbers which have the blinkInterval times. Here I have listed three different arrays of blinkInterval times. Only one of these should be placed in the B_GLOBALS tab.
// Interval at which to blink in milliseconds // Numbers to make lights blink in unison const unsigned long blinkInterval[] = {500, 500, 500, 500, 500, 500, 500, 500, 500}; // Numbers to make alternate lights blink const unsigned long blinkInterval[] = {500, 1000, 500, 1000, 500, 1000, 500, 1000, 500}; // Random Prime Numbers so the blinks aren't synchronous const unsigned long blinkInterval[] = {710, 371, 563, 281, 809, 151, 640, 173, 440, 599};
The other half of the blink effect is found in the D_LOOP tab. It's the actual calls for the blink effect for each pixel. The first set blink effects says that each of the 9 pixels will start blinking immediately and will either be in unison or at random intervals depending on how the blinkInterval array is specified in the B_GLOBALS tab.
// Blink effect call for blinks in unison and random blinks blinkPixel( 1, 0 ); blinkPixel( 2, 0 ); blinkPixel( 3, 0 ); blinkPixel( 4, 0 ); blinkPixel( 5, 0 ); blinkPixel( 6, 0 ); blinkPixel( 7, 0 ); blinkPixel( 8, 0 ); blinkPixel( 9, 0 );
Blinking in unison:
Blinking at random intervals:
This second set of blink effects says that every other pixel has a 1/2 second (500 milliseconds) delay. So if the blinkInterval is 500 milliseconds as listed in the blinkInterval array and every other pixel is delayed by 500 milliseconds, that means the pixels that got illuminated immediately will be turning off at the same time the delayed pixels are going to full brightness.
// Blink effect call for alternating blinks blinkPixel( 1, 0 ); blinkPixel( 2, 500 ); blinkPixel( 3, 0 ); blinkPixel( 4, 500 ); blinkPixel( 5, 0 ); blinkPixel( 6, 500 ); blinkPixel( 7, 0 ); blinkPixel( 8, 500 ); blinkPixel( 9, 0 );
Pixels blinking alternately:
Epilogue:
To create chaser lights or other more complex blinking sequences, the E_BLINKS code would need to be rewritten to allow for the amount of time a pixel is on to be different from the amount of time a pixel is off. Currently the blink effect has a pixel's on time and off time be the same.
Coming Attractions:
Next up, the lightswitch effect...the F_TOGGLE_OnOff tab.
-wrtyler-