NaMaLiCS Tutorial Supplement 2, pixelState (or...are we there yet?)
This is an updated version of the B_GLOBALS tab. Sorry, no pictures or video this discussion, just dry text.
// = = = = = = = = = = = = = = = = = = = = = = = = = // NOTE: I refer to individual pico lights as pixels // = = = = = = = = = = = = = = = = = = = = = = = = = // -------- LIBRARIES -------------- #include <Adafruit_NeoPixel.h>
I have included some macros to make it easier to work with the new pixelState concept. These access the pixelState variable at the bit level. You don't need to understand the logic behind the scenes, just how to how to use them to read and write values to pixels. We'll see how these get used in the next tutorial.
// -------- MACROS -------------- #define pixelBitRead(value, bit) (((value) >> (bit)) & 0x01) #define pixelBitSet(value, bit) ((value) |= (1UL << (bit))) #define pixelBitClear(value, bit) ((value) &= ~(1UL << (bit))) #define pixelBitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit):bitClear(value, bit) // -------- CONSTANTS (won't change) -------------- // BrickPixel controls are on Pin 7 & 8 // Pin 7 = PIXEL 1, Pin 8 = PIXEL 2 #define PIN 7 // Minimum and maximum brightness values a pixel can have #define minBrightness 0 #define preferredBrightness 30 #define maxBrightness 255 // The AdaFruit software is designed to work with triplets of lights (R,G & B) // The Brickstuff hardware is designed to work with individual single color // lights (what I refer to as pixels). So, one Adafruit light is the same as // three Brickstuff lights, or three pixels. // // This is handled by the NUMPIXELS definition. // // To calculate the NUMPIXELS, take the total number of Brickstuff lights that // are connected and divide by 3. If there is any remainder, increase // NUMPIXELS by 1. // // In this example there are 20 Brickstuff lights attached. // 20/3 = 6 with a remainder of 2. So, 6 for the quotient + 2 for the remainder. #define NUMPIXELS 7 // Declare a NeoPixel object. This is used to control the individual lights. Adafruit_NeoPixel pixels ( NUMPIXELS, PIN, NEO_RGB + NEO_KHZ800 ); // Argument 1 = Number of triplet groups of lights // Argument 2 = Arduino pin number of the BrickPixel socket being used // Argument 3 = Pixel type flags, add together as needed: // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) // NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/ WS2812 LEDs) // NEO_KHZ800 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // Stores the value of millis() when the program is started unsigned long startMillis = 0; // Interval at which to blink in milliseconds // Random Prime Numbers so the blinks aren't synchronous const unsigned long blinkInterval[] = {710, 371, 563, 281, 809, 151, 640, 173, 440, 599}; // Number of millisecs that pixels are on when blinking const unsigned long blinkDuration = 500; const unsigned long flickerDuration = 100;
What used to be here was a variable called loopCounter, which was a temporary fix for making sure all the initial light animations were completed before changing over to
the maintenance animations. loopCounter is gone! Instead, the concept of a pixelState is used.
// Values used to create two different flicker rates unsigned long flickerInterval01 = 4000; unsigned long flickerInterval02 = 2500; // -------- VARIABLES (will change) -------------- // Generally, you should use "unsigned long" for variables that hold time // The value will quickly become too large for an int to store // stores the value of millis() in each iteration of loop() unsigned long currentMillis = 0; // Used for flickering lights when they are turned on so they don't // flicker forever uint8_t incrementCounter = 0; // Will store last time the pixel was updated // Used in blinks and flickers on a BrickPixel 9-LED adapter unsigned long previousPixelMillis[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Values used by pixels to create a flickering effect unsigned long flickerMillis01 = 0; unsigned long flickerMillis02 = 0; bool flickerSwitch01 = false; bool flickerSwitch02 = false;
Previously I was using a specified number of times through the portion of loop() that handled the the initial light animations before changing over to the maintenance animations. This worked fine when I had a set number of pixels that didn't change. But each time more pixels were added the number of times through the loop had to be tweaked.
Now each lighting effect has an associated flag that is OFF when the initial effect is in process and changed to ON when the effect is finished. The variable pixelState keeps tabs on which pixels are done and which are still going through the initial animation.
By checking the value of pixelState, the programs knows exactly when all the pixels are finished.
The pixelState variable has one bit for every pixel. Initially the value of pixelState is zero...none of the initial lighting effects have finished. When every bit has a value
of one, then the startup portion of NaMaLiCS is over and it's time to switch to the maintenance section.
Currently there are 20 pixels in the Mall testbed, so the final pixelState will have 20 bits that can be set to OFF or ON. In binary the initial of value of pixelState value will be: 0000 0000 0000 0000 0000 and the final value 1111 1111 1111 1111 1111. When the first pixel has finished its initial animation the value becomes 0000 0000 0000 0000 0001. When the second pixel finishes the value is 0000 0000 0000 0000 0011 and so on.
Instead of writing out pixelState as a binary number, it's more convenient to express it as a hexadecimal. The final value is written as 0x0FFFFF.
Now, each time we go through loop() we check to see if pixelState = 0x0FFFFF. To make the code easier understand, there are two variables call allPixelsOff and allPixelsOn that have the beginning and ending values that pixelState can have. There are also two variables, pixelOff and pixelOn, used to indicate that if a particular effect is finished or not.
// Values used by pixelState to know when a pixel effect // has finished its startup uint32_t allPixelsOff = 0x00; uint32_t allPixelsOn = 0x0FFFFF; uint32_t pixelState = 0x00; uint32_t pixelOff = 0x00; uint32_t pixelOn = 0x01;
But wait, you say, pixelState can only handle 32 bits which means only 32 pixels. Yes...there will be a pixelState02 so that 64 pixels can be tracked. However, if the Mall testbed I've got setup is representative of the other sections in the Mall, those 64 pixels could control somewhere in the vicinity of 2200 lights, and I'm projecting only 1200. Frankly, I'd run out of power before I'd run out of tracking bits in pixelState.
Next, we change from the high level control and get to the nitty gritty turning lights on and off. First...we blink. 😐😑😐 The E_BLINKS tab is coming up.
-
Rob Klingberg at some point when we meet up again...and you have the time...you'll have to tell me how you got from being an English major to running a company that specializes in miniature lighting systems.
I'm just imagining this very 1950's era conversation in my head.
Rob: Mom...Dad, I've decided to major in English.
Mom: That's nice dear.
Dad: Um, Rob? What are you going to do with an English degree after you graduate?
Rob: I'm going to start my own company that specializes in electronics for miniatures. Specifically, lighting systems for LEGO.
Dad & Mom: That's wonderful! We're so glad you have a plan!// =====
I have a tendancy to accidentally fall backwards into something really interesting and then I wind up doing a deep dive into whatever it might be. If I were really brave I would be implementing an OOP library for NaMaLiCS...and trust me, I've thought about it. But that is FAR beyond my current skills and knowledge.
Guess I'm a natural born teacher. Love to share what I learn.
-
WRTyler I agree you are indeed a natural teacher! And I'm grateful for it.
RE: my story, it's far less interesting than you think. 😄 Starts with a kid who grew up loving working on Lionel trains with his dad, loved electronics in the 1970s (when everything was analog), hated math (so did not pursue a CompSci degree), majored in English, worked in publishing and then technical sales for years, and finally switched to running this business. I've always been a tinkerer, just never had the math to back up an engineering life, so now I make due as an engineering imposter. 🧐
It's always fascinating when parents bring their kids up to me at LEGO shows, seeing what I do and what we sell, and tell their kids, "See, you need to stick with math so you can become an engineer like this man!" and I have to break their hearts by telling them their kids would be best suited going the liberal arts route. 😈