C# serial slow communication

Hello,

I have Arduino Pro Micro with ATMega32u4 and WS2812 LED strip (83 LEDs) and FastLED library.
What I want to make is C# computer program which can control individual LEDs (I want to make functions like VU meter, ambilight etc.).
I am sending 4 bytes - H S V and LED number, so it's 332 bytes one "frame" for whole strip and I tried to make it 144 FPS, which was 47 808 bytes = 382 464 bits per second.
I set baud rate to 1 000 000 (which I read shoud be ok for that) and it took about 2,5 secs to go through that loop. Why? I also tried 2 000 000 baud rate and it was not any better.
Where is my bottleneck? I think that my Arduino code is not perfect for this, how can I improve it?

I also tried 60 FPS which is 159 360 bits per second and it's still around 1 second on 1 000 000 baud rate. Once it's 850ms, then 1100ms, then 960ms...
I am sending one led at a time without any delays and Arduino immediately sets those four values to leds array and lights it up after whole strip data received.

Arduino code:

void loop() {
  while (Serial.available()>0) {
    for (int i = 0; i < 4; i++) {
      a[i] = Serial.read();
    }
    leds[a[0]] = CHSV (a[1], a[2], a[3]);
  }

  FastLED.show();
}

C# code:

// 60 FPS
for (int u = 0; u < 60; u++)
  {
    // 83 LEDs
    for (int i = 0; i < 83; i++)
    {
      byt[0] = (byte) i;
      byt[1] = 0;
      byt[2] = 255;
      byt[3] = 255;
      _sp.Write(byt, 0, 4);
    }
  }

On an Arduino with a 32U4 MCU the baud rate is irrelevant - data is sent at full USB speed.

Your bit calculation is off because you have not allowed for the start and stop bits - but that would not explain your experience.

Have you measured how long this line takes?

FastLED.show();

Your method of receiving data is not at all robust. There is nothing to prevent the Arduino from thinking (for example) the 3rd byte is the first byte.

...R

I tried calculating FastLED.show() using millis() and I got around 4ms.
That's 240 ms for 60 FPS.
I tried sending 60 FPS of data with commented FastLED.show() (so it just receives data without updating strip) and it takes 275 - 320 ms to send it. Sum it and I am on 560ms.

And so how to make it more robust? I think of sending termination character after every 4 bytes, but how to implement it properly? Read bytes until that character and than update strip? Could this also solve my timing problem?

Thank you!

LaXi0rCZ:
Sum it and I am on 560ms.

Is that fast enough? Does it mean the speed problem is solved?

If not, my suspicion is that this line

while (Serial.available()>0) {

concludes very much faster than the data arrives and consequently the fastLed() is called much more often than you think

And so how to make it more robust? I think of sending termination character after every 4 bytes, but how to implement it properly? Read bytes until that character and than update strip?

That sounds like a practical solution but you must ensure that the end-marker value NEVER appears in the data.

If my surmise is correct an end-marker would probably solve the problem.

...R

Sorry, I didn't write it well. 560 ms is ideal time for 60FPS. FastLED.show() takes 4 ms and transmit with commented show() takes up to 320 ms (I would have 440 ms room), but when I uncomment show(), it takes from 800 ms to even 2000 ms (I am using stopwatch in C#). Far away from calculated ideal 560 ms.

But I think I've got it now. It works and it takes about 450 ms to do 60 FPS cycle.
But I still think my code is not fully bulletproof.
I cannot use termination character, because I'm sending and reading byte. What do you think about this? What would you make differently? I think my biggest problem is it can go out of sync. I'd rather send something to Arduino and run FastLED.show() when it arrives and do not rely on calculating received LEDs..

void loop() {
  int index = 0;
  int currled = 1;
  byte recv;
  while (Serial.available()) {
    recv = Serial.read();
    a[index] = recv;
    index++;
    
    if (index == 4) {
      leds[a[0]] = CHSV (a[1], a[2], a[3]);
      index = 0;
      currled++;
    }

    if (currled == NUM_LEDS) {
      FastLED.show();
      currled = 1;
    }
    
  }
}

LaXi0rCZ:
I cannot use termination character, because I'm sending and reading byte. What do you think about this?

If it was my project I would reserve 255 as the end-marker and any time a value of 255 appears in the data to be transmitted I would change it to 254.

If that is not acceptable then there is a more complex solution. If you only want an end-marker (and not a start-marker as well) then treat 254 as a special identifier. Always follow it by another byte which could be either 254 or 255. So, if a "real" 254 exists in your data send 254 254 or if a "real" 255 exists send 254 255. The Arduino is then programmed to treat 254 as special and look at the byte that follows it. Of course this make the transmitted message bigger - but there's no such thing as a free lunch.

...R