Serial communication occasionally messes up

I am writing some Arduino code to read in commands from a PC over serial then relay these commands over SPI to one of six 10-bit digital pots.

The pots are for controlling set points in a PID control system, and are labelled Xcoarse, Xfine, Ycoarse, Yfine, Zcoarse and Zfine.

The computer sends the serial data as:

“Xcoarse 1020”

Where the first part is the pot identifier and the second part is the value to be sent to that pot (0-1023).

The Arduino reads in the serial data from the PC using:

//  Read serial port if there is data available
//------------------------------------------
char dataIn[20] = {0};
for(n=0; Serial.available() > 0; n++) {
      dataIn[n] = Serial.read();
      receivedData = true;
}

then processes the string and extracts the requested pot and value and sends the command to the digital pot over SPI using a function I wrote called digitalPotWrite():

    if(receivedData){  // if we have received data over serial...
      
      //  Split the string at spaces
      //------------------------------------------
        char *p = dataIn;
        char *str;
        n=0;
        while ((str = strtok_r(p, " ", &p)) != NULL){ // split string with space as a delimiter
          splitData[n] = str;
          n++;
        }

      //  Convert the prefix into the chosen pot number
      //------------------------------------------
        if(splitData[0]=="Xcoarse")   {chosenPot = 0;}
        if(splitData[0]=="Xfine")     {chosenPot = 1;}
        if(splitData[0]=="Ycoarse")   {chosenPot = 2;}
        if(splitData[0]=="Yfine")     {chosenPot = 3;}
        if(splitData[0]=="Zcoarse")   {chosenPot = 4;}
        if(splitData[0]=="Zfine")     {chosenPot = 5;}

      //  Convert string value to number (i.e. "123" to 123)
      //------------------------------------------ 
        output = 0;
        for(int i=0; i<splitData[1].length(); i++) {
          output = (10*output) + (splitData[1].charAt(i)-'0');  // such a hack
        }
        
      //  Debugging - print out received and extracted information
      //------------------------------------------ 
        Serial.print(splitData[0]);
        Serial.print(",");
        Serial.print(" Pot: ");
        Serial.print(chosenPot); 
        Serial.print(", Value: ");
        Serial.println(output);

      //  Write requested value to chosen pot over SPI
      //------------------------------------------ 
        digitalPotWrite(chosenPot, output);  // write the values to the digital pots
    }
    
    delay(100);  // wait a little bit  
    Serial.flush();  // clear the serial buffer (just incase)
    receivedData = false;  // reset the received data flag
}

This behaves nicely around 90-95% of the time, and returns the correct pot number and value for a given input, however, if commands are given in somewhat quick succession (or sometimes just sporadically) the initial “Xcoarse” pot identifier part of the serial data sent to the Arduino gets mangled (i.e. it usually drops the first few characters of “Xcoarse” giving “coarse” or “oarse”), and as such the extracted data is also mangled, sending the received value to the previously received pot (as the pot number is not updated when the pot identifier is not Xcoarse, Xfine, Ycoarse, Yfine, Zcoarse, Zfine).

For example, if I send:

“Xcoarse 100”
“Ycoarse 500”

and the Ycoarse command gets mangled in transit, the Arduino prints over the serial terminal:

Xcoarse, Pot: 1, Value: 100
Xcoarse, Pot: 1, Value 500

This is for a rather critical application, and as such I would like to make it extremely reliable and the weak link seems to be the serial comm.

Can anyone see what might be going wrong here? Has anyone experienced anything like this before with serial comm?

Can anyone see what might be going wrong here?

Yes. You have a misunderstanding about how serial data is sent. You seem to be missing the “ssslllooowwwlllyyy” part. You assume that an entire packet arrives at once, and that only one packet will arrive at a time.

You need to send something like “<Xcoarse 1020>”, instead, so that the Arduino can tell where a packet starts and ends.

#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}

Will read such data, whenever it arrives. Only when the trailing > arrives will the “Process the packet” code be executed, where you need to decide what to do with the data in the inData array.

Thanks a lot, PaulS, that's done the trick! Comms seem to be really stable and reliable now. I did think about having start and end signifiers at one point during coding, but I thought they were unnecessary - how wrong I was! Thanks again =)