Go Down

Topic: Android, Arduino, and Bluetooth (Read 1 time) previous topic - next topic

TeslaIaint

Aug 13, 2017, 06:31 pm Last Edit: Aug 13, 2017, 08:35 pm by TeslaIaint
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?
Code: [Select]
#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);
}

TeslaIaint

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.

Code: [Select]
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
}

Nick_Pyner

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.

TeslaIaint

#3
Aug 14, 2017, 03:38 am Last Edit: Aug 14, 2017, 05:06 am by TeslaIaint Reason:
Quote
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.

Code: [Select]
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;

  }
}

TeslaIaint

Quote
Is that really true? It doesn't sound like freezing to me.
If it isn't freezing, what do you think it might be?

Quote
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?


Nick_Pyner

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.

TeslaIaint

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.

PaulS

Quote
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?
The art of getting good answers lies in asking good questions.

TeslaIaint

#8
Aug 21, 2017, 12:14 am Last Edit: Aug 21, 2017, 02:30 am by TeslaIaint
Quote
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.

 
Quote
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"

Quote
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"

Code: [Select]
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

Nick_Pyner

thousands? I don't know really.
thousands? I don't know really.
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.

TeslaIaint

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

PaulS

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?
The art of getting good answers lies in asking good questions.

TeslaIaint

#12
Aug 21, 2017, 08:52 pm Last Edit: Aug 22, 2017, 12:40 am by TeslaIaint
I'm not at home to test this, but is this what you have in mind?

Code: [Select]
#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
}

TeslaIaint

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.

PaulS

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.
The art of getting good answers lies in asking good questions.

Go Up