Question about keeping Serial data in sync

Hi,

I'm driving a ws2812b type LED panel that has 24-bit colors for each LED pixel. The ws2812b IC is baked within the SMD5050, pretty cool stuff to toy with.

I've been driving other LEDS before with success but never the amount I am driving now, and it is causing issues. The panel is a 16x16 matrix arrangement. Which is 256*3 (bytes) to refresh the entire panel.

I've been playing with the Serial class and a .NET app that sends the data as byte arrays. I've also been playing with the buffer size of the Arduino software. Right now I am getting around 16fps which is pretty decent already. I am however packing the 24 bits into a 16 bit message basically driving it at 16 bit colors instead of 24.

Even still I am having sync issues. I tried using magic words and additional bytes as flags but non of those methods gave me good results. I had to strip down the entire code to its basics to get it to perform the way it does without glitches. The only bug I have right now is that occasionally the entire frame is read with an offset. It will give consistent results, but the colors will be all wrong. Usually this state will be present from the beginning until I reset the Arduino and my software.

I can probably solve this problem by implementing a simple protocol, I just didn't have any luck yet. Here's the code I am using now:

This is the entire loop function:

// Setup:
 Serial.begin(115200);
  mx.begin();
  mx.Show(0);

// Loop
i = 0;    
    while (i < 256) // Loop once for each LED
    {
      if (Serial.available() > 1) // Check for at least two bytes on Serial
      {
        byte b1 = Serial.read(); 
        byte b2 = Serial.read(); 
 
        int c = b1 | (b2 << 8);  // Bitshift two bytes into int
        byte r = (c & 0x7C00) >> 10; // Extract left most 5 bits
        byte g = (c & 0x3E0) >> 5; // Middle 5 bits
        byte b = (c & 0x1F);  // Right 5 bits
        
        // Set the LED color to RGB * 5 (32 * 4 = 128 = ~50% brightness)
        mx.Set(i++, mx.Rgb(r * 4, g * 4, b * 4));
      }
    }
  
    mx.Show(0);

I've tried various methods to solve this problem and none of them have led to anything. What thought do you have about solving this problem? Any help is very appreciated.

Thing I've tried:

  • Buffer flushing
  • Delays
  • Adjusting the data rate
  • Adjusting the size of the chunks
  • Byte commands to initiate a new read
  • Byte send to PC to notify state

It would be best to post your complete sketch so we don't have to guess how that snippet is called. Presumably you aren't re-initialising the serial port at every frame.

Your code reads pairs of bytes but does not provide any means to ensure that pairs of bytes written together are read together - anything causing the receiver to get out of sync with the sender would leave them permanently out of sync. Also you seem to assume that two bytes will be available for every LED but if the receiver ever read data from the stream faster than it was being sent then it would get confused about which LED the subsequent data was being read for.

I suggest you need to come up with a better encoding scheme so that you can detect the start of a frame reliably and consistently, and also fix the logic so that the receiver stays in sync with the incoming stream.

I suggest one of the first decisions should be whether you're going to use a textual or binary encoding scheme. Binary is slightly easier to code and can be more efficient but is harder to make robust.

If you use a binary encoding scheme you need to think of a way to indicate the boundary between frames. If there is a byte sequence (two zeroes, for example?) which you know will never be in the data stream then you could use that as flag to separate messages.

If you use a textual encoding scheme then the most obvious way to separate messages is with a newline.

What about sending a complete row or column at one time? (I think that would be 48 bytes).

...R