Neopixels and Serial communication causing Arduino to auto-restart

Hi All,

I’m working on an LED bookshelf project and am trying to develop a communication system to allow me to control the LEDs over Bluetooth.

I’ve started by getting the Arduino to read serial command strings sent from the pc in the format <cmd,red,green,blue>. The Arduino uses the received values to then set the colour of a pulse animation which travels down the neopixel strip. My code can be found below:

BookshelfComms.ino

#include "BookshelfComms.h"
#include <Adafruit_NeoPixel.h>

#define PIN 6
#define NUMPIXELS 82
#define PULSESIZE 10
#define FILLARRAY(a,n) a[0]=n, memcpy( ((char*)a)+sizeof(a[0]), a, sizeof(a)-sizeof(a[0]) );

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
cmdData LEDdisplay = {1,0,0,255};
BookshelfComms book;

byte pattern[(NUMPIXELS+PULSESIZE)][3] = {0};
boolean blank[(NUMPIXELS+PULSESIZE)];

void setup() {

  FILLARRAY(blank,true);

  Serial.begin(115200);
  Serial.println("Serial started");

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(50);
}


void loop() {

  if (Serial.available() > 0){
    book.readComms(&LEDdisplay);
    pulse();
    book.showNewData(&LEDdisplay);
  }

  for (int i = (NUMPIXELS+PULSESIZE-1); i > -1; i-- ){
      if ((pattern[i][0] != 0) || (pattern[i][1] != 0) || (pattern[i][2] != 0)){

        strip.setPixelColor(i-PULSESIZE, pattern[i][0], pattern[i][1], pattern[i][2]);

        // shift pattern
        pattern[i+1][0] = pattern[i][0];
        pattern[i+1][1] = pattern[i][1];
        pattern[i+1][2] = pattern[i][2];

        // make pattern blank (will be filled if colour is being shifted to it in next loop iteration)
        pattern[i][0] = 0;
        pattern[i][1] = 0;
        pattern[i][2] = 0;
        blank[i] = false;
      }
      // only update LEDS that need to be
      else if (blank[i] == false){ 
        strip.setPixelColor(i-PULSESIZE, 0, 0, 0);  
        blank[i] = true;
      } 

    }
     
    delay(10);
    strip.show(); 

}

void pulse(){
  for (int i = 0; i < PULSESIZE; i++ ){
    pattern[PULSESIZE-i][0] =  LEDdisplay.r/i+1;
    pattern[PULSESIZE-i][1] =  LEDdisplay.g/i+1;
    pattern[PULSESIZE-i][2] =  LEDdisplay.b/i+1;
  }
}

BookshelfComms.h

#ifndef _bookComms_H_
#define _bookComms_H_

// custom struct for command and colour data
typedef struct  
{
  int cmd;
  int r;
  int g;
  int b;
} cmdData;

#define NUMCHARS 20

class BookshelfComms{
	public:
		char receivedChars[NUMCHARS];
		int newData = 0;

		void readComms(cmdData* parseContainer);
		void parseData(cmdData* parseContainer);
		void showNewData(cmdData* parseContainer); 
};

#endif

BookshelfComms.cpp

#include "BookshelfComms.h"
#include "Arduino.h"

// Comms uses the format of: <data>
void BookshelfComms::readComms(cmdData* parseContainer){
  static int recvInProgress = 0;
  static int ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char serialByte;
  
  while (Serial.available() > 0 && newData == 0) {
        
        serialByte = Serial.read();

        // Add serial data to read array
        if (recvInProgress == 1) {
            if (serialByte != endMarker) {
                receivedChars[ndx] = serialByte;
                ndx++;
                if (ndx >= NUMCHARS) {
                    ndx = NUMCHARS - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = 0;
                ndx = 0;
                newData = 1;
                parseData(parseContainer);
            }
        }
        // Begin logging serial cmd data
        else if (serialByte == startMarker) {
            recvInProgress = 1;
        }
    }
}

// Parse command data of: cmd,r,g,b
void BookshelfComms::parseData(cmdData* parseContainer) {

  // split the data into its parts 
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok(receivedChars,",");     // get the first part - the string
  parseContainer->cmd = atoi(strtokIndx);     // convert this part to an integer
  
  strtokIndx = strtok(NULL, ",");             // this continues where the previous call left off
  parseContainer->r = atoi(strtokIndx);       

  strtokIndx = strtok(NULL, ",");
  parseContainer->g = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  parseContainer->b = atoi(strtokIndx);
}

// Print recieved serial data
void BookshelfComms::showNewData(cmdData* parseContainer) {
    if (newData == 1) {
        Serial.print(parseContainer->cmd);
        Serial.print(",");
        Serial.print(parseContainer->r);
        Serial.print(",");
        Serial.print(parseContainer->g);
        Serial.print(",");
        Serial.println(parseContainer->b);
        newData = 0;
    }
}

The first time I send a command string to the Arduino it responds correctly and displays a pulse animation in the right colour. However, if I then send another command string it crashes the Arduino which then auto-restarts. While trying to debug this issue I discovered that the Arduino will correctly respond to several command strings in quick succession as long they are sent within a ~2 second window after the first command string is sent. Any command after this will crash the board.

At first glance I thought this issue was a result of a memory overflow as I am using an Arduino UNO to drive 82 LEDs and communicate over serial at the same time. I therefore tried reduced the number of LEDs to 10 but this has did not fix the problem.

I’ve encountered the same issue before when I was initialling developing the pulse animation using a button as the trigger instead of serial data. For that program I found that any time I called a Serial.print() it would cause the auto-restarting issue. I therefore tried removing all instances from this code as well but it did not resolve the issue. Even more peculiarly, when I removed the showNewData() function from the BookshelfComms library, the Arduino responded to the first command string and stopped responding to any additional command strings but didn’t then crash or restart.

I have tested my serial communication code independently and have found that the Arduino will happily echo back any command strings I send to it. The same is true for the pulse animation which triggers fine when using a button. It therefore appears to be a problem of the two parts conflicting in some way.

Does anyone have any idea about what may be causing this issue?

Adafruit_NeoPixel.h

I saw this while helping out on another thread...

Thanks for the suggestion.

I read through the thread and their solution seems to be to increase the serial baud rate and to suspend updating the neopixels when reading from Serial. Those are things I'm already doing in my code so it doesn't solve my problem unfortunately.

I'm not sure exactly what you're doing wrong - but crashes/restarts when handling c strings is usually indicative of having written off the end of an array. So I would do a rigorous examination of the code for that.

The issue with the neopixel library disabling interupts doesn't explain this issue (though you need to be aware of it depending on the length of the string of LEDs - once it takes longer to write to the LEDs than it does to receive one byte over serial, you'll lose serial data if it tries to update the LEDs while receiving serial data.

...yup facepalm, that's what it was. My code was trying to shift the pattern to an index one more than the array size

I'm so used to having programs like Matlab scream at me when I try to access an out of range index that I figured the compiler would do something similar. I guess it's not quite as high level as that.

Thanks for helping me figure this out!

This is c, bro! It will let you write off the end of an array, and you just overwrite whatever comes after it in the memory :) which usually hoses everything badly enough to make it restart.