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
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
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.
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 Although it may make the measurements above a bit faster.
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
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.
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;
}*/
}
}
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.
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'))