Variable value disappears

Hello! I am programming arduino UNO for controlling led strips in my room Object oriented way. Tried to make some libraries to understand how do they work, but after adding Adafruit_NeoPixel.h library and using it, my code started to behave strangely. Variables just lose their values.

I’m using PlatformIO IDE

Here’s code:

main.cpp

#include <Arduino.h>
#include <Formicarium.h>
#include <ButtonInput.h>
#include <Animations.h>
#include <Adafruit_NeoPixel.h>

#define NUM_LEDS 79

const byte redPin = 3;
const byte greenPin = 5;
const byte bluePin = 6;

const byte outLedPin = 9;
const byte ventPin = 10;

const byte ledPin = 4;

const byte topButtonPin = 7;
const byte botButtonPin = 8;
const byte potPin = 19;
const byte switchPin = 2;

byte rgb[3];

// AntVent antVent(ventPin, "Ant Vent", 0);
// AntLed antLed(outLedPin, "Ant LED", 1);

// Input input(topButtonPin, botButtonPin, potPin, switchPin, 150);
Animation *animation[2];

// Adafruit_NeoPixel strip(NUM_LEDS, ledPin, NEO_GRB + NEO_KHZ800);

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  

  // animation[0] = new SolidRainbow(10);
  // animation[0]->firstTime(rgb);

  animation[1] = new Fire(15, NUM_LEDS);
  animation[1]->firstTime(rgb);
}

void loop()
{
  // Serial.println(animation[0]->getDelay());
  // animation[0]->execute(rgb);
  // analogWrite(3, rgb[0]);
  // analogWrite(5, rgb[1]);
  // analogWrite(6, rgb[2]);

  animation[1]->execute(rgb);
}

Animations.h

#ifndef animations
#define animations
#include "Arduino.h"
#include "Adafruit_NeoPixel.h"

class Animation
{
public:
    Animation();                              //class constructor
    String name;                              //name of objecy
    void setDelay(int setDelayTime);          //sets delay for object
    unsigned long getTime();                  //returns oldTimeInt of object
    int getDelay();                           //returns current delayTime of object
    virtual void execute(byte rgbArr[3]) = 0; //executes
    virtual void firstTime(byte part[3]) = 0; //prepares for execution
    virtual ~Animation(){};                   //class destructor
protected:
    int _delayTime;
    unsigned long _oldTimeInt;
};

class SolidFade : public Animation
{ //inherited solid fade class
public:
    SolidFade(int startDelayTime);
    SolidFade(){};
    void execute(byte rgbArr[3]); //executes solid fade function
    void firstTime(byte part[3]); //prepares solid fade for execution
private:
    byte _rgbPart[3]; //stores values to fade
};

class SolidRainbow : public Animation
{ //inherited solid rainbow class
public:
    SolidRainbow(int startDelayTime);
    SolidRainbow(){};
    void execute(byte rgbArr[3]);
    void firstTime(byte part[3]); //if there were an animation other than solid rainbow running before, then this should be executed to start new cycle
private:
    byte _decColor;       //decreasing color of rainbow cycle
    byte _incColor;       //increasing color of rainbow cycle
    byte _rainbowCounter; //rainbow cycle counter
};

class SolidBlink : public Animation
{
public:
    SolidBlink(int startDelayTime);
    void execute(byte rgbArr[3]);
    void firstTime(byte part[3]);
private:
    byte _rgbPart[3];
    bool _onOffState;//saves last state of blink
};

class Fire : public Animation
{
public:
    Fire(int startDelayTime, byte numLeds);
    void execute(byte rgbArr[3]);
    void firstTime(byte part[3]);
private:
    byte _numLeds;
    Adafruit_NeoPixel _strip;
};

class Rainbow : public Animation
{
public:
    Rainbow(int startDelayTime);
    void execute(byte rgbArr[3]);
};

#endif

fire.cpp

#include "Arduino.h"
#include "Animations.h"
#include "Adafruit_NeoPixel.h"

Fire::Fire(int startDelayTime, byte numLeds)
{
  name = "Fire";
  _delayTime = startDelayTime;
  byte _numLeds = numLeds;
  
  Adafruit_NeoPixel _strip(numLeds, 4, NEO_GRB + NEO_KHZ800);
  _strip.begin();
  _strip.clear(); //notīra iepriekšējās krāsas no krāsu lentas
  _strip.show();  //apdeito krāsu lentu, lai iepriekšējās krāsas tiktu notīrītas
  _strip.fill(_strip.Color(255, 0, 0), 1, 79);
  Serial.println("start");
}

void Fire::execute(byte rgbArr[3])
{
  Serial.println(_numLeds);
  if (millis() - _oldTimeInt >= _delayTime)
  {
    _oldTimeInt = millis();
    byte Cooling = 55;
    byte Sparking = 120;
    byte heat[_numLeds];
    int cooldown;

    // Step 1.  Cool down every cell a little
    for (int i = 0; i < _numLeds; i++)
    {
      cooldown = random(0, ((Cooling * 10) / _numLeds) + 2);
      if (cooldown > heat[i])
      {
        heat[i] = 0;
      }
      else
      {
        heat[i] = heat[i] - cooldown;
      }
    }

    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for (int k = _numLeds - 1; k >= 2; k--)
    {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
    }

    // Step 3.  Randomly ignite new 'sparks' near the bottom
    if (random(255) < Sparking)
    {
      int y = random(7);
      heat[y] = heat[y] + random(160, 255);
      //heat[y] = random(160,255);
    }
    // Step 4.  Convert heat to LED colors
    for (int j = 0; j < _numLeds; j++)
    {
      // Scale 'heat' down from 0-255 to 0-191
      byte t192 = round((heat[j] / 255.0) * 191);

      // calculate ramp up from
      byte heatramp = t192 & 0x3F; // 0..63
      heatramp <<= 2;              // scale up to 0..252

      // figure out which third of the spectrum we're in:
      if (t192 > 0x80)
      { // hottest
        _strip.setPixelColor(j, 255, heatramp, 255);
      }
      else if (t192 > 0x40)
      { // middle
        _strip.setPixelColor(j, heatramp, 0, 255);
      }
      else
      { // coolest
        _strip.setPixelColor(j, 0, 0, heatramp);
      }
    }
    _strip.show();
  }
}

void Fire::firstTime(byte part[3]){
  
}

I want to create every led animation as object and store them in array so I could use the same function names, but in different objects, that way making my code shorter.

Fire is addressable led strip function, and it is the one that behaves strangely. When I initialize Fire object I make some variable values there, but when I try to use them in Fire::execute(), variables suddenly lose their values, both Adafruit_NeoPixel object AND other private variables.

Maybe it’s just too much to handle for UNO?

Aren't you supposed to create the neopixel object globally (it uses dynamic memory allocation) and then pass a reference to that when you create the fire object ? It's not my specialty.

both Adafruit_NeoPixel object AND other private variables

there is only one other privately declared variable _numleds, the rest are just local variables.

what controls the speed of you animations?

Deva_Rishi:
Aren’t you supposed to create the neopixel object globally (it uses dynamic memory allocation) and then pass a reference to that when you create the fire object ? It’s not my specialty.there is only one other privately declared variable _numleds, the rest are just local variables.

Yes, I did it previously, but it wasn’t working either, so I tried to create neopixel object inside Fire object.

Originally it was:

Fire.cpp:

Fire::Fire(int startDelayTime, byte numLeds, Adafruit_NeoPixel & stripObj)
{
  name = "Fire";
  _delayTime = startDelayTime;
  byte _numLeds = numLeds;
  
  Adafruit_NeoPixel _strip = stripObj;
  Serial.println("start");
}

Animations.h:

class Fire : public Animation
{
public:
    Fire(int startDelayTime, byte numLeds, Adafruit_NeoPixel & stripObj);
    void execute(byte rgbArr[3]);
    void firstTime(byte part[3]);
private:
    byte _numLeds;
    Adafruit_NeoPixel _strip;
};

main.cpp:

Adafruit_NeoPixel strip(NUM_LEDS, ledPin, NEO_GRB + NEO_KHZ800);

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  
  strip.begin();
  strip.clear(); //notīra iepriekšējās krāsas no krāsu lentas
  strip.show();  //apdeito krāsu lentu, lai iepriekšējās krāsas tiktu notīrītas

  // animation[0] = new SolidRainbow(10);
  // animation[0]->firstTime(rgb);

  animation[1] = new Fire(15, NUM_LEDS, strip);
  animation[1]->firstTime(rgb);
}

Where I made a global neopixel object and passed it’s reference to Fire.
You’ve said about dynamic memory and I think that maybe it’s the thing that causes this bug. Maybe I have to make neopixel object reference to Fire::execute() function, like Fire::execute(Adafruit_Neopixel & stripObj) so every time it gets called, Neopixel object current memory address gets accessed

gcjr:
what controls the speed of you animations?

When I create Animations object or any of it's derived objects I have to input delay time length.

animation[1] = new Fire(15, NUM_LEDS, strip);

This time Fire speed is 15 milliseconds

In Fire::execute() there is this line

  if (millis() - _oldTimeInt >= _delayTime)
  {
    _oldTimeInt = millis();
   /*code*/
}

It controls the speed of animations, and executes every _delayTime milliseconds

martinnc:
variables suddenly lose their values, both Adafruit_NeoPixel object AND other private variables.

You never assigned values to those variables, so it’s rather hard for them to loose something.

Read about scope (this could fix after understanding the loosing) and
initialization lists (this could help you to embed objects or references to objects).

Whandall:
You never assigned values to those variables, so it's rather hard for them to loose something.

Read about scope (this could fix after understanding the loosing) and
initialization lists (this could help you to embed objects or references to objects).

Thank you! I made a foolish mistake. Forgot about scopes. I thought that when you initalize object you have to write variables with their data types and that these variables would be saved in the same object scope.

You've said about dynamic memory and I think that maybe it's the thing that causes this bug.

Well it creates a buffer on the heap, but that is secure. Check out the neopixel.h to see what happens in there.
I have looked at the issue my self, since i have also quite library of function that manipulate the buffer and considered creating a library to hold them. It will look more neat. In the end i came to 2 options.

  • either i let the animations be created and just pass a reference to the buffer, make my own setPixelColor() functions and have the object global, and call .show() from the main program. Which is actually what i am doing now, just the animations are not in a separate class.
  • or, include the animations in the neopixel library (or vice versa) so it can be one big thing and show() can be called from within the animation. I have anyway added function to the neopixel library and i could also just add the animations.

For option 1 the animations should be non-blocking (which i anyway think they should be, and yours nearly are)
Option 2 is sort of what they've done in FastLED, though their animations are blocking and you have to declare the buffer yourself and pass a reference to it.

nb. The strangest thought occurred to my while writing this, you could create a neopixel object, Extract the reference to the buffer and pass it to the FastLED object on creation. I have no purpose for that, but it was a thought. "you can do that, but why ?"