Serial commands from PC crash Arduino after some time

I have a Raspberry PI Linux PC and Arduino nano connected over USB. This is the first building block in a manual RC control program and at present I control two servos via sending a string over serial such as:

"90,180;"

The string would mean -> servo 1 go to 90 degrees angle and servo 2 -> go to 180 degrees angle. This appears to work very well but after a few seconds - especially when sending the update commands more rapidly I find the Arduino resets, disconnects and I have to restart my program on the Raspberry PI pointing to a different virtual tty port.

Video of 2 DOF robotic arm with 2 servos:
Youtube link

I suspect I am flooding a buffer or overloading the Arduino in some way?

Code below:

#include <Servo.h>
Servo s1;
Servo s2;

#define s1Neutral 60
#define s2Neutral 80

int firstPosition;
int secondPosition;

void writeServos(){
  s1.write(firstPosition);
  s2.write(secondPosition);
}
 
void setup() {
  pinMode(A3,OUTPUT);
  Serial.begin(9600);
  analogWrite(A3,150); // Flash LED to show that reset happened
  delay(200);
   analogWrite(A3,0);
  s1.attach(3);
  s2.attach(5);

  firstPosition=s1Neutral;
  secondPosition=s2Neutral;
  writeServos();
}

int getValue(String spec, int start, int end)
{
  String val;
    for(int i=start;i<end;i++){
      val+=spec[i];
    }
    return val.toInt();
}

int getIndex(String spec, char splitter){
    int index=-1;
    for(int i=0;i<spec.length()-1;i++){
      if(spec[i]==splitter){
          index=i;
          break;
       }
    }
    return index;
}

void loop() {
  String spec;
  spec = Serial.readStringUntil(';');
  if(spec!=""){
    char splitter=',';
    int index = getIndex(spec, splitter);
    if(index!=-1){
      int first = getValue(spec,0, index);
      int second = getValue(spec,index+1,spec.length());
      if(first==0 && second==0){
        firstPosition=s1Neutral;
        secondPosition=s2Neutral;
         }else {
        firstPosition=first;
        secondPosition=second;
      }
    }
  }
  delay(20);
  writeServos();
  delay(20);
}

The python program writing to the port uses code similar to below and I invoke this every time I find a PS3 controller axis changes within my main python code's loop.

import serial
i=0
ser=serial.Serial("/dev/ttyUSB"+str(i),9600)
ser.write("50,100;".encode())   // works the same with and without encode

Quit using Strings. Read all the serial data available, and store it in a global array of chars, adding a NULL after each byte. When you read a ';', reset the index and parse the data, using strtok() and atoi(), and move the servos.

The Pi program needs to be smarter than that. It should ONLY send data when the data changes.

Have a look at the examples in serial input basics. They are simple, reliable, non-blocking and they don't use Strings (cap S). And they do pretty much what @PaulS has suggested.

...R

To Paul's point, I did say I only send data when there is a change in axis. Now when the axis is being pushed fully forward we are obviously going to be changing the position quite quickly - this works OK but at a certain point when the volume of write commands increases there is breakage.

Robin - I can certainly rewrite the code to use character arrays etc that is the next logic thing to try. Can you help me understand what exactly is causing the Arduino to crash and reset?

alexellisuk:
Can you help me understand what exactly is causing the Arduino to crash and reset?

If you modify it to use my code and there is a problem I will certainly try to help.

A common problem using Strings is that the small memory in the Arduino gets corrupted after a while.

...R

Are you powering the servos from the Arduino? Also, increase the baud rate. 250000 baud is blindingly fast.

Isaac96:
Are you powering the servos from the Arduino? Also, increase the baud rate. 250000 baud is blindingly fast.

I haven't tried increasing the baud rate yet and the servos are powered by their own 4xAA battery pack. When on the robot they would be powered through a line from a 5v UBEC.

Robin - there is a lot of discussion and changes suggested to the code posted on that thread, have you got a cut down version which I can take and start testing out? Once I have this working well I want to send some additional signals through for motor pulses and then to start receiving feedback from encoders etc.

Would you suggest having one string with all the relevant data - or going down the route of having multiple packet/command types.

Separate 'write' commands:
S:100:030; // servo1 100 deg, servo2 30 deg

M:010:090; // motors left 10 percent of cycle, motors right 90 percent

Combined/flat structure:
100:030:090:090;

Thanks for the help

alexellisuk:
Robin - there is a lot of discussion and changes suggested to the code posted on that thread,

All you need to read is my first 2 posts. They include references to a couple of later posts but I don't think they are essential - it's been a while

...R