0

NeoPixel Strip + NeoPatterns Problem when in reverse.
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

NeoPixel Strip + NeoPatterns Problem when in reverse.

by IAmOrion on Mon Aug 12, 2019 7:45 pm

Hi,

I suspect I'm having a senior moment, but I'm having issues with NeoPatterns - specifically when it's in REVERSE direction.

I have a NeoPixel strip of 20 pixels. If I want to animate half the strip, i can, for example set
Code: Select all | TOGGLE FULL SIZE
Index=10;
TotalSteps = numPixels();

and the animation works as expected. It correctly colorwipes from Pixel 10 to pixel 20 (being the last pixel).

The problem I'm having if I want to reverse the effect, so now, pixels 9 to 0 should animate - except it never reaches Pixel 0 no matter how many steps I make it :(

What am I doing wrong?

I've tried all sorts and can't fathom it out - I've add/removed/changed code countless times. I added 2 effect, LEFT_TURN and RIGHT_TURN.

Here's where my code is at currently:

Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_NeoPixel.h>

// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, LEFT_TURN, RIGHT_TURN, HAZARDS };
// 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 - 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;
    }
   
    // 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;
                case LEFT_TURN:
                    LeftTurnUpdate();
                    break;
                case RIGHT_TURN:
                    RightTurnUpdate();
                    break;
                case HAZARDS:
                    HazardsUpdate();
                    break;
                default:
                    break;
            }
        }
    }
 
    // 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
                }
            }
        }
    }
   
    // Reverse pattern direction
    void Reverse()
    {
        if (Direction == FORWARD)
        {
            Direction = REVERSE;
            Index = TotalSteps-1;
        }
        else
        {
            Direction = FORWARD;
            Index = 0;
        }
    }
   
    // Initialize for a RainbowCycle
    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = RAINBOW_CYCLE;
        Interval = interval;
        TotalSteps = 255;
        Index = 0;
        Direction = dir;
    }
   
    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
        }
        show();
        Increment();
    }

    // Initialize for a Theater Chase
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = THEATER_CHASE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
   }
   
    // Update the Theater Chase Pattern
    void TheaterChaseUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            if ((i + Index) % 3 == 0)
            {
                setPixelColor(i, Color1);
            }
            else
            {
                setPixelColor(i, Color2);
            }
        }
        show();
        Increment();
    }

    // Initialize for a ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = COLOR_WIPE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color;
        Index = 0;
        Direction = dir;
    }
   
    // Update the Color Wipe Pattern
    void ColorWipeUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }
   
    // Initialize for a SCANNNER
    void Scanner(uint32_t color1, uint8_t interval)
    {
        ActivePattern = SCANNER;
        Interval = interval;
        TotalSteps = (numPixels() - 1) * 2;
        Color1 = color1;
        Index = 0;
    }

    // Update the Scanner Pattern
    void ScannerUpdate()
    {
        for (int i = 0; i < numPixels(); i++)
        {
            if (i == Index)  // Scan Pixel to the right
            {
                 setPixelColor(i, Color1);
            }
            else if (i == TotalSteps - Index) // Scan Pixel to the left
            {
                 setPixelColor(i, Color1);
            }
            else // Fading tail
            {
                 setPixelColor(i, DimColor(getPixelColor(i)));
            }
        }
        show();
        Increment();
    }
   
    // Initialize for a Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = FADE;
        Interval = interval;
        TotalSteps = steps;
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
    }
   
    // Update the Fade Pattern
    void FadeUpdate()
    {
        // Calculate linear interpolation between Color1 and Color2
        // Optimise order of operations to minimize truncation error
        uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
        uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
        uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
       
        ColorSet(Color(red, green, blue));
        show();
        Increment();
    }

    void LeftTurn(uint32_t color1, uint8_t interval)
    {
        ActivePattern = LEFT_TURN;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Index = numPixels()/2;
    }

    void LeftTurnUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }

    void RightTurn(uint32_t color1, uint8_t interval)
    {
        ActivePattern = RIGHT_TURN;
        Direction = REVERSE;
        Interval = interval;
        TotalSteps = numPixels()/2;
        Color1 = color1;
        Index = (numPixels()/2)-1;
    }

    void RightTurnUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }

    void Hazards()
    {
     
    }

    void HazardsUpdate()
    {
     
    }
   
    // Calculate 50% dimmed version of a color (used by ScannerUpdate)
    uint32_t DimColor(uint32_t color)
    {
        // Shift R, G and B components one bit to the right
        uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
        return dimColor;
    }

    // Set all pixels to a color (synchronously)
    void ColorSet(uint32_t color)
    {
        for (int i = 0; i < numPixels(); i++)
        {
            setPixelColor(i, color);
        }
        show();
    }

    // Returns the Red component of a 32-bit color
    uint8_t Red(uint32_t color)
    {
        return (color >> 16) & 0xFF;
    }

    // Returns the Green component of a 32-bit color
    uint8_t Green(uint32_t color)
    {
        return (color >> 8) & 0xFF;
    }

    // Returns the Blue component of a 32-bit color
    uint8_t Blue(uint32_t color)
    {
        return color & 0xFF;
    }
   
    // Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos)
    {
        WheelPos = 255 - WheelPos;
        if(WheelPos < 85)
        {
            return Color(255 - WheelPos * 3, 0, WheelPos * 3);
        }
        else if(WheelPos < 170)
        {
            WheelPos -= 85;
            return Color(0, WheelPos * 3, 255 - WheelPos * 3);
        }
        else
        {
            WheelPos -= 170;
            return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
        }
    }
};

void Ring1Complete();
void Ring2Complete();
void StickComplete();

// Define some NeoPatterns for the two rings and the stick
//  as well as some completion routines
NeoPatterns Ring1(24, 5, NEO_GRB + NEO_KHZ800, &Ring1Complete);
NeoPatterns Ring2(16, 6, NEO_GRB + NEO_KHZ800, &Ring2Complete);
NeoPatterns Stick(20, 2, NEO_GRB + NEO_KHZ800, &StickComplete);

// Initialize everything and prepare to start
void setup()
{
  Serial.begin(115200);

   pinMode(8, INPUT_PULLUP);
   pinMode(9, INPUT_PULLUP);
   
    // Initialize all the pixelStrips
    Ring1.begin();
    Ring2.begin();
    Stick.begin();
    Stick.ColorSet(Stick.Color(0,0,0));
   
    // Kick off a pattern
    Ring1.TheaterChase(Ring1.Color(255,255,0), Ring1.Color(0,0,50), 100);
    Ring2.RainbowCycle(3);
    Ring2.Color1 = Ring1.Color1;
    //Stick.Scanner(Ring1.Color(255,0,0), 55);
   
    //Stick.ColorWipe(Stick.Color(255,0,0),25);
    //Stick.Index=20;
    //Stick.ColorWipe(Stick.Color(255,0,0),125, REVERSE);

    //Stick.LeftTurn(Stick.Color(255,0,0),25);
    Stick.RightTurn(Stick.Color(255,0,0),999);
}

// Main loop
void loop()
{
    // Update the rings.
    Ring1.Update();
    Ring2.Update();   
   
    // Switch patterns on a button press:
    if (digitalRead(8) == LOW) // Button #1 pressed
    {
        // Switch Ring1 to FADE pattern
        Ring1.ActivePattern = FADE;
        Ring1.Interval = 20;
        // Speed up the rainbow on Ring2
        Ring2.Interval = 0;
        // Set stick to all red
        Stick.ColorSet(Stick.Color(255, 0, 0));
    }
    else if (digitalRead(9) == LOW) // Button #2 pressed
    {
        // Switch to alternating color wipes on Rings1 and 2
        Ring1.ActivePattern = COLOR_WIPE;
        Ring2.ActivePattern = COLOR_WIPE;
        Ring2.TotalSteps = Ring2.numPixels();
        // And update tbe stick
        Stick.Update();
    }
    else // Back to normal operation
    {
        // Restore all pattern parameters to normal values
        Ring1.ActivePattern = THEATER_CHASE;
        Ring1.Interval = 100;
        Ring2.ActivePattern = RAINBOW_CYCLE;
        Ring2.TotalSteps = 255;
        Ring2.Interval = min(10, Ring2.Interval);
        // And update tbe stick
        Stick.Update();
    }   
}

//------------------------------------------------------------
//Completion Routines - get called on completion of a pattern
//------------------------------------------------------------

// Ring1 Completion Callback
void Ring1Complete()
{
    if (digitalRead(9) == LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring2
        Ring2.Interval = 40;
        Ring1.Color1 = Ring1.Wheel(random(255));
        Ring1.Interval = 20000;
    }
    else  // Retrn to normal
    {
      Ring1.Reverse();
    }
}

// Ring 2 Completion Callback
void Ring2Complete()
{
    if (digitalRead(9) == LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring1
        Ring1.Interval = 20;
        Ring2.Color1 = Ring2.Wheel(random(255));
        Ring2.Interval = 20000;
    }
    else  // Retrn to normal
    {
        Ring2.RainbowCycle(random(0,10));
    }
}

// Stick Completion Callback
void StickComplete()
{
    // Random color change for next scan
    Stick.Color1 = Stick.Wheel(random(255));
    if (Stick.ActivePattern == LEFT_TURN) {
      if (Stick.getPixelColor(19) == 0) {
        Stick.Color1 = Stick.Color(255,0,0);
      } else {
        Stick.Color1 = Stick.Color(0,0,0);
      }
      Stick.Index=10;
    }
   
    if (Stick.ActivePattern == RIGHT_TURN) {
      if (Stick.getPixelColor(9) == 0) {
        Stick.Color1 = Stick.Color(255,0,0);
      } else {
        Stick.Color1 = Stick.Color(0,0,0);
      }
      Stick.Index=9;
    }
}


LEFT_TURN works perfectly as expected, and Pixels 10 up to 20 all turn on (So 10 pixels in total light up), but RIGHT_TURN which counts down as it's in reverse never turns on Pixel 1 (aka Pixel 0). So only 9 pixels turn on, the 10th (Pixel 0) doesn't.

(All Pixel work correctly and will all light up etc so it's NOT a duff pixel)

IAmOrion
 
Posts: 54
Joined: Thu May 14, 2015 8:22 am

Re: NeoPixel Strip + NeoPatterns Problem when in reverse.

by adafruit_support_bill on Tue Aug 13, 2019 8:57 am

Try:

Code: Select all | TOGGLE FULL SIZE
    void RightTurn(uint32_t color1, uint8_t interval)
    {
        ActivePattern = RIGHT_TURN;
        Direction = REVERSE;
        Interval = interval;
        TotalSteps = numPixels()/2;
        Color1 = color1;
        Index = (numPixels()/2);
    }

    void RightTurnUpdate()
    {
        Increment();  // pre-decrement when counting down
        setPixelColor(Index, Color1);
        show();
    }

adafruit_support_bill
 
Posts: 74058
Joined: Sat Feb 07, 2009 10:11 am

Please be positive and constructive with your questions and comments.