Pc to arduino communication

I've built an led strip controller using arduino mega. On arduino side serial communications code works like this:

  • Wait until Serial.available is > 0
  • Read first byte via Serial.read and save it to command[] byte array
  • Depending on its value, choose one of multiple commands and wait until enough bytes that encode arguments are waiting to be read, then read them into command[] byte array
  • Process them accordingly
  • Return to step one

And while i was sending commands by hand, everything was ok, but when i wrote a program that sends commands for me, two commands sent too quickly cause glitches, it feels like commands "collide"

Simplified arduino code:

#include <bitswap.h>
#include <chipsets.h>
#include <color.h>
#include <colorpalettes.h>
#include <colorutils.h>
#include <controller.h>
#include <cpp_compat.h>
#include <dmx.h>
#include <FastLED.h>
#include <fastled_config.h>
#include <fastled_delay.h>
#include <fastled_progmem.h>
#include <fastpin.h>
#include <fastspi.h>
#include <fastspi_bitbang.h>
#include <fastspi_dma.h>
#include <fastspi_nop.h>
#include <fastspi_ref.h>
#include <fastspi_types.h>
#include <hsv2rgb.h>
#include <led_sysdefs.h>
#include <lib8tion.h>
#include <noise.h>
#include <pixelset.h>
#include <pixeltypes.h>
#include <platforms.h>
#include <power_mgt.h>

FASTLED_USING_NAMESPACE

#include <String.h>

#define NUM_LEDS 131
#define DATA_PIN 11

CRGB leds[NUM_LEDS];
byte command[12];
bool manualshow = 1;

void setup() { 
 
FastLED.addLeds<WS2812, DATA_PIN, BRG>(leds, NUM_LEDS);
 
randomSeed(analogRead(0));
 
Serial.begin(115200); //Baud rate does not affect glitched behavior
  }

void loop() {
 
    while(Serial.available() == 0){} //Wait until there is something in serial buffer
  
    command[0] = Serial.read();  //Recieve command code
  
switch(command[0])
  {
    default:
      Serial.write(255); //Write 255 back to sender, indicating wrong command code
      break;
    //////////////SET WHOLE STRIP. Format: mode, H, S, V. |OR| mode, R, G, B.
    case 0:  
      
while(Serial.available() < 4){}; //Wait for arguments
      
for(int i = 1; i < 5; i++)
      {
        command[i] = Serial.read(); //Read 4 arguments
      }
      
/////////PUT LED CODE AFTER THIS/////////
/Processing command with FastLED library
      
if(command[1] == 0) // 0 = HSV   1 = RGB
      {
        for(int i = 0; i<=NUM_LEDS; i++)
        {
          leds[i] = CHSV(command[2],command[3],command[4]);
        }
      }
      else
      {
        for(int i = 0; i<=NUM_LEDS; i++)
        {
          leds[i] = CRGB(command[2],command[3],command[4]);
        }
      }
      
      if(manualshow){FastLED.show();}
      Serial.write(0);
      break;
//Other commands follow somilar structure
      
//////////////FILL GRADIENT . Format: mode, StartPoint, (Start color), End Point, (End color), DIR (0 - FWD, 1 = BKWD, 2 - SHRT, 3 = LNG)
    case 1:
      while(Serial.available() < 10){};
      
      for(int i = 1; i <= 10; i++)
      {
        command[i] = Serial.read();
      }
TGradientDirectionCode Dir;
     Dir = command[10];
     
      if(command[1] == 0) // 0 = HSV   1 = RGB
      {
          fill_gradient(leds,command[2],CHSV(command[3],command[4],command[5]),command[6],CHSV(command[7],command[8],command[9]), Dir);
      Serial.write(1);
      }
      else
      {
          fill_gradient_RGB(leds,command[2],CRGB(command[3],command[4],command[5]),command[6],CRGB(command[7],command[8],command[9]), Dir);
      Serial.write(1);
      }
      if(manualshow){FastLED.show();}
      
      break;

    case 2: //FILL SOLID Format: mode, startpos, endpos, (color)

      while(Serial.available() < 6){};
      
      for(int i = 1; i <= 6; i++)
      {
        command[i] = Serial.read();
      }

      if(command[1] == 0)
      {
        for(int i = command[2]; i <= command[3]; i++)
        {
          leds[i] = CHSV(command[4],command[5],command[6]);
        }
      }
      else
      {
        for(int i = command[2]; i <= command[3]; i++)
        {
          leds[i] = CRGB(command[4],command[5],command[6]);
        }
      }
      if(manualshow){FastLED.show();}
      Serial.write(2);
      break;

      case 3: //SET ONE LED Format: mode, pos, (color)

      while(Serial.available() < 5){};
      
      for(int i = 1; i <= 5; i++)
      {
        command[i] = Serial.read();
      }

      if(command[1] == 0)
      {
        leds[command[2]] = CHSV(command[3],command[4],command[5]);
        if(manualshow){FastLED.show();}
      }
      else
      {
        leds[command[2]] = CRGB(command[3],command[4],command[5]);
        if(manualshow){FastLED.show();}
      }
      Serial.write(3);
      break;

      case 4: //SET MODE (AUTO/MANUAL) Format: mode (0 - auto, 1 - manual)

      while(Serial.available() < 1){};
      
      command[1] = Serial.read();
      if(command[1]==0){manualshow = 0;}else{manualshow = 1;}
      break;

      case 5: //SHOW

      FastLED.show();
       
      break;

      case 6: //POLL Format: info

      FastLED.show();

      while(Serial.available() < 1){};
      
      command[1] = Serial.read();

        switch(command[1])
        {
          default:
          Serial.write(0);
          Serial.write(0);
          break;
          
          case 0:
            Serial.write((byte)NUM_LEDS);
            Serial.write(0);
          break;
        }
       
      break;
  }
}

What is wrong, and how can i properly implement byte "queue" so stuff wont collide

Read this thread to see how to do it better.

Basically, you should be filling in your command[] until you get an entire line and then process it.

blh64:
Read this thread to see how to do it better.

Basically, you should be filling in your command[] until you get an entire line and then process it.

That is exactly what i do

Delta_G:
Yeah, but read that thread about how to do it right.

while(Serial.available() == 0){} //Wait until there is something in serial buffer

Whatever you're building, receiving these commands is all it will ever be able to do if you block like this. Why not just let the loop do nothing if there's nothing on the line? Why block? Instead of blocking until you get the whole message, just let the loop run gathering characters until a whole message arrives and then process it. It's a lot easier to use start and end markers than to try to keep up with how many characters you need to read.

For one thing, open your code in the IDE and press control-T. That will autoformat it so it isn't wandering all over the page. When you see code written by real coders and all the blocks line up all nice and neat, that isn't done just to make it pretty. It helps you see how your code logic flows and what statements belong in what blocks.

Problem is, i send byte numeric values, representing Hue Saturation and Value of HSV color, and for example 0 might mean end of command, or 0 in Value of some color parameter

Delta_G:
But at no time does it freeze code in a while loop waiting for that stuff to get there. My robot has lots of other things to do while it waits.

My LED controller really doesnt need to do anything while it has no commands: just recieve command, do what it says, and then do nothing. No sensors, no cameras, no motors, just recieve, do, and then nothing

I think you should consider receiving the entirety of the serial data in a proper state machine, separate the receipt of serial data from the execution of the command and "compartmentalize" (i.e. use functions rather than cramming everything into loop()) have more structure to your messages (e.g. alignment header, fields, checksum etc).

I notice after each command your Arduino writes a single byte response to the PC: Does your PC actually wait for that response before sending the next command?

Might consider using these libraries (if not, they are at least a good reference on how to deal with UART serial data):

Blackfin:
I notice after each command your Arduino writes a single byte response to the PC: Does your PC actually wait for that response before sending the next command?

No, it was just a debug thing i added some time ago to make sure that arduino got to this point in program

Delta_G:
So you only accept serial input when a led sequence is completely done? I would think that the ability to receive the next command while you are executing the last one would be pretty useful.

How do i implement this? I heard of arduino pseudo-parallel execution, is that what i need?

Delta_G:
No, nothing that complicated. Just stop freezing your code in while loops. @Robin2 has a great Serial Input Basics thread on this site that shows some good techniques to reading from serial. But the point is that no program should ever stop and wait. For anything. It should continue running and keep coming back to check. Not freeze in its tracks and wait for something.

That's exactly what this serial library provides - and also offers more robustness than the basic tutorial