while loop stuck

In this sketch I’m receiving data from an Android, and I get stuck in the while loop. Once the sketch gets into the while loop, it never reads the button to see if the state has changed. I tested it by changing the “while” to “if” and that allows the state to change. But then the sketch doesn’t read the incoming data. Any ideas how I can fix this? thanks in advance.

char myChar = 'a';
String string;
char LorR;

int M2INA = 8;   //left
int M2INB = 7;
int M2PWM = 9;

int M1INA = 11;  //right
int M1INB = 12;
int M1PWM = 10;

int ledB = A2;
int ledR = A3;
int button = 2;

int buttonState;
int val;
int mode = 0;

void setup() {

  pinMode(M1INA, OUTPUT);
  pinMode(M1INB, OUTPUT);
  pinMode(M2INA, OUTPUT);
  pinMode(M2INB, OUTPUT);
  pinMode(M1PWM, OUTPUT);
  pinMode(M2PWM, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledR, OUTPUT);
  Serial.begin(57600);  //230400  460800   921600

  buttonState = digitalRead(button);   // read the initial state
}

void loop() {
  val = digitalRead(button);      // read input value and store it in val

  if (val != buttonState) {          // the button state has changed!
    if (val == LOW) {                // check if the button is pressed
      if (mode == 0) {          // light is off
        mode = 1;               // light is on!

      } else {
        mode = 0;               // light is on!

      }
    }
  }
  buttonState = val;                 // save the new state in our variable
  if (mode == 0) {
    btMode();
  }
  if (mode == 1) {
    followMode();
  }

}

void followMode() {
  digitalWrite (ledR, HIGH);
  digitalWrite (ledB, LOW);
    }


void btMode() {
  digitalWrite (ledR, LOW);
  digitalWrite (ledB, HIGH);
  
    string = "";
    //Add to string
    
    while (true) {

      myChar = Serial.read();
      if (32 <= myChar && myChar <= 127) {
        string += myChar;
      }
      if (myChar == ':'){
        break;
      
    }// While End
    }
    //Analyse string
    if (string != "off:" && string != "on:") {
      if (string != "L0:" && string != "R0:") {
        //code for setting motor left or right
        if (string[0] == 'L') {
          LorR = 'L';
        } else {
          LorR = 'R';
        }
        string.remove(0, 1);
        //code for putting it in reverse
        if (string[0] == '-') {
          string.remove(0, 1);
          string.remove((string.length() - 1), 1);
          //Serial.println(string);
          if (LorR == 'L') {
            //digitalWrite(M2EN, HIGH);
            digitalWrite (M2INA, LOW);
            digitalWrite (M2INB, HIGH);
            analogWrite(M2PWM, string.toInt());

          } else {                           //Reverse left
            //digitalWrite(M1EN, HIGH);
            digitalWrite (M1INA, LOW);
            digitalWrite (M1INB, HIGH);
            analogWrite(M1PWM, string.toInt());
          }
        } else {
          string.remove((string.length() - 1), 1);
          if (LorR == 'L') {
            //digitalWrite(M2EN, HIGH);
            digitalWrite (M2INA, HIGH);
            digitalWrite (M2INB, LOW);
            analogWrite(M2PWM, string.toInt());

          } else {
            digitalWrite (M1INA, HIGH);
            digitalWrite (M1INB, LOW);
            analogWrite(M1PWM, string.toInt());
          }
        }
      } else {
        allStop();
      }

    }//End of long if statment
    if (string == "on:") {
      
    }
    if (string == "off:") {
      
    }    
  }


void allStop () {  //coast
  analogWrite(M1PWM, 0);
  analogWrite(M2PWM, 0);
  digitalWrite(M1INA, HIGH);
  digitalWrite(M2INA, HIGH);
  digitalWrite(M1INB, HIGH);
  digitalWrite(M2INB, HIGH);
}
  while (true)
  {
    myChar = Serial.read();
    if (32 <= myChar && myChar <= 127)
    {
      string += myChar;
    }
    if (myChar == ':')
    {
      break;
    }// While End
  }

Do you ever receive a colon ?
Have you tried printing what you receive ?

yes, a colon is received. The data from the android looks like this:

L178:
R215:

L-125:
R-115:

The android app computes the PWM positive or negative
PWM data is sent for both the L and R motors
While the Android app is running, I can see on the phone screen the same data that is sent to the Arduino.

While the Android app is running, I can see on the phone screen the same data that is sent to the Arduino.

With respect, seeing the data on the 'phone is not the same as seeing what is received.

Does the break; end the code block for the if test or for the while ?

Try this
declare inputComplete as a global variable and set it to false

  while (!inputComplete)
  {
    myChar = Serial.read();
    if (32 <= myChar && myChar <= 127)
    {
      string += myChar;
    }
    if (myChar == ':')
    {
      inputComplete = true;
    }
  }

With respect, seeing the data on the 'phone is not the same as seeing what is received.

You're right. In earlier versions of the code, I used SoftwareSerial to read the incoming data to the
Monitor. So I assumed the sketch is still reading the ":"

Does the break; end the code block for the if test or for the while ?

ends the while loop (or it's intended to)

I don't even know what that while(true) meant in the sketch as I first posted it. while what was true? (This sketch was found on a tutorial)

Anyway, It works fine exactly how you suggested. Thanks!!!

I don't even know what that while(true) meant in the sketch as I first posted it. while what was true?

Nothing in particular has to be true to satisfy the condition which means that it can never be false and will, therefore, loop forever.

I spoke too soon when I said that this sketch works. Unfortunately the button only switches modes when bluetooth is connected. I would like the button mode to be able to switch when the bluetooth is not connected. Any ideas where this is getting stuck?

Post your updated code :wink:

Oh, ok. It’s same as above. And I did reverify that I’m receiving the ‘:’ character. The void btMode() while loop is where the issue lies.

char myChar = 'a';
String string;
char LorR;

int M2INA = 8;   //left
int M2INB = 7;
int M2PWM = 9;

int M1INA = 11;  //right
int M1INB = 12;
int M1PWM = 10;

int ledB = A2;
int ledR = A3;
int button = 2;

int buttonState;
int val;
int mode = 0;

void setup() {

  pinMode(M1INA, OUTPUT);
  pinMode(M1INB, OUTPUT);
  pinMode(M2INA, OUTPUT);
  pinMode(M2INB, OUTPUT);
  pinMode(M1PWM, OUTPUT);
  pinMode(M2PWM, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledR, OUTPUT);
  Serial.begin(57600);  //230400  460800   921600

  buttonState = digitalRead(button);   // read the initial state
}

void loop() {
  val = digitalRead(button);      // read input value and store it in val

  if (val != buttonState) {          // the button state has changed!
    if (val == LOW) {                // check if the button is pressed
      if (mode == 0) {          // light is off
        mode = 1;               // light is on!

      } else {
        mode = 0;               // light is on!

      }
    }
  }
  buttonState = val;                 // save the new state in our variable
  if (mode == 0) {
    btMode();
  }
  if (mode == 1) {
    followMode();
  }

}

void followMode() {
  digitalWrite (ledR, HIGH);
  digitalWrite (ledB, LOW);
    }


void btMode() {
  digitalWrite (ledR, LOW);
  digitalWrite (ledB, HIGH);
 
    string = "";
    //Add to string
   
    while (true) {

      myChar = Serial.read();
      if (32 <= myChar && myChar <= 127) {
        string += myChar;
      }
      if (myChar == ':'){
        break;
     
    }// While End
    }
    //Analyse string
    if (string != "off:" && string != "on:") {
      if (string != "L0:" && string != "R0:") {
        //code for setting motor left or right
        if (string[0] == 'L') {
          LorR = 'L';
        } else {
          LorR = 'R';
        }
        string.remove(0, 1);
        //code for putting it in reverse
        if (string[0] == '-') {
          string.remove(0, 1);
          string.remove((string.length() - 1), 1);
          //Serial.println(string);
          if (LorR == 'L') {
            //digitalWrite(M2EN, HIGH);
            digitalWrite (M2INA, LOW);
            digitalWrite (M2INB, HIGH);
            analogWrite(M2PWM, string.toInt());

          } else {                           //Reverse left
            //digitalWrite(M1EN, HIGH);
            digitalWrite (M1INA, LOW);
            digitalWrite (M1INB, HIGH);
            analogWrite(M1PWM, string.toInt());
          }
        } else {
          string.remove((string.length() - 1), 1);
          if (LorR == 'L') {
            //digitalWrite(M2EN, HIGH);
            digitalWrite (M2INA, HIGH);
            digitalWrite (M2INB, LOW);
            analogWrite(M2PWM, string.toInt());

          } else {
            digitalWrite (M1INA, HIGH);
            digitalWrite (M1INB, LOW);
            analogWrite(M1PWM, string.toInt());
          }
        }
      } else {
        allStop();
      }

    }//End of long if statment
    if (string == "on:") {
     
    }
    if (string == "off:") {
     
    }   
  }


void allStop () {  //coast
  analogWrite(M1PWM, 0);
  analogWrite(M2PWM, 0);
  digitalWrite(M1INA, HIGH);
  digitalWrite(M2INA, HIGH);
  digitalWrite(M1INB, HIGH);
  digitalWrite(M2INB, HIGH);
}

Maybe read Robin's updated Serial Input Basics thread how to read serial data properly.

And Robin's Demonstration code for several things at the same time might also be of help.

Yes, I have tried Robin’s method. You even helped me with it here. The incoming data still freezes using that method. This method does not. This method works other than if the bluetooth is not connected, then the button never gets read to check the state.

Maybe if I add this: if (Serial.available() > 0) to the while loop, it will never get into the while loop when the bluetooth is not connected; therefore the button will be read. I will have to try it when I get home.

void btMode() {
  digitalWrite (ledR, LOW);
  digitalWrite (ledB, HIGH);

  string = "";
if (Serial.available() > 0){
  while (true) {
    myChar = Serial.read();
    if (32 <= myChar && myChar <= 127) {
      string += myChar;
    }
    if (myChar == ':') {
      break;
    }
  } // While End
}

//analyse string
}

Your code is wrong. You must never call Serial.read() without first checking that there is at least one
character available using Serial.available(). Otherwise read() will return nonsense to you.

In general if you use a while loop, nothing else can progress, you have blocked stuff.

If you use just an if within the loop function for each aspect of your project, they can all progress
as fast as they individually need, but you need to manage state explicitly - google "state-machine"

Its more compicated to code, but it then makes combining parts of a sketch really easy.

Your code is wrong. You must never call Serial.read() without first checking that there is at least one
character available using Serial.available(). Otherwise read() will return nonsense to you.

Isn't that what I have in reply #10? I may have snuck that post in before you posted your reply.

Isn't that what I have in reply #10?

No. You check once whether at least one byte is available then proceed to read bytes without checking again.

what? can you be more specific?

what? can you be more specific?

if (Serial.available() > 0){ // check once for incoming data
  while (true) {
    myChar = Serial.read(); // read over and over and over and over
    if (32 <= myChar && myChar <= 127) {
      string += myChar;
    }
    if (myChar == ':') {
      break;
    }
  } // While End
}

//analyse string
}

like this?

while (Serial.available() > 0){ 
    myChar = Serial.read(); // read over and over and over and over
    if (32 <= myChar && myChar <= 127) {
      string += myChar;
    }
    if (myChar == ':') {
      break;
    }

}

//analyse string
}

The problem was that I could not break the while loop to read the button, but this now works. I changed this

  while (true) {

to

while (mode == 0) {
readButton();

This allows me to read the button (again) and break the while loop if needed

char myChar = 'a';
String string;
char LorR;

int M2INA = 8;   //left
int M2INB = 7;
int M2PWM = 9;

int M1INA = 11;  //right
int M1INB = 12;
int M1PWM = 10;

int ledB = A2;
int ledR = A3;
int button = 2;

int buttonState;
int val;
int mode = 0;

unsigned long previousButtonMillis=0;
const int buttonInterval = 50; // number of millisecs between button reading
unsigned long previousSerialReadMillis=0;
const int serialReadInterval = 10; // number of millisecs between button reading

void setup() {

  pinMode(M1INA, OUTPUT);
  pinMode(M1INB, OUTPUT);
  pinMode(M2INA, OUTPUT);
  pinMode(M2INB, OUTPUT);
  pinMode(M1PWM, OUTPUT);
  pinMode(M2PWM, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledR, OUTPUT);
  Serial.begin(57600);  //230400  460800   921600

  buttonState = digitalRead(button);   // read the initial state
}

void loop() {
  
  readButton();
  if (mode == 0) {
    btMode();
  }
  if (mode == 1) {
    followMode();
  }
  
}

void readButton() {
   if (millis() - previousButtonMillis >= buttonInterval) {   
  val = digitalRead(button);      // read input value and store it in val
  if (val != buttonState) {          // the button state has changed!
    if (val == LOW) {                // check if the button is pressed
      if (mode == 0) {          
        mode = 1;               
      } else {
        mode = 0;               
      }
    }
  } 
  buttonState = val;                 // save the new state in our variable
  previousButtonMillis += buttonInterval;
}
}

void followMode() {
  digitalWrite (ledR, HIGH);
  digitalWrite (ledB, LOW);
}

void btMode() {
  digitalWrite (ledR, LOW);
  digitalWrite (ledB, HIGH);
if (millis() - previousSerialReadMillis >= serialReadInterval){
  string = "";

  while (mode == 0) {
    readButton();
    myChar = Serial.read();
    if (32 <= myChar && myChar <= 127) {
      string += myChar;
    }
    if (myChar == ':') {
      break;
    }
  }
  previousSerialReadMillis += serialReadInterval;
  } // While End
  analyseString();
}

void  analyseString() {

  if (string != "L0:" && string != "R0:") {
    //code for setting motor left or right
    if (string[0] == 'L') {
      LorR = 'L';
    } else {
      LorR = 'R';
    }
    string.remove(0, 1);
    //code for putting it in reverse
    if (string[0] == '-') {
      string.remove(0, 1);
      string.remove((string.length() - 1), 1);
      if (LorR == 'L') {
        digitalWrite (M2INA, LOW);
        digitalWrite (M2INB, HIGH);
        int leftPWM = string.toInt();
        if (leftPWM > 80) {
          leftPWM = 80;
        }
        analogWrite(M2PWM, leftPWM);

      } else {                           //Reverse left
        digitalWrite (M1INA, LOW);
        digitalWrite (M1INB, HIGH);
        int rightPWM = string.toInt();
        if (rightPWM > 80) {
          rightPWM = 80;
        }
        analogWrite(M1PWM, rightPWM);
      }
    } else {
      string.remove((string.length() - 1), 1);
      if (LorR == 'L') {
        digitalWrite (M2INA, HIGH);
        digitalWrite (M2INB, LOW);
        analogWrite(M2PWM, string.toInt());

      } else {
        digitalWrite (M1INA, HIGH);
        digitalWrite (M1INB, LOW);
        analogWrite(M1PWM, string.toInt());
      }
    }
  } else {
    allStop();
  }
  //End of long if statment
}

void allStop () {

  analogWrite(M1PWM, 125);
  analogWrite(M2PWM, 125);
  digitalWrite(M1INA, HIGH);
  digitalWrite(M2INA, HIGH);
  digitalWrite(M1INB, HIGH);
  digitalWrite(M2INB, HIGH);
}