2

Below is some code that I'm writing to concurrently run "animations" on the same strip of NeoPixels. When I try to make use of the Serial monitor to control the program functions, I just get a bunch of garbled garbage and often, a reboot. I have similar code to this that is actually smaller that runs from a Bluetooth module attached to the same Arduino and has worked for years. Is there some obvious memory issue I'm missing, could my Arduino's SRAM finally be starting to eat it?

#include "TripCycling.h"
#include "CycleLight.h"
#include "Alternate.h"
#include "Collision.h"
#include "FluidColor.h"
#include "ColorRandomizer.h"
#include "StreamAnimation.h"
#include "StreamControl.h"
#include <MemoryFree.h>

#define MAX_STRIPS 1
#define MAX_ANIMATIONS 6


#define control Serial

static Adafruit_NeoPixel** strips = new Adafruit_NeoPixel*[MAX_STRIPS];

static uint32_t colors[] = {LEDUtils::red, LEDUtils::orange, LEDUtils::yellow, LEDUtils::green, LEDUtils::blue, LEDUtils::purple};

static Animation** animations = new Animation*[MAX_ANIMATIONS];

static int8_t stripIDs[MAX_STRIPS];


static String temp = "";
static char val;
static String strings[12];


void setup() {

  Serial.begin(9600);
  Serial.println("Free Memory: " + String(freeMemory()));

  strips[0] = new Adafruit_NeoPixel(42, 9);
  stripIDs[0] = 1;

  strips[0]->begin();
  strips[0]->clear();
  strips[0]->show();


  control.println("Setup finished!");
}

void loop() {
  Serial.println("Free Memory: " + String(freeMemory()));

  if(control.available())
  {
    if(control.find('~'))
    {
      temp = control.readStringUntil('~');

      val = LEDUtils::split(strings, temp, ':');

      if(strings[0].equals("CLR"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          LEDUtils::clearSection(*strips[arrayPos], strings[2].toInt(), strings[3].toInt());

          if(strings[4].equals("True"))
          {
            strips[arrayPos]->show();
          }
        }
      }
      else if(strings[0].equals("ALL"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          LEDUtils::setAll(*strips[arrayPos], strings[2].toInt(), strings[3].toInt(), strings[4].toInt());
        }
      }
      else if(strings[0].equals("MAT"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          int8_t findValue = getNextAniPos();

          if(findValue == -1)
          {
            control.print("ERR:Max animations reached~");
          }
          else
          {
            uint16_t startPixel = strings[2].toInt();
            uint16_t stopPixel = strings[3].toInt();
            long updateTime = strings[4].toInt();
            uint16_t refNum = strings[5].toInt();

            uint32_t colors[val - 6];

            for(uint8_t i = 6; i < val; i++)
            {
              colors[i - 6] = strings[i].toInt();
            }

            animations[findValue] = new Alternate(*strips[arrayPos], refNum, startPixel, stopPixel, colors, val - 6, updateTime);
            animations[findValue]->start();
          }
        }
      }
      else if(strings[0].equals("CRZ"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          int8_t findValue = getNextAniPos();

          if(findValue == -1)
          {
            control.print("ERR:Max animations reached~");
          }
          else
          {
            uint16_t startPixel = strings[2].toInt();
            uint16_t stopPixel = strings[3].toInt();
            long updateTime = strings[4].toInt();
            boolean fade = strings[5].toInt() ? true : false; 
            uint16_t refNum = strings[6].toInt();

            uint32_t colors[val - 7];

            for(uint8_t i = 7; i < val; i++)
            {
              colors[i - 7] = strings[i].toInt();
            }

            animations[findValue] = new ColorRandomizer(*strips[arrayPos], refNum, startPixel, stopPixel, colors, val - 7, fade, updateTime);
            animations[findValue]->start();
          }
        }
      }
      else if(strings[0].equals("TCL"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          int8_t findValue = getNextAniPos();

          if(findValue == -1)
          {
            control.print("ERR:Max animations reached~");
          }
          else
          {
            uint16_t startPixel = strings[2].toInt();
            uint16_t stopPixel = strings[3].toInt();
            long updateTime = strings[4].toInt();
            uint32_t color1 = strings[5].toInt();
            uint32_t color2 = strings[6].toInt();
            uint16_t tripSize = strings[7].toInt();
            uint16_t refNum = strings[8].toInt();

            animations[findValue] = new TripCycling(*strips[arrayPos], refNum, startPixel, stopPixel, tripSize, color1, color2, updateTime);
            animations[findValue]->start();
          }
        }
      }
      else if(strings[0].equals("CLI"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          int8_t findValue = getNextAniPos();

          if(findValue == -1)
          {
            control.print("ERR:Max animations reached~");
          }
          else
          {
            uint16_t startPixel = strings[2].toInt();
            uint16_t stopPixel = strings[3].toInt();
            long updateTime = strings[4].toInt();
            uint32_t color1 = strings[5].toInt();
            uint32_t color2 = strings[6].toInt();
            uint16_t refNum = strings[7].toInt();

            animations[findValue] = new CycleLight(*strips[arrayPos], refNum, startPixel, stopPixel, color1, color2, updateTime);
            animations[findValue]->start();
          }
        }
      }
      else if(strings[0].equals("COL"))
      {
        int8_t arrayPos = getArrayPosByID(strings[1].toInt());

        if(arrayPos == -1)
        {
          control.print("ERR:Strip ID not found~");
        }
        else
        {
          int8_t findValue = getNextAniPos();

          if(findValue == -1)
          {
            control.print("ERR:Max animations reached~");
          }
          else
          {
            uint16_t startPixel = strings[2].toInt();
            uint16_t stopPixel = strings[3].toInt();
            long updateTime = strings[4].toInt();
            uint32_t color1 = strings[5].toInt();
            uint32_t color2 = strings[6].toInt();
            uint16_t refNum = strings[7].toInt();

            animations[findValue] = new Collision(*strips[arrayPos], refNum, startPixel, stopPixel, color1, color2, updateTime);
            animations[findValue]->start();
          }
        }
      }
    }
  }

  for(uint16_t i = 0; i < MAX_ANIMATIONS; i++)
  {
    if(animations[i]->getRefNum() != 65535 && animations[i]->isStarted())
    {
      animations[i]->update();
    }
  }

  delay(1);
}

int8_t getArrayPosByID(int8_t id)
{
  for(int8_t i = 0; i < MAX_STRIPS; i++)
  {
    if(stripIDs[i] == id)
    {
      return i;
    }
  }

  return -1;
}

int8_t getNextAniPos()
{
  for(int8_t i = 0; i < MAX_ANIMATIONS; i++)
  {
    if(animations[i]->getRefNum() == 65535)
    {
      return i;
    }
  }

  return -1;
}

Note that the includes above are all based on classes I designed to represent the animations, each one has some information in them like the strip to use (passed by reference), the starting pixel, stopping pixel, colors, how long there timers should wait before executing the next animation step, etc. And they are all subclasses of the parent class Animation. I cannot seem to find what is causing the garbling to happen, if I remove references to Serial and just hard-code animations in, everything works fine, and I have around 1KB of SRAM (out of 2K) for other stuff running 6 different animations on one strip of 42 LEDs. At this point I don't make it through the Free Memory println before stuff starts coming up weird and normally the Arduino will just hang and do nothing or keep transmitting the last garbled character over and over as fast as it can. Am I missing something?

4

In short:

  • Lots of new, no delete.
  • Constant strings like Serial.print("something") can be stored in flash memory by Flash Helper Serial.print(F("something")).
  • Maybe too wide color spectre. You can save 1Byte per pixel if you use struct with 8b R/G/B. (or you can use even 16b depth colors - but it is little bit harder).
  • And maybe, if you were storing some instructions only and prepare pixels into ledstrip directly, you can save a lot. However it takes some time to prepare it, so you have to count with it. Something like fill(color, from, to), gradient(...), move left/right...
  • Using Arduino framework is memory and cpu extensive too.

Anyway crashing as you described sounds like stack overflow or memory corruption.

  • See but that's the thing, I have code extremely similar to this, it makes use of BOTH SoftwareSerial and Serial to control a Bluetooth module and provide constant debug feedback when testing. That code runs no problem, this code, which is broken up into classes, not separate methods like the Bluetooth code is, is what's having the issue. In fact, the Bluetooth code on compile time uses more memory by default, I would think this code would at the very least run, because initially nothing beyond some pointers, text, and the same strip used for the Bluetooth code are being loaded into memory. – brad95411 Aug 30 '16 at 12:41
0

There was a very major issue with the amount of Strings I was storing in RAM. Moving Strings out of RAM and into PROGMEM using the F() macro has helped significantly. Going at things step by step helped me see where the issue was.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.