Common Code

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
                }
            }
        }
    }
Last updated on 2015-06-14 at 10.11.16 AM Published on 2015-03-02 at 12.29.14 PM