Code uploads but only works part of the time (suspected memory issue)

Hi all - I hope I picked the right sub-forum to post in.

I am quite dumbfounded at the issue I’m experiencing using a MEGA2560. I am working on an application that controls regions of LEDs based on commands sent from a Raspberry Pi. I wrote several behaviors (methods) for the LEDs which work individually. However, after writing additional methods, the code would indicate uploading correctly, but none of the regions would light or respond to serial messages. If I do something as simple as commenting out one of the method calls in the switch statements (any of them - not a specific one), the code will begin to run properly again. I have been able to debug every method this way to ensure that they work on their own.

Additional things I’ve noticed: The L light is solid when the code works. It flashes when the code does not work. I am not doing anything with pin 13. I’m sure this is a good clue into the problem I’m experiencing, but all I can find when I look this up is beginners trying to use the “blink” program.

What I’ve tried: I replaced the LEDs and uploaded to a different board, which did not fix the problem. I also used the memory free library to check the RAM and I have ~6900 bytes free at any given time throughout the program. I have changed the pin that I am using.

My code is ~600 lines and for a senior design project, so I only posting a snippet to get an idea of how it runs. I think the problem has something to do with memory and not the program itself. If it helps I am using the FastLED library. My partner has a theory that it has to do with FastLED for the WS2811s and interrupts in combination with the serial port. I think the L LED is the biggest clue but I can’t find what it’s telling us :stuck_out_tongue:

Any help would be appreciated - thank you for taking the time to read this.

#define PixelDataPin 8

class PixelRing
{
public:
#define numLEDs 12          //How many LEDs controlled in this region
#define SHORT_WAIT 50       // wait times (ms)
#define MED_WAIT 100
#define LONG_WAIT 250
#define SHORT_CYCLE 4       // the number of changes in the cycle
#define MED_CYCLE 6
#define LONG_CYCLE 12

    int hue = 0;               // controls fading
    byte opCode = 0;            //Indicates the behavior of the region
    byte changesCompleted = 0;  //How many changes have been completed
    boolean state = false;      //on or off
    long lastUpdated = 0;       //Time the behavior was last updated
    CRGB *leds;                 // The LEDs in this region

    void begin()
    {
      pinMode(PixelDataPin, OUTPUT);
      leds = new CRGB[numLEDs];
      FastLED.addLeds<WS2811, PixelDataPin>(leds,numLEDs);
    }

    void behavior0()
    {
        //Dummy behavior all regions have that just does no updates
        //Used when a region is to stay as it was left

        return;
    }
    void behavior1()
    {
        // turn everything off
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
       
        opCode = 0; // we don't need to return here
    }
    void behavior2()
    {
        // send a single led around the circle starting from the beginning
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
        leds[changesCompleted] = CHSV(random8(),255,255);
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            leds[numLEDs - 1] = CRGB::Black;
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    

    void update()
    {
        //No opCode given
        //When a region is given the chance to update, the switch
        //will pick the correct method based on the current opCode
        //and that method will determine what, if anything, needs done

        switch(opCode)
        {
            case 0:
                behavior0();
                break;
            case 1:
                behavior1();
                break;
            case 2:
                behavior2();
                break;
            case 3:
                behavior3();
                break;
            case 4:
                behavior4();
                break;
            case 5:
                behavior5();
                break;
            case 6:
                behavior6();
                break;
            case 7:
                behavior7();
                break;
            case 8:
                behavior8();
                break;
            default:
                break;
        }
        return;
    }

    void update(byte newCode)
    {
      //A new code has been given that could replace the currently
      //executing behavior. Set all the indicators to a fresh state
      //and update the opCode. update() will now switch to the new
      //behavior to region activity.

      hue = 0;
      lastUpdated = 0;
      state = false;
      changesCompleted = 0;
      opCode = (byte)newCode;
    }
};


  byte incomingMsg = -1;
  bool byteToProcess = false;
  PixelRing ring;

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(115200); //set the baud rate
  ring.begin();
  ring.update(1);
  FastLED.setBrightness(50);
}

void loop() 
{
  receiveByte();
  processByte();
  ring.update();
  FastLED.show();
}

void receiveByte()
{
  if (Serial.available() > 0) 
  {
    incomingMsg = Serial.read();
    byteToProcess = true;
  }
}

void processByte()
{
  if (byteToProcess) 
  {
    switch (incomingMsg) 
    {
      case 10:
        ring.update(2);
        break;
      case 11:
        ring.update(3);
        break;
      case 12:
        ring.update(4);
        break;
      case 13:
        ring.update(5);
        break;
      case 14:
        ring.update(6);
        break;
      case 15:
        ring.update(7);
        break;
      case 16:
        ring.update(8);
        break;
      default:
        break;
    }
    byteToProcess = false;
  }
}

Post the full sketch. It would surprise me if the problem was memory unless there were far more leds than that snippet implies. Snippets in general are not helpful - we need a full sketch that reproduces the problem; the simple fact that you are asking for help is evidence that you do not know where in your code the problem comes from. Often people will think the problem is in one part of their code, and think posting just that is sufficient, but when we finally convince them to post the full sketch, we find that the problem is a completely different place in their code than they thought - a global declaration that’s wrong, some other part of the sketch writing past the end of an array corrupting memory, etc.

[Part 1] Here is the full sketch, then. It verifies and uploads. If I were to comment out any one of the behavior calls in the switch statement of the NeoPixelRing class, it would work and perform all the other behaviors. It also worked if I commented out receiveByte() and processByte() and just manually update behaviors. Otherwise, it just blinks the L LED and will not control any other LEDs. I had to split it into multiple pieces because it exceeded max character length.

#include <FastLED.h>
#include <MemoryFree.h>

#define regionADataPin 9
#define regionBDataPin 10
#define NeoPixelDataPin 11

class RegionA
{
public:
#define numLEDs 2           //How many LEDs controlled in this region
#define SHORT_WAIT 25       // wait times (ms)
#define MED_WAIT 50
#define LONG_WAIT 100
#define SHORT_CYCLE 4       // the number of changes in the cycle
#define MED_CYCLE 6
#define LONG_CYCLE 8

    byte opCode = 0;            //Indicates the bahvior of the region
    byte changesCompleted = 0;  //How many changes have been completed
    boolean state = false;      //on or off
    long lastUpdated = 0;       //Time the behavior was last updated
    CRGB *leds;                 // The LEDs in this region

    void begin()
    {
      pinMode(regionADataPin, OUTPUT);
      leds = new CRGB[numLEDs];
      FastLED.addLeds<WS2811, regionADataPin>(leds,numLEDs);
      behavior1();
    }

    void behavior0()
    {
        //Dummy behavior all regions have that just does no updates
        //Used when a region is to stay as it was left

        return;
    }
    void behavior1()
    {
        // both LEDs "off"

        leds[0] = CRGB::Green;
        leds[1] = CRGB::Green;
        
        opCode = 0; // we don't need to return here
    }
    void behavior2()
    {
        // LED 0 off, 1 on

        leds[0] = CRGB::Green;
        leds[1] = CRGB::Gold;
        
        opCode = 0; // we don't need to return here
    }
    void behavior3()
    {
        // LED 0 on, 1 off

        leds[0] = CRGB::Gold;
        leds[1] = CRGB::Green;
        
        opCode = 0; // we don't need to return here
    }
    void behavior4()
    {
        // both LEDs on

        leds[0] = CRGB::Gold;
        leds[1] = CRGB::Gold;
        
        opCode = 0; // we don't need to return here
    }

    void update()
    {
        //No opCode given
        //When a region is given the chace to update, the switch
        //will pick the correct method based on the current opCode
        //and that method will determine what, if anything, needs done

        switch(opCode)
        {
            case 0:
                behavior0();
                break;
            case 1:
                behavior1();
                break;
            case 2:
                behavior2();
                break;
            case 3:
                behavior3();
                break;
            case 4:
                behavior4();
                break;
            default:
                break;
        }
        return;
    }

    void update(byte newCode)
    {
      //A new code has been given that could replace the currently
      //executing behavior. Set all the indicators to a fresh state
      //and update the opCode. update() will now switch to the new
      //behavior to region activity.

      lastUpdated = 0;
      state = false;
      changesCompleted = 0;
      opCode = newCode;
    }

};

class RegionB
{
public:
#define numLEDs 5           //How many LEDs controlled in this region
#define SHORT_WAIT 100       // wait times (ms)
#define MED_WAIT 400
#define LONG_WAIT 500
#define SHORT_CYCLE 4       // the number of changes in the cycle
#define MED_CYCLE 6
#define LONG_CYCLE 8

    byte hue = 0;               // used for fading
    byte opCode = 0;            //Indicates the bahvior of the region
    byte changesCompleted = 0;  //How many changes have been completed
    boolean state = false;      //on or off
    long lastUpdated = 0;       //Time the behavior was last updated
    CRGB *leds;                 // The LEDs in this region

    void begin()
    {
      pinMode(regionBDataPin, OUTPUT);
      leds = new CRGB[numLEDs];
      FastLED.addLeds<NEOPIXEL, regionBDataPin>(leds,numLEDs);
      behavior3();
    }

    void behavior0()
    {
        //Dummy behavior all regions have that just does no updates
        //Used when a region is to stay as it was left

        return;
    }
    void behavior1()
    {
        // gate 1 LED off

        leds[1] = CRGB::Green;
        
        opCode = 0; // we don't need to return here
    }
    void behavior2()
    {
        // flash gate 1 on
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        //It must be time for an update to occur
        if(state)
        {
            //if the LEDs are on, turn it off
            leds[1] = CRGB::Green;
            state = false;
        }
        else
        {
            //LEDs must be off, so turn them on
            leds[1] = CRGB::Gold;
            state = true;
        }
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= MED_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            leds[1] = CRGB::Gold;
            // [ LED library command for the state we wish to leave the LEDs in ]
        }
        
    }
    void behavior3()
    {
      for (byte i = 0; i < numLEDs; i++)
      {
        leds[i] = CRGB::Black;
      }
      behavior1();
    }
    
    void update()
    {
        //No opCode given
        //When a region is given the chace to update, the switch
        //will pick the correct method based on the current opCode
        //and that method will determine what, if anything, needs done

        switch(opCode)
        {
            case 0:
                behavior0();
                break;
            case 1:
                behavior1();
                break;
            case 2:
                behavior2();
                break;
            default:
                break;
        }
        return;
    }

    void update(byte newCode)
    {
      //A new code has been given that could replace the currently
      //executing behavior. Set all the indicators to a fresh state
      //and update the opCode. update() will now switch to the new
      //behavior to region activity.

      hue = 0;
      lastUpdated = 0;
      state = false;
      changesCompleted = 0;
      opCode = newCode;
    }

};

[Part 2]

class NeoPixelRing
{
public:
#define numLEDs 12          //How many LEDs controlled in this region
#define SHORT_WAIT 50       // wait times (ms)
#define MED_WAIT 100
#define LONG_WAIT 250
#define SHORT_CYCLE 4       // the number of changes in the cycle
#define MED_CYCLE 6
#define LONG_CYCLE 12

    int hue = 0;                // controls fading
    byte opCode = 0;            //Indicates the bahvior of the region
    byte changesCompleted = 0;  //How many changes have been completed
    boolean state = false;      //on or off
    long lastUpdated = 0;       //Time the behavior was last updated
    CRGB *leds;                 // The LEDs in this region

    void begin()
    {
      pinMode(NeoPixelDataPin, OUTPUT);
      leds = new CRGB[numLEDs];
      FastLED.addLeds<NEOPIXEL, NeoPixelDataPin>(leds,numLEDs);
    }

    void behavior0()
    {
        //Dummy behavior all regions have that just does no updates
        //Used when a region is to stay as it was left

        return;
    }
    void behavior1()
    {
        // turn everything off
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
       
        opCode = 0; // we don't need to return here
    }
    void behavior2()
    {
        // send a single led around the circle starting from the beginning
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
        leds[changesCompleted] = CHSV(random8(),255,255);
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            leds[numLEDs - 1] = CRGB::Black;
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    void behavior3()
    {
      // send a single led around the circle starting from the end
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
        leds[numLEDs - 1 - changesCompleted] = CHSV(random8(),255,255);
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            leds[0] = CRGB::Black;
            opCode = 0;
            changesCompleted = 0;
        }    
    }
    void behavior4()
    {
        // start from beginning and make a full circle of LEDs
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i <= changesCompleted; i++)
        {
          leds[i] = CHSV(random8(),255,255);
        }
        for (byte i = changesCompleted + 1; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    void behavior5()
    {
        // start from end of circle and turn LEDs off
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i < numLEDs - 1 - changesCompleted; i++)
        {
          leds[i] = CHSV(random8(),255,255);
        }
        for (byte i = numLEDs - 1 - changesCompleted; i < numLEDs; i++)
        {
          leds[i] = CRGB::Black;
        }
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            changesCompleted = 0;
        }    
    }
    void behavior6()
    {
        // start from beginning and turn off all LEDs
        if(millis() - lastUpdated < SHORT_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i <= changesCompleted; i++)
        {
          leds[i] = CRGB::Black;
        }
        for (byte i = changesCompleted + 1; i < numLEDs; i++)
        {
          leds[i] = CHSV(random8(),255,255);
        }
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    void behavior7()
    {
      // fill entire ring in rainbow
        if(millis() - lastUpdated < MED_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        fill_rainbow (leds,numLEDs,hue,15);
        hue += 15;
       
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= MED_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    void behavior8()
    {
      // send 3 LEDs around a circle (purple leds around pink ring)
        if(millis() - lastUpdated < MED_WAIT)
        {
            //Not time to act yet, return
            return;
        }
        for (byte i = 0; i < numLEDs; i++)
        {
          leds[i] = CRGB::Pink;
        }
        
        if (changesCompleted == 0) leds[numLEDs - 1] = CRGB::Purple;
        else leds[changesCompleted - 1]  = CRGB::Purple;
        leds[changesCompleted] = CRGB::Purple;
        if (changesCompleted == (numLEDs - 1)) leds[0]  = CRGB::Purple;
        else leds[changesCompleted + 1] = CRGB::Purple;
        
        //bookkeeping and status updates
        lastUpdated = millis();
        changesCompleted++;
        if(changesCompleted >= LONG_CYCLE)
        {
            //We reached the desired number of cycles
            opCode = 0;
            changesCompleted = 0;
        }        
    }
    void update()
    {
        //No opCode given
        //When a region is given the chance to update, the switch
        //will pick the correct method based on the current opCode
        //and that method will determine what, if anything, needs done

        switch(opCode)
        {
            case 0:
                behavior0();
                break;
            case 1:
                behavior1();
                break;
            case 2:
                behavior2();
                break;
            case 3:
                behavior3();
                break;
            case 4:
                behavior4();
                break;
            case 5:
                behavior5();
                break;
            case 6:
                behavior6();
                break;
            case 7:
                behavior7();
                break;
            case 8:
                behavior8();
                break;
            default:
                break;
        }
        return;
    }

    void update(byte newCode)
    {
      //A new code has been given that could replace the currently
      //executing behavior. Set all the indicators to a fresh state
      //and update the opCode. update() will now switch to the new
      //behavior to region activity.

      hue = 0;
      lastUpdated = 0;
      state = false;
      changesCompleted = 0;
      opCode = (byte)newCode;
    }
};

[Part 3] If you add all 3 code segments from the different parts together in order it makes the whole program as-is. There is a similar issue here: L Led Flashes Constantly - SparkFun Electronics but the fix did not work for me. The behavior of the LED and the fact that the parts work individually is what leads me to believe the problem is something other than my code but I appreciate any input.

//===================================================================================================

  byte incomingMsg = -1;
  bool byteToProcess = false;
  RegionA regionA;
  RegionB regionB;
  NeoPixelRing ring;

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(115200); //set the baud rate
  regionA.begin();
  regionB.begin();
  ring.begin();
  ring.update(1);
  FastLED.setBrightness(50);
}

void loop() 
{
  receiveByte();
  processByte();
  regionA.update();
  regionB.update();
  ring.update();
  FastLED.show();
}

void receiveByte()
{
  if (Serial.available() > 0) 
  {
    incomingMsg = Serial.read();
    byteToProcess = true;
  }
}

void processByte()
{
  if (byteToProcess) 
  {
    switch (incomingMsg) 
    {
      case 0:
        regionA.update(1);
        regionB.update(1);
        break;
      case 1:
        regionA.update(2);
        regionB.update(1);
        break;
      case 2:
        regionA.update(3);
        regionB.update(1);
        break;
      case 3:
        regionA.update(4);
        regionB.update(1);
        break;
      case 4:
        regionA.update(1);
        regionB.update(2);
        break;
      case 5:
        regionA.update(2);
        regionB.update(2);
        break;
      case 6:
        regionA.update(3);
        regionB.update(2);
        break;
      case 7:
        regionA.update(4);
        regionB.update(2);
        break;
      case 10:
        ring.update(2);
        break;
      case 11:
        ring.update(3);
        break;
      case 12:
        ring.update(4);
        break;
      case 13:
        ring.update(5);
        break;
      case 14:
        ring.update(6);
        break;
      case 15:
        ring.update(7);
        break;
      case 16:
        ring.update(8);
        break;
      default:
        break;
    }
    byteToProcess = false;
  }
}

Why do you think this is a bootloader issue?

TheMemberFormerlyKnownAsAWOL:
Why do you think this is a bootloader issue?

Other people who have had similar issues seemed to have accompanying bootloader problems, so I though that this might be the subset of people who could understand the problem. I also wasn't sure which forum to post under and was trying to find one that focused on the hardware and not code. I see now that maybe it should've been under installation and troubleshooting but I am new to posting on the forum.

Definitely not installation and troubleshooting. That is for problems installing the IDE and getting basic functionality. If it's not a problem with the IDE itself, and you can detect board and compile+upload blink to it, it doesn't belong in installation and troubleshooting.

Your problem is not hardware, it is software (I don't know what gave you the idea that this is a hardware problem - everything about this screams software bug). Ergo, programming questions is the right section for it. Don't repost the thread there (that is cross-posting, which is a violation of forum rules). You can report your thread to moderator and ask them to move to the correct section.

Since your code does not blink the L light, the fact that it blinks indicates the bootloader running. That this happens when sketch should be running instead, and continually, it implies that it's resetting. This often happens if you're scribbling over memory you shouldn't.

I'm not an expert on classes in C, and I didn't look too hard, but I suspect the problem is the way you're working with the leds arrays for the different regions, when the overall effect being that you end up writing to memory outside of the bounds of an array. C does not do bounds checking on arrays (indeed, in an embedded environment, the overhead of this would be prohibitive anyway), and writing off the end of an array on an Arduino will usually result in a hang or reset.

If you comment out those two offending lines, but put a call to one of the regions' update functions in setup, does it also break? That would confirm my theory.