In this tutorial we will re-engineer some of the more popular pixel patterns, including:
- Rainbow Cycle
- Color Wipe
- Theater Chase
- Scanner
- Fader
We'll encapsulate these patterns into a single "NeoPattern" class derived from the Adafruit_NeoPixel class so you can freely change between patterns on the same strip.
And, since NeoPattern is derived from Adafruit_NeoPixel, you can use all the normal funcitons from the NeoPixel library on it too!
Before we get into the pattern code imlementation. Let's take a look at what these patterns have in common, what we need to know to manage them. From that we can create a class structure that will handle them all:
Member Variables:
The class definition below contains member variables to contain the pattern in use, the directon it is running in and the current state of the pattern state machine.
Pro-Tip: The Direction option is handy when dealing with neopixel rings that have counter-clockwise vs clockwise pixel ordering.
These variables represent a tradeoff between speed and size. The per-pixel overhead is the same as for the Adafruit_Neopixel base class. Most of the pattern state variables are common to most of the patterns
Given the limited SRAM available on the Arduino, some calculations will be done 'on-the-fly' in the Update() functions and save the extra space for more pixels!
Callback on Completion:
If initialized, the "OnComplete()" callback function is invoked on completion of each iteration of the pattern. You can define the callback to perform any action - including making changes to the pattern and/or operating state. We'll show some examples of how this is used later.
#include <Adafruit_NeoPixel.h> // Pattern types supported: enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE }; // Patern directions supported: enum direction { FORWARD, REVERSE }; // NeoPattern Class - derived from the Adafruit_NeoPixel class class NeoPatterns : public Adafruit_NeoPixel { public: // Member Variables: pattern ActivePattern; // which pattern is running direction Direction; // direction to run the pattern unsigned long Interval; // milliseconds between updates unsigned long lastUpdate; // last update of position uint32_t Color1, Color2; // What colors are in use uint16_t TotalSteps; // total number of steps in the pattern uint16_t Index; // current step within the pattern void (*OnComplete)(); // Callback on completion of pattern
Constructor:
The constructor initializes the base-class, as well as an (optional) pointer to a callback function.
// Constructor - calls base-class constructor to initialize strip NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) :Adafruit_NeoPixel(pixels, pin, type) { OnComplete = callback; }
The Updater:
The Update() function works much like the Update() functions from part 1 of the series. It checks the elapsed time since the last update and returns immediately if it is not time to do anything yet.
If it determines that it is time do actualy update the pattern, it will look at the ActivePattern member variable to decide which pattern-specific update function to call.
For each of the listed patterns, we will need to write an initialization function and an Update() function. We'll show how to do this in the next few pages:
// Update the pattern void Update() { if((millis() - lastUpdate) > Interval) // time to update { lastUpdate = millis(); switch(ActivePattern) { case RAINBOW_CYCLE: RainbowCycleUpdate(); break; case THEATER_CHASE: TheaterChaseUpdate(); break; case COLOR_WIPE: ColorWipeUpdate(); break; case SCANNER: ScannerUpdate(); break; case FADE: FadeUpdate(); break; default: break; } } }
Incrementing the Pattern
The Increment() function looks at the current state of Direction and increments or decrements Index accordingly. When Index reaches the end of the pattern, it is wrapped around to restart the pattern. If the OnComplete() callback function is non-null then it is called.
// Increment the Index and reset at the end void Increment() { if (Direction == FORWARD) { Index++; if (Index >= TotalSteps) { Index = 0; if (OnComplete != NULL) { OnComplete(); // call the comlpetion callback } } } else // Direction == REVERSE { --Index; if (Index <= 0) { Index = TotalSteps-1; if (OnComplete != NULL) { OnComplete(); // call the comlpetion callback } } } }
Page last edited January 28, 2015
Text editor powered by tinymce.