Arduino Mega Freezing upon high serial load

I have a python script that sends motor speed values to an arduino with the pyserial library, however the data comes in fast and needs to be proccessed by the arduino and sent to an l298n motor controller. Everything works fine until about 10 seconds in where the blinking orange light (the one that flashes every time serial data is sent) goes solid instead of flashing, and my python script is having to wait a really long time to go past sending the serial message. After some research i suspected the serial buffer may be filling, and having to wait to proccess each command before accepting the next serial input, so whenever the arduino receives a serial message, at the end of the code i end serial a start it again as i saw a post saying that should flush the buffer, however this has not worked and the arduino is still freezing.
The code flashed to the arduino:

String serialRead;
String motorL;
String motorR;
String directionL;
String directionR;
int motorLS = 7;
int motorL1 = 6;
int motorL2 = 5;
int motorR1 = 4;
int motorR2 = 3;
int motorRS = 2;
void setup() {
  Serial.begin(1000000);
  pinMode(motorL1, OUTPUT);
  pinMode(motorL2, OUTPUT);
  pinMode(motorR1, OUTPUT);
  pinMode(motorR2, OUTPUT);
  pinMode(motorLS, OUTPUT); 
  pinMode(motorRS, OUTPUT);
}

void loop() {
  if (Serial.available()>0) {
  serialRead = Serial.readStringUntil('.');

  motorL = serialRead.charAt(0);
  motorL += serialRead.charAt(1);
  motorL += serialRead.charAt(2);
  motorR = serialRead.charAt(3);
  motorR += serialRead.charAt(4);
  motorR += serialRead.charAt(5);
  directionL = serialRead.charAt(6);
  directionR = serialRead.charAt(7);
  Serial.println(motorL);
  Serial.println(motorR);
  Serial.println("Char 6: " + directionL);
  Serial.println("Char 7: " + directionR);

  analogWrite(motorLS, motorL.toInt());
  analogWrite(motorRS, motorR.toInt());

  if (directionL=="1") {
    Serial.println("1");
    digitalWrite(motorL1, LOW);
    digitalWrite(motorL2, HIGH);
    digitalWrite(motorR2, LOW);
    digitalWrite(motorR1, HIGH);
  }
  else {
    Serial.println("0");
    digitalWrite(motorL1, HIGH);
    digitalWrite(motorL2, LOW);
    digitalWrite(motorR2, HIGH);
    digitalWrite(motorR1, LOW);
  }
  Serial.end();
  Serial.begin(1000000);
  }
}

Demo python script:

import serial
import random
arduino = serial.Serial(port='COM3', baudrate=1000000, timeout=.001)
while True:
    # rjust takes any 1 or 2 digit number and puts leading 0s infront to make it 3 digits
    data = str(random.randint(0, 255)).rjust(3, '0') + str(random.randint(0, 255)).rjust(3, '0') + "11."
    arduino.write(bytes(data, 'utf-8'))

i dont need to use all the data, whenever the arduino finishes it should use the latest data coming in and update, without slowing down the python script as it also needs to proccess data in realtime and any pauses might miss some things. If anyone knows how to overcome this issue your help will be much appreciated!

I suspect its your heavy use of String

Eveytime you modify a String it needs to extend its length and will get put into a new available memory location causing memory fragmentation. You would possibly be running out of memory.

Rather than sending strings via your serial method you should be sending bytes. A value of 0 to 255 can be 1 x uint8_t

If your reading large incoming serial amounts as a string you should store them in a fixed size char array

In loop():

  Serial.end();
  Serial.begin(1000000);

Why?

Edit, sorry. Just saw your explanation. IMO, not necessary, if anything, problematic.
Re-read serial basics for how to properly handle a serial incoming command. Start, end markers, length, should be more than enough to guarantee a valid command structure.

1 Like

problem. When there is no serial handshake, the script should just send; how is it that your Arduino is able to hold up the script transmissions?

Or do you mean that the script waits to send, because the serial interface is slow - that's up to your script code to manage, not the domain of the Arduino. Serial is what serial is.

Additionally, your code sends a lot of chars back for every command received; this is likely filling the tx buffer of the Arduino. Consider sending status info less frequently.
Each println() sends the character AND cr/lf, a total of three chars for one char 'payload'.

You will have to give a complete description of your project including what is being driven by the L298 and the reasoning behind the maniac transfer.

Only because your python code is sending it like a maniac. Either slow it down or implement a protocol.

Simple protocol

  1. Python send message and waits for acknowledge.
  2. Arduino receives and sends acknowledge back.
  3. Back to (1).

And read Robin's updated serial input basics topic.

2 Likes

Ah I was afraid this was going to be the problem, I don't have much experience with languages that need to keep memory in mind. I'll see if I can get rid of the strings.

Thanks, I'll delete that

Id assume the reason would be buried somewhere in the pyserial library

Ideally the 2 should be in sync, there's no point of the script sending data if the Arduino isn't reading it, and everything does work as expected for the first 10-20 seconds, and then the Arduino locks up and can't read serial or update the motors

Ok thanks, I'll take a look at that post

Hi all, thanks for your suggestions. Ive incorperated Robin's serial code and gotten rid of all the strings and now it works like a charm. I did have to delete all the print statements for it to work but now it no longer freezes up!

int motorL;
int motorR;
int directionL;
int directionR;

int motorLS = 7;
int motorL1 = 6;
int motorL2 = 5;
int motorR1 = 4;
int motorR2 = 3;
int motorRS = 2;

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

void setup() {
  Serial.begin(1000000);
  pinMode(motorL1, OUTPUT);
  pinMode(motorL2, OUTPUT);
  pinMode(motorR1, OUTPUT);
  pinMode(motorR2, OUTPUT);
  pinMode(motorLS, OUTPUT); 
  pinMode(motorRS, OUTPUT);
}

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
        parseData();
        controlMotors();
        newData = false;
    }
}


void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0';
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}


void parseData() {

    char * strtokIndx;

    strtokIndx = strtok(tempChars,",");
    motorL = atoi(strtokIndx);
 
    strtokIndx = strtok(NULL, ",");
    motorR = atoi(strtokIndx);

    strtokIndx = strtok(NULL, ",");
    directionL = atoi(strtokIndx);

    strtokIndx = strtok(NULL, ",");
    directionR = atoi(strtokIndx);

}

void controlMotors() {
  
  analogWrite(motorLS, motorL);
  analogWrite(motorRS, motorR);

  if (directionL==1) {
    digitalWrite(motorL1, LOW);
    digitalWrite(motorL2, HIGH);
  }
  else {
    digitalWrite(motorL1, HIGH);
    digitalWrite(motorL2, LOW);
  }
  if (directionR==1) {
    digitalWrite(motorR2, LOW);
    digitalWrite(motorR1, HIGH);
  }
  else {
    digitalWrite(motorR2, HIGH);
    digitalWrite(motorR1, LOW);
  }
}