Problem controlling more than 3 LEDs through serial

Working on a project which will eventually involve controlling 9 Neopixel LED strips simultaneously through Unity and the Kinect routed through an Arduino Uno. It works exactly how I'd like it to, except when I try anything more than 3 LEDs, it will periodically "freeze" and stop responding to input.

I've done some testing with this and found that I am indeed still sending the signal from Unity as normal. In order to unfreeze it, it must receive another signal first. This fact holds even when my entire loop function is checking the first digit of the received data and an else statement to change the color. When it freezes, it is obviously not receiving new data, but does not default to the else statement either. When doing my normal program (which receives a new color and sends it down the line), the colors stop moving altogether.

As I've said, I've never had problems using only three LEDs, regardless of which LEDs they are. Every unity frame I am sending new data to the Arduino, which then writes new info to the 60 LEDs in each strip (as well as a few additional operations like showing). I know Arduino's write function is sometimes slow, could this be the problem? Would this be fixed by using multiple Arduino's or some other chip?

I'm also still having some connection problems with a few strips which seems to be related to having a stable common ground. However, the problem does not change based on which strips are involved, only the number.

Any ideas for what I could be doing wrong? I've already solved the common problem of using sizeof for arrays. Below is the relevant parts of my code for the basic two color test described above and my normal operation.

Thanks in advance for the help!

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN1 1
#define PIN2 2
#define PIN3 3
#define PIN4 4
#define PIN5 5
#define PIN6 6
#define PIN7 7
#define PIN8 8
#define PIN9 9


int lightNum= 3;
Adafruit_NeoPixel strips[] = {
  Adafruit_NeoPixel(60, PIN4, NEO_GRB + NEO_KHZ800),
  Adafruit_NeoPixel(60, PIN5, NEO_GRB + NEO_KHZ800),
 Adafruit_NeoPixel(60, PIN6, NEO_GRB + NEO_KHZ800),
  Adafruit_NeoPixel(60, PIN7, NEO_GRB + NEO_KHZ800),

};

byte readByte[5];
int receiveBytes = 4;

void setup(){
 stripsBegin();
 showStrips();
 Serial.begin(28800);

}




void loopTest(){
  Serial.readBytes(readByte, 4);
if(readByte[0] == 5){
      colorStrip(strip.Color(0, 255, 0));
      r = 200;
  } else{
    colorStrip(strip.Color(255, 0, 0));
  }
}

void loop(){
     Serial.readBytes(readByte, 4);
     gradient(readByte[1], readByte[2], readByte[3]);
}


void colorStrip(uint32_t c) {
  for(uint16_t i=0; i<60;i++){//strip.numPixels(); i++) {
    setStripPixels(i, c);
  }
      showStrips();
}

void showStrips(){
  for(int i = 0; i < lightNum; i++){
    strips[i].show();
  }
}

void stripsBegin(){
  for(int i = 0; i < lightNum; i++){
    strips[i].begin();
  }
}

void gradient(float lpos, float mult, float z){
    float sr = 0;
  float sg = 0;
  float sb = 0;
  sr = 30-lpos;
  sb = lpos-30;
  sg = lpos-abs(lpos-30);
    if(z>50){
      sb = sb + (z-50)*3;
      sr = sr + (z-50)*3;
    }
   float w = sr*mult;
   float k = sg*mult;
   float c = sb*mult;
          if(w<15){
            w = 15;
          }
          if(k<15){
            k = 15;
          }
          if(c<15){
            c = 15;
          }
          if(mult==0){
            w = 0;
            k = 0;
            c = 0;
          }
   if(w > 255){
     w = 255;
   }
      if(k > 255){
     k = 255;
   }
   if(c > 255){
     c = 255;
   }
   colorWipe(strip.Color(w, k, c));

 }

void colorWipe(uint32_t c) {
  setStripPixels(0, c);
  for(uint16_t i=0; i<strip.numPixels()-1; i++) {
    int j = strip.numPixels()-1-i;
    setStripPixels(j, strip.getPixelColor(j-1));
  }
  showStrips();
}

You can only have one loop().

Apologies for the confusion, that was a typing mistake and I've edited my post. To specify, the loopTest function never runs, when I want to test it, I change the name to loop and loop to some other temporary name. They are alternate functions. I don't think the problem is related to that as I ran into it originally before I wrote the alternate testing function.

Hi,

I think you should try using Serial.available() in your loop(), to check that there are 4 bytes ready before you call Serial.readBytes().

If that does not help then i think the problem may be caused by the strip.show() function corrupting the incoming serial bytes. It does this by disabling interrupts, which it needs to do to perform the accurate timing needed to control the Neopixel signals. But switching off interrupts prevents serial data being received correctly, corrupting one or more bytes, so that your sketch waits to receive more.

Can you arrange that there will be a delay between groups of 4 bytes being sent? This would give the Arduino time to update the strip without fear of missing some serial data.

Paul

I see. I just changed the baud rate from 288800 to 9600 and was able to control 6 without problem. About to test 9. Would using serial.available likely address this problem as well?

Sorry, I didn't expect such a quick response and have updated my first post.

Another technique to try would be to monitor Serial.available() while it is less than 4. If there is no change within a timeout period (use millis() for this), perform a Serial.flush() to clear out the partial message in the buffer.

No problem, thanks for the help.

So if I understand correctly, arduino is doing too much by showing all the strips so that it doesn't have enough time to receive the next signal by starting the loop again? Unfortunately the program requires that the reception be constant and quite fast. Would trying to optimize a little help at all? Would doing everything for each strip rather than all at once potentially be beneficial?

8 strips broke it again, but this was solved by again reducing the baud rate, although it is getting quite slow comparatively.

I just tested out some stuff with Serial.available() at 9600, like flushing if the available was less than 3 and such, which didn't seem to fix the problem. I had not tried implementing it with millis() which I'll try now.

EthanEdwards:
So if I understand correctly, arduino is doing too much by showing all the strips so that it doesn't have enough time to receive the next signal by starting the loop again?

Could be, but ultimately you need some gaps in the transmissions so that the Arduino can update the strips. Arduino only has one processor core. Increase the baud rate and put some delays between the transmissions, that way the data rate can be the same overall.

EthanEdwards:
Unfortunately the program requires that the reception be constant and quite fast. Would trying to optimize a little help at all? Would doing everything for each strip rather than all at once potentially be beneficial?

Yes. But you have to update an entire strip in one transaction, and that will get slower the more leds there are on the strip.

EthanEdwards:
8 strips broke it again, but this was solved by again reducing the baud rate, although it is getting quite slow comparatively.

I would have thought reducing the baud rate would make things worse!

EthanEdwards:
I just tested out some stuff with Serial.available() at 9600, like flushing if the available was less than 3 and such, which didn't seem to fix the problem. I had not tried implementing it with millis() which I'll try now.

If you tried to do it without millis(), I obviously did not explain the idea clearly enough. You can't just Serial.flush() because there are less than 4 bytes available, you will never get to 4 that way! What I was trying to suggest was that if Serial.available() is 1, 2 or 3, and stays that way for some time (like several milliseconds), then its likely that there has been a byte corrupted or missed. However, for this to work, you need those gaps between the 4-byte transmissions.

EthanEdwards:
Every unity frame I am sending new data to the Arduino, which then writes new info to the 60 LEDs in each strip (

Neopixels require a very accurate timing up to less than 1 microsecond, and this is achieved by suppressing all interrupts while the Neopoxel data are written to the LEDs.

So every interrupt driven routine in your Arduino is halted while the LEDs are updated.
No millis() is counting and nothing else that is relying on interrupts.

The more Neopixel LEDs you have in a row, the longer the interrupts are disabled.

You will either have to use different LEDs which are driven using a "clock" line signal.

Or if you want to drive Neopoxel LEDs you will have to decouple receiving the data and driving the LEDs.

For example, your Neopixel driving Arduino might show a "ready" state when he is ready to receive new data. Then he receives the data, disables his ready state, then drives the LED strips.

You cannot receive (interrupt driven) Serial data in background and drive your Neopixel strips from the application code at the very same time. If you cannot synchronize data transmission to/from one Arduino, you will at least need two Arduino boards to synchronizse them: One asynchronously receiving data (from your PC or other device?`), the second driving the Neopixel-LEDs actually. And your code must keep them accurately synchronized with receiving data, syncing data on both boards and driving the LEDs.

So, if I would like to do the process while synced, I need to make sure the arduino has enough time to execute all its actions before receiving new data? Because I need the transmission to be constant and Unity serial communications are a little difficult, I think staying synced is the best option for now.

How large should the buffer that I store the serial data in be? So far I've kept it at the size of each chunk I read. Does decreasing the frequency of transmission from Unity help regardless of other changes?

I have the full program (mostly) working. I had to use two arduino's because one could not store 9 neopixel strips as global variables. Now I'd like to increase the amount of bytes I'm sending to each from 4 each to 16 each during certain periods. One option I've considered is by default sending and reading 4 bytes (which is all that's necessary for most operations) and when I need to send 16 bytes, send a signal to arduino to read in the new amount and then reduce the unity transmission rate. I'm currently implementing the test, but does this sound like the right approach for handling more data? Should reducing the rate theoretically do the trick?

Thanks for all the help so far.

Can you explain what you mean by "synched" in this context?

My point is that the Arduino cannot update the strips and receive serial data at the same time. You need to separate the two operations, time-wise.

Reducing the baud rate in Unity (I have no idea what that is) will make things worse, because the time taken to transmit each block of data would be longer, resulting in shorter gaps between blocks.

To increase the time available to update the strips, you need to increase the baud rate while keeping the overall data rate the same.