Serial Write/Read- How to avoid missing a read without sacrificing the loop

I am using the arduino to talk to a robot controller.
It’s critical for me to know at all times what the voltage of both the motor and battery is so that I can initiate certain commands.
The robot controller can send and receive serial commands, hence me using the arduino to probe it.

My question pertains to how I can know these values at all times at 1st priority, but not interrupt what else is happening in the loop. I don’t know how quick the robot controller can send back values and do not want to use a delay to slow down the other processes in the loop.

Can you look at my attempt and advise on how to do this properly?

void loop () {
  int voltagedrv = Serial.write(?V 1); // ask robot controller for motor voltage
     int voltagedrvread = Serial.read();  // read response
  int voltagebat = Serial.write(?V 2); // ask robot controller for battery voltage
     int voltagebatread = Serial.read();  // read response
  
  if( voltagedrvread < voltagebatread ){   // if battery voltage is greater than motor voltage do something
    doSomething;
  }
  else if ( voltagedrvread => voltagebatread){  // if motor voltage is equal or greater than battery voltage do something else
    doSomethingelse;
  }
}

Thanks!

My question pertains to how I can know these values at all times at 1st priority, but not interrupt what else is happening in the loop.

Strange set of priorities. If reading serial data is higher priority, then it should be able to interrupt loop. If not interrupting loop() is more important, then reading serial data is NOT first priority.

So, which is it?

I don't know how quick the robot controller can send back values

Why not? Haven't you looked at the documentation/measure it?

  int voltagedrv = Serial.write(?V 1); // ask robot controller for motor voltage
     int voltagedrvread = Serial.read();  // read response

Here's a hint. It's nowhere near that fast.

99.9% of the time, voltagedrvread will be -1. Not likely what you want.

What do you think is in voltagedrv? Why is that important?

do not want to use a delay to slow down the other processes in the loop.

Why would you have a delay to read serial data?

Batteries can drain in microseconds…?

squid08: Can you look at my attempt and advise on how to do this properly?

Decide how often you want to read the values.

In loop(), use the technique demonstrated in the 'blink without delay' example sketch to send a request for a value at regular intervals. Use a flag to record which variable you requested last time, so you can alternate them. In loop(), test whether a response is available; when it is, read the response and store it. The flag tells you which value the response contains.

If you want to do anything else within loop() other than read these values, then add code to do it. Make sure that none of the code you add does anything that stops (blocks) the execution of loop() - it should be designed as above to detect when something needs to be done and do it, rather than stop and wait with a delay().

PaulS: Strange set of priorities. If reading serial data is higher priority, then it should be able to interrupt loop. If not interrupting loop() is more important, then reading serial data is NOT first priority.

So, which is it?

The wording might not have been clear, but since I am using a bank of ultracapacitors, overvoltage is not an option, therefore I need the arduino to accurately read what the voltage is at the beginning of each loop.

Why not? Haven't you looked at the documentation/measure it?

The controller runs the serial connection at 115200 bit/s. I do not know exactly how it relates to my situation

Here's a hint. It's nowhere near that fast.

99.9% of the time, voltagedrvread will be -1. Not likely what you want.

What do you think is in voltagedrv? Why is that important?

inside of voltagedrv is the voltage level of the motor. when I send the controller the command ?V 1, the controller reports the current value.

Why would you have a delay to read serial data?

Its my only understanding on how to capture the controllers response without missing it.

PeterH:

squid08: Can you look at my attempt and advise on how to do this properly?

Decide how often you want to read the values.

In loop(), use the technique demonstrated in the 'blink without delay' example sketch to send a request for a value at regular intervals. Use a flag to record which variable you requested last time, so you can alternate them. In loop(), test whether a response is available; when it is, read the response and store it. The flag tells you which value the response contains.

If you want to do anything else within loop() other than read these values, then add code to do it. Make sure that none of the code you add does anything that stops (blocks) the execution of loop() - it should be designed as above to detect when something needs to be done and do it, rather than stop and wait with a delay().

Thanks Peter. This sounds like something I am looking for. I will look into it and report back.

Its my only understanding on how to capture the controllers response without missing it.

You are wrong. You will never miss a read, it sits in the buffer until you collect it. You must use serial Avaliable to check it has arrived before trying to read it.

The controller runs the serial connection at 115200 bit/s. I do not know exactly how it relates to my situation

It takes 10 bits to send a byte so you can tell it will do 11520 bytes per second at the most. There might be other delays in your robot in addition to this.

Grumpy_Mike: You are wrong. You will never miss a read, it sits in the buffer until you collect it. You must use serial Avaliable to check it has arrived before trying to read it.

I was actually just reading this as you posted. That is helpful thanks.

If the serial response is a different value each time, is there a way to mark it so that I know which value belong to which request?

I.e. if I'm asking it for two different voltages, how do I know which is which in the buffer?

Unless the responder can tag the data some way the only way to do it is to send a request and then enter a loop that constantly checks if there is data avaliable then read that data. The repeat for the next data you want.

Grumpy_Mike: Unless the responder can tag the data some way the only way to do it is to send a request and then enter a loop that constantly checks if there is data avaliable then read that data. The repeat for the next data you want.

This is the command.

Syntax: ?V [cc] Reply: V=vdr:vmot:v5out

Instead of sending one for each the motor(dr) and the battery(mot), is there a way to tell the arduino which one (1:2:3)? I assume its something along the lines of the array function?

You need to know a bit more about the data coming back. Is it a string separated by : ? If so do you know how long that string is? That is how many bytes long. If so then you need to wait untill they are all in, tat is the serial avaliable shows that there are at least that many bytes in the buffer. You can then read them back either into each byte if the string or assemble them into the two or three variables that the reply contains. Do some tests and find out if this is not documented.

inside of voltagedrv is the voltage level of the motor. when I send the controller the command ?V 1, the controller reports the current value.

No. voltagedrv contains the number of characters you just wrote to the serial port. I fail to see how that information is useful.

Grumpy_Mike: You need to know a bit more about the data coming back. Is it a string separated by : ? If so do you know how long that string is? That is how many bytes long. If so then you need to wait untill they are all in, tat is the serial avaliable shows that there are at least that many bytes in the buffer. You can then read them back either into each byte if the string or assemble them into the two or three variables that the reply contains. Do some tests and find out if this is not documented.

It probably would have been smarter to just show you the entire example. This is from the documentation.

Where: vdr= internal voltage in Volts *10 vmot= main battery voltage in Volts *10 v5out = 5V output on DSub connector in millivolts Examples: Q: ?V R:V=135:246:4730 Q: ?V 3 R:V=4730

The question would be how the controller treats the voltage if it goes below 10v... I.E. 4.2 volts as (042 or 42)

Thanks for the input so far Mike

PaulS:

inside of voltagedrv is the voltage level of the motor. when I send the controller the command ?V 1, the controller reports the current value.

No. voltagedrv contains the number of characters you just wrote to the serial port. I fail to see how that information is useful.

Paul, I appreciate you taking your time to help with my issue, but its obvious that I am not very knowledgeable with programming. I think it would be more beneficial for both of us if there was more explanation, rather than telling me that the my information is not useful. I know that a lot of people come onto this forum wanting others to program their projects for them, but in my case I honestly want to learn. Have a happy thanksgiving. Thanks

The question would be how the controller treats the voltage if it goes below 10v... I.E. 4.2 volts as (042 or 42)

Have you tried connecting it to a battery that outputs less than 10v to see the result? The controller may have a limited voltage span for various reasons.

zoomkat:

The question would be how the controller treats the voltage if it goes below 10v... I.E. 4.2 volts as (042 or 42)

Have you tried connecting it to a battery that outputs less than 10v to see the result? The controller may have a limited voltage span for various reasons.

I don't have the controller yet. I am trying to write as much of the code before I receive it.

The battery supply is limited to 10V, but obviously the motor voltage could be as low as 0.

I found some reference to the strtok command and after some searching I found a previous thread which I took some code from.

How does this look for what I am trying to accomplish?

String command; // hold the incoming command

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

void loop(){
  
  Serial.print ("?V");
  handleCommand();
  
  if (data[0] > data [1]){
    dosomething;
  }
  
}

void serialEvent(){
  while (Serial.available()){
    char incomingByte = (char)Serial.read();
    
     command += incomingByte;
      return;
    }
  }


void handleCommand() {
  
  // variables to hold the command id and the command data
  int id;
  int data[2]; // up to three integers of data
  
  char cmd[command.length()+1];
  command.toCharArray(cmd, command.length()+1);
  char *token = strtok(cmd, ":");
  
  // id
  if (token) {
    id = atoi(token);
    token = strtok(NULL, ":");
  }
  
  // data  
  for (int i = 0; i < 3; i++) {
    
    if (token) {
      data[i] = atoi(token);
      token = strtok(NULL, ":");
    }
    
  }
  
}

How does this look for what I am trying to accomplish?

Lousy. Why are you using a String?

  while (Serial.available()){
    char incomingByte = (char)Serial.read();
    
     command += incomingByte;
      return;
    }

As long as there is serial data to read, read one character and return. Why?

  int data[2]; // up to three integers of data

How do you intend to fit three integers into a two element array?

You shouldn't be calling handleCommand() until you know you have a command to handle.

PaulS:

How does this look for what I am trying to accomplish?

Lousy. Why are you using a String?

I don't know. That is what the code I took from originally had. What would be better? Unsigned long? Will that allow data which is separated by colons?

  while (Serial.available()){
    char incomingByte = (char)Serial.read();
    
     command += incomingByte;
      return;
    }

As long as there is serial data to read, read one character and return. Why?

I realize now that the previous gentleman was using it to find his ! and @ which indicated start and end.

would it be better if I did this?

void serialEvent(){
  while (Serial.available()){
    int incomingByte = Serial.read();
  
    }
  }
  int data[2]; // up to three integers of data

How do you intend to fit three integers into a two element array?

does it not start at 0? 0,1,2 is three? Should I change it to int data[3]

You shouldn't be calling handleCommand() until you know you have a command to handle.

So are you suggesting that I write a command that says "when handleCommand is finished, send a "true" value" which is then used to compare data[0] and data[1]?

I presume this is pseudo-code:

void loop () {
  int voltagedrv = Serial.write(?V 1); // ask robot controller for motor voltage
     int voltagedrvread = Serial.read();  // read response
  int voltagebat = Serial.write(?V 2); // ask robot controller for battery voltage
     int voltagebatread = Serial.read();  // read response
  
  if( voltagedrvread < voltagebatread ){   // if battery voltage is greater than motor voltage do something
    doSomething;
  }
  else if ( voltagedrvread => voltagebatread){  // if motor voltage is equal or greater than battery voltage do something else
    doSomethingelse;
  }
}

It’s hard to advise on how to improve something that obviously won’t compile or work. Just as an example, to write a string like ?V 1 to the serial port you have to quote it:

  Serial.write("?V 1");

You’ve got the IDE there (or you can download it), at least get something that compiles.

Reading serial hints: Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking