Endless Loop waiting for Serial.read

I'm trying to get this function to run that takes in a speed input followed by a directional input. So far the speed input has been verified however I'm getting stuck in a loop if I input anything other than 0. I believe it's some syntax issue with the loop structure but am unsure how to correct it. Any guidance would be appreciated.

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(13,OUTPUT);
}

void loop() {
  motor_speed();
}

int motor_speed(){
      int input_speed = 0;
      int output_speed = 0;
      
      do{
  if (Serial.available() > 0) {    // is a character available?
      Serial.println("Input motor rate from 0 to 9...");
    input_speed = Serial.read();       // get the number
    // check if a number was received
    if ((input_speed >= '0') && (input_speed <= '9')) {
      Serial.print("Speed selected: ");
            output_speed = (input_speed-48) * 28.34;
      Serial.println(output_speed);
    }
    else {
      Serial.println("Not a number.");
    }
  } // end: if (Serial.available() > 0)  
    }while((input_speed < '0') && (input_speed < '9'));

  int drive = 0;
    if(output_speed==0){  //Motor speed 0 selected
      Serial.println("Motor stopped");
      analogWrite(6,output_speed); //Controls a PWM signal on pin 6
      //Setting 7 and 8 to LOW will open entire H-Bridge and coast to stop
      digitalWrite(7,LOW); 
      digitalWrite(8,LOW);
      digitalWrite(13,HIGH);
      delay(3000);} //wait three seconds
      
    else if(output_speed!=0){  //Motor speed not 0
      do{
  Serial.println("Input (F)orward or (R)everse...");
  drive = Serial.read();
          }while (drive!=70||drive!=102||drive!=82||drive!=114);
        if (drive==70||drive==102){  //Set drive to "Forward"
          analogWrite(6,output_speed); //Controls a PWM signal on pin 6
          digitalWrite(13,LOW);
          Serial.println("Forward");
          digitalWrite(7,HIGH);
          digitalWrite(8,LOW);
          delay(3000);  }//wait three seconds
      else if(drive==82||drive==114){  //Set drive to "Reverse"
          analogWrite(6,output_speed); //Controls a PWM signal on pin 6
          Serial.println("Reverse");
          digitalWrite(7,LOW);
          digitalWrite(8,HIGH);
          digitalWrite(13,LOW);
          delay(3000);}//wait three seconds
          }}//Return to loop
    }while((input_speed < '0') && (input_speed < '9'));

So, suppose the user enters a 'Z'. That's not less than '9', so the do/while statement ends.

Suppose that they enter a '('. That's less then '0', so the do/while ends.

Suppose that they enter a '4'. That's not less '0', so what happens?

I suspect that you want the while clause to require the value be greater than or equal '0' and less than or equal '9' to continue.

PaulS:
I suspect that you want the while clause to require the value be greater than or equal '0' and less than or equal '9' to continue.

This particular clause I had running previously as a standalone function, and it would kick back if the value was not in the indicated range "Not a number." I set up a second function with a do while clause to then select the motor direction I have encountered this endless loop problem. The idea was to replicate the first clause but for F or R.

"Input motor rate from 0 to 9...
Speed selected: 0
Motor stopped
Input motor rate from 0 to 9...
Speed selected: 56
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
Input (F)orward or (R)everse...
.
.
."

When I remove the second do while clause it does prompt for forward or reverse but doesn't activate motor motion, which are proven functions.

@scotdani, you should NOT be reading serial input in a function called motor_speed(). Make a separate function for receiving the data. That way problems are much easier to solve.

There are ready-made functions for receiving serial data reliably in Serial Input Basics

...R

Robin2:
@scotdani, you should NOT be reading serial input in a function called motor_speed(). Make a separate function for receiving the data. That way problems are much easier to solve.

There are ready-made functions for receiving serial data reliably in Serial Input Basics

...R

I'm still quite new to the Arduino language so bare with me. Since one requirement takes in a single number and the other takes in a single letter, should there be two separate receiving functions or treat them both as a single char and repeat the function, like the one in Example 1?

Since one requirement takes in a single number

42 is a single number, but not a single digit number. Is that what you mean by single number?

PaulS:
42 is a single number, but not a single digit number. Is that what you mean by single number?

To clarify, single digit number 0-9 (first input) followed by F/f or R/r (second input).

scotdani:
I'm still quite new to the Arduino language so bare with me.

No thank you.

Since one requirement takes in a single number and the other takes in a single letter, should there be two separate receiving functions or treat them both as a single char and repeat the function, like the one in Example 1?

First of all, you don't need 2 receiving functions as there is only one serial channel through which data can arrive.

And wouldn't it be much simpler to send the data in one message in the style <9,F> as in Example 3?

...R

You can't have 2 functions doing serial input. What if the f/r input sees a 7? It's not expecting a 7 so it throws away the 7. There is no f or r arrived yet (serial is slow) so it must exit. The motor is still spinning so you cannot wait.

Then later, the number input sees a f. It's not expecting an f so it throws that away and exits.

You see the pattern here?

Robin2:
And wouldn't it be much simpler to send the data in one message in the style <9,F> as in Example 3?

...R

I'll look at both approaches and go from there. Regarding the examples, which variables would be the ones to alter for my own purposes?

MorganS:
You can't have 2 functions doing serial input.

I reached out to one of the GA's and he said I would need a (Serial.read() > 0) every time Serial.read is used; not sure if that is the same as what you are saying. I'm going to try it out as well since I have this much of the code done already.

scotdani:
Regarding the examples, which variables would be the ones to alter for my own purposes?

Spend a bit of time trying to figure that out yourself. If you can't get it to work post the code that represents your best attempt and tell us what it actually does.

I reached out to one of the GA's and he said I would need a (Serial.read() > 0) every time Serial.read is used;

If that is what was actually said it makes absolutely no sense.

...R

I reached out to one of the GA's and he said I would need a (Serial.read() > 0) every time Serial.read is used; not sure if that is the same as what you are saying. I'm going to try it out as well since I have this much of the code done already.

I suspect that what he said (or meant) was that you need to do call Serial.available() and test the return value against 0 before you do a Serial.read().

That is NOT what MorganS was suggesting.

Robin2:
If that is what was actually said it makes absolutely no sense.

...R

Could he perhaps be referring to the "Clear Input Buffer" in your example post? The first serial.read I sourced from public code for the numeric input has this, or a very similar, clause. If I understand the logic correctly this could correct the faulty f/r sequence with the flow I am working trying? I'm flying at the seat of my pants so be gentle if I'm wrong.

while (Serial.available() > 0) {
   Serial.read();
}

I really don't understand your problem. If there is serial data to read (available() > 0), read it. You either read a direction value (f or r) or a digit. You know what to do with either one, so just do it. If you read something else, ignore it.

PaulS:
You know what to do with either one, so just do it. If you read something else, ignore it.

I have not had the opportunity to test any of these ideas yet, so call it data collecting for the next attempts. I know what it needs to do, wish to learn how to make that happen, and reproduce it to some degree.

So long as you're happy with just single-digit speeds, then you can make this pretty simple. Note that you could use letters A-Z if you want to have 26 speeds instead of 10. (Then pick different characters for forwards and reverse.)

void loop() {
  checkSerial();
  motorRun();
}

void checkSerial() {
  if(Serial.available()) {
    char c = Serial.read();
    if(c>='0' && c<='9') {
      output_speed = (c-'0') * SPEED_MULTIPLIER; 
    }
    if(c == 'f' || c=='F') {
      drive = FORWARDS;
    }
    if(c == 'r' || c=='R') {
      drive = REVERSE;
    }
  }
}

Note that output_speed and drive must be global variables. The all-caps names are constants declared at the top of the program so they can be used anywhere. The motorRun() function is going to look a lot like your motor_speed() but it doesn't have any inputs - it just looks at the global variables.

MorganS:
Note that output_speed and drive must be global variables. The all-caps names are constants declared at the top of the program so they can be used anywhere. The motorRun() function is going to look a lot like your motor_speed() but it doesn't have any inputs - it just looks at the global variables.

Thank you for this snippet of code. I have the motor responding to speed inputs but only forward or stopped.

int output_speed;
char drive;
char FORWARD;
char REVERSE;

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(13,OUTPUT);
}

void loop() {
  checkSerial();   //Read input from serial monitor
  motorRun();     //Activate motor based on input information
}

void checkSerial() {
  if(Serial.available()) {
    char c = Serial.read();
    if(c>='0' && c<='9') {
      output_speed = (c-'0') * 28.34; 
    }
    if(c == 'f' || c=='F') {
      drive = FORWARD;
    }
    if(c == 'r' || c=='R') {
      drive = REVERSE;
    }
  }
}

int motorRun(){
    if(output_speed==0){  //Motor speed 0 selected
      Serial.println("Motor stopped");
      analogWrite(6,output_speed); //Controls a PWM signal on pin 6
      //Setting 7 and 8 to LOW will open entire H-Bridge and coast to stop
      digitalWrite(7,LOW); 
      digitalWrite(8,LOW);
      digitalWrite(13,HIGH);
      delay(3000); //wait three seconds  
        }
    else if(output_speed!=0){  //Motor speed not 0
      if (drive==FORWARD){  //Set drive to "Forward"
          analogWrite(6,output_speed); //Controls a PWM signal on pin 6
          digitalWrite(13,LOW);
          Serial.print("Forward at ");
          Serial.println(output_speed);
          digitalWrite(7,HIGH);
          digitalWrite(8,LOW);
          delay(3000);  }  //wait three seconds
      else if (drive==REVERSE){  //Set drive to "Reverse"
          analogWrite(6,output_speed); //Controls a PWM signal on pin 6
          Serial.print("Reverse at ");
          Serial.println(output_speed);
          digitalWrite(7,LOW);
          digitalWrite(8,HIGH);
          digitalWrite(13,LOW);
          delay(3000);  } //wait three seconds
          } } //Return to loop

do{
Serial.println("Input (F)orward or (R)everse...")

drive = Serial.read();

you are reading empty Serial buffer - there is no code to fill it util this point
so
what is the value of "drive" here ?

}while (drive!=70||drive!=102||drive!=82||drive!=114);

does while checks for "0"?

char FORWARD;

char REVERSE;

The convention is that ALL_CAPS is a constant. That is something which has a value. Try...

const char FORWARD=true;
const char REVERSE=false;
          } } //Return to loop

This is going to make the code hard to read in the future. Run the auto-format function in the Arduino editor. It will split this into two lines for you. It will also fix up the many other places where you have a closing } on the end of a line.

The auto-format is very good. If it appears to scramble your code then your code was wrong.