Android, Arduino, and Bluetooth

I'm using my Android phone (as a remote control) to communicate through bluetooth to my Arduino. The Android is sending x and y axis accelerometer data to the Arduino to steer a two wheel robot. The Android app freezes from time to time, and when that happens the robot continues to do whatever it was doing when when the Android froze. When the Arduino stops receiving data or receives the same data over and over, I would like for the robot to just stop.

I have added a watchdog timer to my Arduino sketch, but it doesn't solve the problem that I have described. The Watchdog.reset(); in its current location does cause the robot to stop when the bluetooth connection becomes disconnected. I would like to keep that.

Any ideas where I could move the reset or add code to stop the robot when the Android freezes?

#include <Adafruit_SleepyDog.h>
#include <DualVNH5019MotorShield.h>
DualVNH5019MotorShield md;
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

// variables to hold the parsed data
int xAxis = 0;
int yAxis = 0;

boolean newData = false;


void setup() {
  int countdownMS = Watchdog.enable(750);
  Serial.begin(57600);
  md.init();
}

void loop() {
 
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    processAndroidData();
    Watchdog.reset();

    // if the first character is 'a' or 'b', control the led
    if (receivedChars[0] == 'a' || receivedChars[0] == 'b')    {
      ledControl();
    }
    // else control the motors
    else
    {
      makeTurns();
    }
    newData = false;

  }
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  unsigned long currentMillis = millis();
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }

      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void processAndroidData() {      // split the data into its parts
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer
}

void makeTurns() {

  //forward
  if ((yAxis >= -10 && yAxis <= -2) && (xAxis > -2 && xAxis < 2)) {
    int syAxis = map(yAxis, -2, -10, 0, 400); //receive data from Android and map to pulse width modulation
    float wL = (float)syAxis;
    //float wR = wL * k;
    float wR = wL;
    md.setSpeeds(wR, wL);
    // md.setM2Speed(wL);
    // md.setM1Speed(wR);
  }

  //backup
  else if ((yAxis <= 10 && yAxis >= 4) && (xAxis > -2 && xAxis < 2)) {
    md.setSpeeds(-110, -110);
  }

  //sharp right
  else if ((yAxis > -2 && yAxis <= 3) && (xAxis <= -2)) {
    md.setM2Speed(110);
    md.setM1Speed(-110);
  }

  //sharp left
  else if ((yAxis > -2 && yAxis <= 3) && (xAxis >= 2)) {
    md.setM2Speed(-110);
    md.setM1Speed(110);
  }

  //straight and right
  else if ((yAxis <= -2 && yAxis >= -10) && (xAxis <= -2 && xAxis >= -10)) {
    turnRight();
  }

  //straight and left
  else if ((yAxis <= -2 && yAxis >= -10) && (xAxis >= 2 && xAxis <= 10)) {
    turnLeft();
  }

  else {
    allStop();
  }
}

void ledControl()
{
  if (receivedChars[0] == 'a')
  {
    // digitalWrite(ledR, LOW);
  }
  if (receivedChars[0] == 'b')
  {
    // digitalWrite(ledR, HIGH);
  }
}


void allStop () {
  md.setBrakes(50, 50);
}

void turnRight () {
  md.setM2Speed(200);
  md.setM1Speed(120);
}

void turnLeft () {
  md.setM2Speed(120);
  md.setM1Speed(200);
}

I tried to change this function to compare two values to see if the android had frozen up. If the android had frozen it would send the same values over and over. If the two values xAxis and xAxis1 match, then the robot would stop. It does not work as intended.

void processAndroidData() {      // split the data into its parts
  unsigned long currentMillis = millis();
  int xAxis1 = 0;
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer
  if (currentMillis - previousMillis >= interval) {  //wait 50 milliseconds to see if
    //xAxis and xAxis1 are the same.
    xAxis1 = atoi(strtokIndx);
  }
  previousMillis = currentMillis;
  if (xAxis1 == xAxis) {
    allStop();
  }
  
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer
}

TeslaIaint:
If the android had frozen it would send the same values over and over.

Is that really true? It doesn't sound like freezing to me. If it is, I guess you need you to work out a way for Arduino to distinguish between identical commands that are sent spuriously, and identical commands sent legitimately.

Is that really true? It doesn't sound like freezing to me. If it is, I guess you need you to work out a way for Arduino to distinguish between identical commands that are sent spuriously, and identical commands sent legitimately.

The app on the phone also has a display of the x and y axis data that is being sent to the Arduino over Bluetooth. When those numbers freeze (i.e. they stop changing as the phone is tilted), the robot continues doing whatever it was doing just before those numbers froze. Also it's very doubtful that the same data would be sent twice in a row. The sensor in the android is very sensitive and the data changes very fast. Even if it's sitting on a table untouched, the data in the display is changing. Even if it did send identical data for a few milliseconds that would be ok. I'm only worried about when it freezes and sends the same data for many seconds or minutes.
I'm testing this function in the above code, but it takes awhile because the Android app doesn't freeze that often.

void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
    lastDataTime = millis();
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    processAndroidData();
    //========Watchdog============
    Watchdog.reset();
    //========Watchdog============
    if ((millis() - lastDataTime) > 500) {
      allStop();
    }
    if (millis() < lastDataTime) {
      lastDataTime = millis(); //fail less horribly on millis overflow
    }
    // if the first character is 'a' or 'b', control the led
    if (receivedChars[0] == 'a' || receivedChars[0] == 'b')    {
      ledControl();
    }
    // else control the motors
    else
    {
      makeTurns();
    }
    newData = false;

  }
}

Is that really true? It doesn't sound like freezing to me.

If it isn't freezing, what do you think it might be?

I guess you need you to work out a way for Arduino to distinguish between identical commands that are sent spuriously, and identical commands sent legitimately.

How might I go about that?

TeslaIaint:
When those numbers freeze (i.e. they stop changing as the phone is tilted), the robot continues doing whatever it was doing just before those numbers froze.

I understand this means that Android is not sending accelerometer data at the precise moment that it should be sending data. I'm afraid I don't feel qualified to comment any further, other than that this is is surely not an Arduino problem, more one of bad choice of Android. You comment suggests the sensor may be sending more spurious signals than kosher ones, and "very sensitive" is actually a euphemism. Perhaps it can be fixed by damping the sensor's output in software. I was going to say, again, Android problem, but I have just realised it may be possible in Arduino. It's still out of my realm though.

I know it isn't an Arduino problem. Once in a while the Android locks up and sends the same x and y axis data over and over. "freezing" I'm working on it on the Android end. I'm not asking about that here. Here I am asking how I might make the robot just stop when it keeps getting the exact same data over and over. Above I have posted some code that I thought might make the robot stop when it receives the same code over and over. It doesn't work. The robot continues to do whatever it was doing when the Android froze. I'm hoping someone might have some suggestions.

Above I have posted some code that I thought might make the robot stop when it receives the same code over and over.

What part of that code compares the data received this time to the data received last time? How many times in a row is receiving the same data OK? After how many times does it become a problem?

What part of that code compares the data received this time to the data received last time?

xAxis compares to xAxis1 after 50 milliseconds. This function is modified from the full sketch on my first post.

How many times in a row is receiving the same data OK?

thousands? I don't know really. A few minutes or more can pass before it "freezes"

After how many times does it become a problem?

thousands? I don't know really. A few minutes or more can pass before it "freezes"

void processAndroidData() {      // split the data into its parts
  unsigned long currentMillis = millis();
  int xAxis1 = 0;
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer
  if (currentMillis - previousMillis >= interval) {  //wait 50 milliseconds to see if
    //xAxis and xAxis1 are the same.
    xAxis1 = atoi(strtokIndx);
  }
  previousMillis = currentMillis;
  if (xAxis1 == xAxis) {
    allStop();
  }
 
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer

TeslaIaint:
thousands? I don't know really.
thousands? I don't know really.

TeslaIaint:
Also it's very doubtful that the same data would be sent twice in a row.

Then I guess, you just compare incoming data with previous received and, if it is the same three times in a row, the data is spurious, and it's time to shut down engines.

I tried to stop the motors after receiving the same data twice. I just posted that in the above code. It seems to have no effect

You have some variables that contain data received this time.
You compare them to variables that are supposed to contain data received last time.

Where do you make the last-time variables contain this-time's data, so that you actually have last-time's data to compare this-time's data to?

I'm not at home to test this, but is this what you have in mind?

#include <Adafruit_SleepyDog.h>
#include <DualVNH5019MotorShield.h>
DualVNH5019MotorShield md;
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing
unsigned long previousMillis = 0;
const long interval = 10;

// variables to hold the parsed data
int xAxis = 0;
int yAxis = 0;
int xAxis1 = 0;
int yAxis1 = 0;

boolean newData = false;


void setup() {
  int countdownMS = Watchdog.enable(750);
  Serial.begin(57600);
  md.init();
}

void loop() {
  unsigned long currentMillis = millis();
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    processAndroidData();
    Watchdog.reset();

    // if the first character is 'a' or 'b', control the led
    if (receivedChars[0] == 'a' || receivedChars[0] == 'b')    {
      ledControl();
    }
    // else control the motors
    else
    {
      makeTurns();
    }
    newData = false;
  }

  //xxxxxxxxxxxxxxxxxxxxxxxxxxx
  // second loop to compare data
  //xxxxxxxxxxxxxxxxxxxxxxxxxxx
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    recvWithStartEndMarkers();
    if (newData == true) {
      strcpy(tempChars, receivedChars);
      // this temporary copy is necessary to protect the original data
      //   because strtok() used in parseData() replaces the commas with \0
      processAndroidData1();
      Watchdog.reset();

      if (xAxis == xAxis1) {   //compares to check if android is "frozen" and sending duplicate data
        allStop();
      }

      // if the first character is 'a' or 'b', control the led
      if (receivedChars[0] == 'a' || receivedChars[0] == 'b')    {
        ledControl();
      }
      // else control the motors
      else
      {
        makeTurns();
      }
      newData = false;

    }
  }
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }

      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void processAndroidData() {      // split the data into its parts
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer
}

void processAndroidData1() {      // split the data into its parts
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis1 = atoi(strtokIndx);     // convert this part to an integer
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis1 = atoi(strtokIndx);     // convert this part to an integer
}

That sketch behaves as the previous one. I tried to get the xAxis and yAxis data from the Android then get xAxis1 to compare them. If they were the same, then allStop(); This does not work. The motors continue turning in the direction that the Android is telling it to turn according to the x axis and y axis data it is sending. I'm stumped.

You need to receive data ONCE on each pass through loop(). When you have complete data, call the parseAndroidData() function to populate xAxis and yAxis.

After you compare xAxis to xAxis1 and yAxis to yAxis1, set xAxis1 to xAxis and set yAxis1 to yAxis. Delete the parseAndroidData1() function. It is NOT how you populate xAxis1 and yAxis1.

I'm stuck on how to do that correctly. I'm thinking of a for loop, but I don't know how to populate the data unless every function is duplicated. I started this sketch, but it's not done, and it isn't finished because I know it's wrong.

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing
char receivedChars1[numChars];
char tempChars1[numChars];        // temporary array for use when parsing
unsigned long previousMillis = 0;
const long interval = 10;

// variables to hold the parsed data
int xAxis = 0;
int yAxis = 0;
int xAxis1 = 0;
int yAxis1 = 0;
int i = 0;

boolean newData = false;




void setup() {



  Serial.begin(230400);  //230400  460800   921600

}

void loop() {
  for (i = 0; i < 2; i++) {
    if (i == 0) {
      recvWithStartEndMarkers();
      if (newData == true) {
        strcpy(tempChars, receivedChars);
        // this temporary copy is necessary to protect the original data
        //   because strtok() used in parseData() replaces the commas with \0
      }
    }
        if (i == 1) {
      recvWithStartEndMarkers1();
      if (newData == true) {
        strcpy(tempChars, receivedChars);
        // this temporary copy is necessary to protect the original data
        //   because strtok() used in parseData() replaces the commas with \0
      }
    }
  }
  processAndroidData();
  // if the first character is 'a' or 'b', control the led
  if (receivedChars[0] == 'a' || receivedChars[0] == 'b')    {
    ledControl();
  }
  // else control the motors
  else
  {
    makeTurns();
  }
  newData = false;
  
  if (i==1) {   //reset counter so it resets loop or else it would only repeat once
    i=0;
  }
}


void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }

      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void recvWithStartEndMarkers1() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars1[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }

      else {
        receivedChars1[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void processAndroidData() {      // split the data into its parts
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer
}

void makeTurns() {
}


void ledControl()
{
  if (receivedChars[0] == 'a')
  {
    // digitalWrite(ledR, LOW);
  }
  if (receivedChars[0] == 'b')
  {
    // digitalWrite(ledR, HIGH);
  }
}



void allStop () {
  //md.setBrakes(70, 70);
}

I'm thinking of a for loop

To do what?

char receivedChars1[numChars];
char tempChars1[numChars];        // temporary array for use when parsing

Wrong.

Suppose that I hand you a post it note with two numbers written on it. You have a pencil and paper. Can you tell that the post it note has numbers on it that match what the last post it note had? How many post it notes do you need to have to determine that?

Hint: the answer to the last question is ONE.

The post it note corresponds to the receivedChars array. You read the data ONCE/get a post it note once.

When you get the first note, you have NO previous data (your paper is blank), so obviously the note contains numbers that don't match what you got last time.

So, you do something with the numbers AND YOU WRITE THEM DOWN. Throw the post it note away; you are done with it.

Now, you get another note. It either contains new data or it contains the same data as last time. How do you know? Well, either the numbers on the post it note match what you wrote down, or they don't.

Now, which variables correspond to the data on the post it note? Which variables correspond to the data on the paper?

Am I on the right track here?

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        if (rc != receivedChars[ndx] {   //MAKE SURE THEY DON'T MATCH LAST LOOP DATA
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      }
...

Am I on the right track here?

        if (rc != receivedChars[ndx] {   //MAKE SURE THEY DON'T MATCH LAST LOOP DATA

No.

You want to read and store all the data. You want to parse the data, when the end of the record arrives.

It is the parsed data that you want to compare to the previously parsed data.

Is this close?

void processAndroidData() {      // split the data into its parts
  xAxis1 = xAxis; //variable to compare data from last loop
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");
  xAxis = atoi(strtokIndx);     // convert this part to an integer
  if (xAxis==xAxis1) {  //if variables match, then Android is "frozen" - stop motors
    allStop();
  }
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  yAxis = atoi(strtokIndx);     // convert this part to an integer
  
}