Delay when comparing value received via serial

Hello

I need to use arduino's serial communication with python. Passing the 'g' character means I want to know how many encoder ticks have passed since the last read. Any other value received will be a PWM value for the motors, for example "50.55&51.55".

When using the code below, the line containing (if(incomingString.equals("g"))) produces a delay of approximately 4 milliseconds for python to receive the read. When I use the line containing a useless comparison like (if('g'== 0x67)) this 4 millisecond delay does not occur.

Am I doing something wrong? What is the best way to do this?

Also, so that the 4ms delay doesn't occur I need to put ser.write('\n') on the python side. I don't know exactly where my error is if it's on the python side or on the arduino side

thanks

int CurrEncLeft = 40;
int CurrEncRight = 40;

int lastEncLeft = 0;
int lastEncRight = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {

  if (Serial.available() > 0) {
    String incomingString = Serial.readStringUntil('\n');
    
    if(incomingString.equals("g")){
        Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
        lastEncLeft = CurrEncLeft;
        lastEncRight = CurrEncRight;
    }else{
      //analogWrite(pinEsqPwm, fabs(pwmLeft));
      //analogWrite(pinDirPwm, fabs(pwmRight));  
    }

    /*if('g'== 0x67){
        Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
        lastEncLeft = CurrEncLeft;
        lastEncRight = CurrEncRight;
    }*/
    
  }
}

My Python code:

#!/usr/bin/env python
import time 
import serial	

if __name__ == '__main__':    

    ser = serial.Serial('/dev/ttyACM0',115200)   
    time.sleep(2)

    lastTime = time.time()

    while(True):

        ser.write("g\n")
	    print(ser.read_until('\n'))
	
	    ser.write("\n")
	
	    #calc elapsepTime
	    elapsedTime = (time.time() - lastTime)
	    lastTime = time.time()
	    print("elapsedTime: "+str(elapsedTime))

	    time.sleep(0.020)

That is because you are using Serial.readStringUntil('\n');
So it reads the input until either i) a \n is read or b) the input readStringUntil times out (default 1sec)

Simple solution is to send the \n
Re the 4mS
the String incomingString = Serial.readStringUntil('\n'); will take some time
but 4mS seems excessive
also
incomingString.equals("g")
runs the code

unsigned char String::equals(const char *cstr) const
{
	if (len == 0) return (cstr == NULL || *cstr == 0);
	if (cstr == NULL) return buffer[0] == 0;
	return strcmp(buffer, cstr) == 0;
}

again 4mS seems a bit long for that to run also
But you could always do
if ((incomingString.length() == 1) && (incomingString[0] == 'g')) {
instead;

Are you sure the delay in not in the PC side? I have noticed my computer slows down a bit as it runs other programs.

The Serial Basics - Updated post provides examples of much lower level Serial handling that avoids String creation.
Post back here is that solves your problem.

So I ran this code, on UNO

int CurrEncLeft = 40;
int CurrEncRight = 40;

int lastEncLeft = 0;
int lastEncRight = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {

  if (Serial.available() > 0) {
    unsigned long uS_start = micros();
    String incomingString = Serial.readStringUntil('\n'); // does NOT return the \n

    if (incomingString.equals("g")) {
      unsigned long uS_stop = micros();
      Serial.print(F("time for read+cmp ")); Serial.print(uS_stop - uS_start);Serial.println("uS");
      Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
      lastEncLeft = CurrEncLeft;
      lastEncRight = CurrEncRight;
    } else {
      //analogWrite(pinEsqPwm, fabs(pwmLeft));
      //analogWrite(pinDirPwm, fabs(pwmRight));
    }

    /*if('g'== 0x67){
        Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
        lastEncLeft = CurrEncLeft;
        lastEncRight = CurrEncRight;
      }*/

  }
}

and the output for the timer is

time for read+cmp() 92uS
40&40
time for read+cmp() 88uS
0&0
time for read+cmp() 92uS
0&0
time for read+cmp() 92uS
0&0
time for read+cmp() 92uS
0&0
time for read+cmp() 92uS
0&0

So nothing like the 4000uS you are reporting.
So moved the timer below the Serial println() and got

40&40
time for read+cmp+print() 336uS

Still nothing like 4000uS.
So SerialBasics will not fix this for you :frowning: Although it may make the measurements above a bit faster.

@mateusguilherme, your topic has been moved to a more suitable location on the forum. Installation and Troubleshooting is not for problems with your project :wink: See About the Installation & Troubleshooting category

Run on UNO
Just to compare I tried the SerialBasics code -- Edited to prevent resetting start time more then once.

// from https://forum.arduino.cc/t/serial-input-basics-updated/382007
// Example 2 - Receive with an end-marker

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;


int CurrEncLeft = 40;
int CurrEncRight = 40;

int lastEncLeft = 0;
int lastEncRight = 0;


void setup() {
  Serial.begin(115200);
  Serial.println("<Arduino is ready>");
}


void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

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

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

void showNewData() {
  if (newData == true) {
    Serial.print("This just in ... ");
    Serial.println(receivedChars);
    newData = false;
  }
}
unsigned long uS_start;
bool startSet = false;
void loop() {
  if  ((Serial.available() > 0) && (!startSet)) { //start timing
    startSet = true;
    uS_start = micros();
  }
  recvWithEndMarker(); // NOTE this does NOT add the terminating \n (same as readStringUntil('\n') does not add it)
  // showNewData();

  if (newData) {
    if (!strcmp(receivedChars, "g")) { // == 0 if same
      unsigned long uS_stop = micros();
      Serial.print(F("time for read+cmp ")); Serial.print(uS_stop - uS_start); Serial.println("uS");
      //  Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
      lastEncLeft = CurrEncLeft;
      lastEncRight = CurrEncRight;
    } else {
      //analogWrite(pinEsqPwm, fabs(pwmLeft));
      //analogWrite(pinDirPwm, fabs(pwmRight));
    }
    newData = false;
    startSet = false;

    /*if('g'== 0x67){
        Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
        lastEncLeft = CurrEncLeft;
        lastEncRight = CurrEncRight;
      }*/

  }
}
<Arduino is ready>
time for read+cmp 12uS
time for read+cmp 92uS
time for read+cmp 16uS
time for read+cmp 16uS
time for read+cmp 88uS
time for read+cmp 96uS
time for read+cmp 92uS
time for read+cmp 92uS

Seems to be the same as the readStringUntil timings except for the second 12uS timing,
but with a lot more code and opportunities for coding errors :frowning:
Not sure what is happening on the second input. Perhaps someone else can explain that.

The receive buffer was emptied before the full message was received, and it was read in two(or more) chunks. You gave the read timing a new start time with each chunk.

yes, thanks for that, I have edited the sketch to hopefully avoid that.
Still get widely varying times. Any other ideas.

recvWithEndMarker(); needs to be called unconditionally. The test for .available() is within the function.

With the uS-start placed within recvWithEndMarker() on the first character received when ndx == 0 there are very consistent timings.

21:53:54.696 -> <Arduino is ready>
21:54:14.502 -> time for read+cmp 96uS
21:54:17.573 -> time for read+cmp 88uS
21:54:20.227 -> time for read+cmp 92uS
21:54:23.052 -> time for read+cmp 92uS
21:54:26.256 -> time for read+cmp 92uS
21:54:28.675 -> time for read+cmp 92uS
21:54:31.323 -> time for read+cmp 88uS
21:54:34.166 -> time for read+cmp 92uS
21:54:37.133 -> time for read+cmp 88uS
21:54:40.767 -> time for read+cmp 88uS
// from https://forum.arduino.cc/t/serial-input-basics-updated/382007
// Example 2 - Receive with an end-marker

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;
unsigned long uS_start;

int CurrEncLeft = 40;
int CurrEncRight = 40;

int lastEncLeft = 0;
int lastEncRight = 0;


void setup() {
  Serial.begin(115200);
  Serial.println("<Arduino is ready>");
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

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

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      if (ndx == 0) uS_start = micros(); 
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

void showNewData() {
  if (newData == true) {
    Serial.print("This just in ... ");
    Serial.println(receivedChars);
    newData = false;
  }
}

void loop() {

  //  if ((Serial.available() > 0) && (!startSet)) { //start timing
  //    startSet = true;
  //    uS_start = micros();
  //  }
  recvWithEndMarker(); // NOTE this does NOT add the terminating \n (same as readStringUntil('\n') does not add it)
  //showNewData();

  if (newData) {
    if (!strcmp(receivedChars, "g")) { // == 0 if same
      unsigned long uS_stop = micros();
      Serial.print(F("time for read+cmp ")); Serial.print(uS_stop - uS_start); Serial.println("uS");
      //  Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
      lastEncLeft = CurrEncLeft;
      lastEncRight = CurrEncRight;
    } else {
      //analogWrite(pinEsqPwm, fabs(pwmLeft));
      //analogWrite(pinDirPwm, fabs(pwmRight));
    }
    newData = false;
    //startSet = false;

    /*if('g'== 0x67){
        Serial.println(String(CurrEncLeft - lastEncLeft) + "&" + String(CurrEncRight - lastEncRight));
        lastEncLeft = CurrEncLeft;
        lastEncRight = CurrEncRight;
      }*/

  }
}

It would be nice if you would use 's' for seconds, because 'S' has a different meaning.

Thanks for that.

interesting... 92 microseconds is an acceptable delay, I will review the python code, but following the internet examples it seems to be correct. I'm testing it on a 4ghz i5 computer, it should provide good performance.

92 microseconds is an acceptable delay, maybe it's the python code "ser.read_until('\n')", but I see it being used in examples on the internet..

Hi @drmpf , I did a test with my UNO arduino and I also got these same delays, so I believe the 4ms delay is caused by python itself, probably in the code snippet (ser.read_until('\n'))

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.