Bluetooth sometimes delayed

Hello,
I am currently building a Nunchuk controlled electric Longboard, using HC-05 Bluetooth modules for communication, an Attiny45 in the Nunchuk and an Arduino Nano in the longboard, controlling a HobbyKing 150a X-car ESC. I am so far happy with how its working out besides one big issue: when controlling the board, it sometimes happens, that it first doesnt react at all, and then suddenly speeds up or slows down (Probably delayed inputs, but it rather feels completely random instead of delayed). The ESC gets controlled via Software Serial PWM Pin 9, and the bluetooth communication is done via Hardware Serial. Here's the Bluetooth reading code.

void recv() {  
  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) {                           //ÜBERPRÜFEN
      recvInProgress = true;
    }
  }
}

void parseData() {      // split the data into its parts
  char * strtokIndx;

  strtokIndx = strtok(tempChars, ",");
  Mode = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  readvalue = atoi(strtokIndx);
}

I currently have no idea what the problem is caused by, but I kind of think it is the Bluetooth communication. Maybe input buffer overflow? Could that end up causing these problems? If yes, how do I prevent it, or in other words: how do I make the arduino only use the newest Info available? I will provide any other data needed, I am just not sure what informations to give.
Also: this never occurs on startup, it only happens a few seconds/minutes after starting

If you reckon it is a code issue, then you need to post all your code.

Are you or have you tried testing without the motor and ESC attached to eliminate any power or interference issues?

I am currently trying to reproduce it without esc and the other stuff, didn't manage to do it though. So power or interference issues could be the cause. However, I kind of think that it also might be a delay of 2 seconds or so when said problem occurs.. what exactly could interfere with the Arduino and how? Can input buffer overflow also cause the issue?
I will post full code as soon as I get home

MJLennox:
I am currently trying to reproduce it without esc and the other stuff, didn't manage to do it though. So power or interference issues could be the cause. However, I kind of think that it also might be a delay of 2 seconds or so when said problem occurs.. what exactly could interfere with the Arduino and how?

Maybe RF generated by the motor. A little loss might cause a delay firstly for the receiver to notice the loss and secondly to perform any required resync.
It might be useful to post a picture or two of your setup if you can. Even if they don't help, project pictures are always interesting :wink:

MJLennox:
Can input buffer overflow also cause the issue?

If there are other parts of the code that prevent the serial being read quickly enough, then sure.

I have now changed the arduino recieving code so that the input buffer clears old messages, when there are more than 2 complete messages available (9 bytes each), since I noticed the input buffer is nearly always on 62 bytes. The arduino gets supplied by a 5V bec, connected to the LiPos, if that helps.
Here's the pictures. The electronics are taped to the board for testing purposes :stuck_out_tongue:


You'll find the other pictures here:

Here's the new code:

void recv() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial.available() > 0 && newData == false) {
    //Serial.println(Serial.available());
    while (Serial.available() > 18 && recvInProgress==false ) {
      for (int i = 0; i < 9; i++) {
        Serial.read();
      }
    }
    rc = Serial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; 
        recvInProgress = false;
        ndx = 0;
        newData = true;

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

Just to make this clear, it doesn't just feel a bit delayed, it feels like I am completely out of control, so that I'll instantly shut everything down.

You need to post the complete program. We know that the code in Serial Input Basics works because many people have tried it. So the problem is probably somwhere else.

...R

Well I wasn't doubting the code. Input buffer overflow was just something I thought might be causing the problem, and because the said code doesn't provide a way of dealing with it I can't really tell if there's a problem caused by it in this particular case. As I said the Input Buffer is always full, which, to me, doesnt seem right.

//Longboard
#include <Arduino.h>
#include <Servo.h>
//------#############----------

//BTPins
#define PIN_R 5
#define PIN_T 7

//ESC Pins
#define PIN_ESC 9

//RPM Pin
#define PIN_RPM 7

//LED
#define PIN_LED 8
Servo ESC1;

//TimeCounters
unsigned long lasttime = 0;
unsigned long initHold = 0;
unsigned long ccSafe = 0;
unsigned long addDelay = 0;
unsigned long slowDelay = 0;

//Recv
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];
int Mode = 0;
int readvalue = 0;
int value = 0; //ReadValue
boolean newData = false;

//Cruisecontrol
int hold = -1; //Speed to hold
int output = 1500;
const int minMS = 1000;
const int maxMS = 2000;
const int INPUTminMS = 1000; //
const int INPUTmaxMS = 2000;
const int neutral = minMS + (maxMS - minMS) / 2; 

//Connection
int issue = 0; //Error?
static int timeout = 200; //Timeout


//For exiting cruise control
int StepSize = -1;

//Main
void setup() {
  ESC1.attach(PIN_ESC);
  Serial.begin(9600);
  newData = false;
}
void loop() {
  unsigned long currentMillis = millis();
  if (issue == 0) {
    recv();
    if (newData == true) {
      strcpy(tempChars, receivedChars);
      parseData();
      value = remap(readvalue);
      if (Mode == 1) { //Cruise Control
        holdSpeed(currentMillis);
        ccSafe = currentMillis; //Save time of last Cruise Control
      }
      else if ((Mode == 0)  && ((currentMillis - ccSafe) < 2000)) { //Make sure exiting cruise control doesnt result in sudden acceleration/breaking
        if ((value == 1500)) { //SlowDown within 2 seconds to neutral
          slowDown(currentMillis, neutral, 2000, 50);
        }
        else if (abs(output - value) > 200 && (accBreak(output, value) != 1)) { 
          slowDown(currentMillis, value, 2000, 50);
        }
        else {
          hold = -1;
          output = value;
          StepSize = -1;
        }

      }
      else {
        hold = -1;
        output = value;
        StepSize = -1;
      }
      newData = false;
      lasttime = currentMillis;
    }
    else if ((currentMillis - lasttime) >= timeout) {
      setissue(1);
      slowDown(currentMillis, neutral, 2000, 70);
      lasttime = currentMillis;
    }
  }
  else if (issue == 1) {
    recv();
    if (currentMillis - lasttime <= 2000) {
      slowDown(currentMillis, neutral, 2000, 70);
    }
    else {
      output = neutral;
    }
    if (newData == true) {
      setissue(0);
      lasttime = currentMillis;
    }

    Serial.print("e "); //debug
  }
  sendtoESC(output);

}
void recv() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Serial.available() > 0 && newData == false) {
    //Serial.println(Serial.available());
    while (Serial.available() > 18 && recvInProgress==false ) {
      for (int i = 0; i < 9; i++) {
        Serial.read();
      }
    }
    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 parseData() {  // split the data into its parts
  char * strtokIndx;

  strtokIndx = strtok(tempChars, ",");
  Mode = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  readvalue = atoi(strtokIndx);
}

int remap(int unmapped) {
  int ret = map(unmapped, INPUTminMS, INPUTmaxMS, minMS, maxMS);
  if (abs(ret - neutral) < 15) {
    ret = neutral;
  }
  return ret;
}

int accBreak(int one, int two) { //check for breaking
  if ((one > 1500 && two > 1500) || (one < 1500 && two < 1500)) {
    return 0;
  }
  else {
    return 1;
  }
}

void slowDown(unsigned long currentM, int toValue, int length, int Steps) {
  int interval = length / Steps;
  StepSize = (output - toValue) / Steps;
  if ((currentM - slowDelay) >= interval) {
    output = output - StepSize;
    slowDelay = currentM;
  }

}

void holdSpeed(unsigned long currentM) {
  int add = 0;
  if (hold == -1) {
    hold = value;
    initHold = currentM;
  }
  else {
    if ((currentM - initHold) > 200 && (currentM - addDelay) > 50) {
      add = (value - 1500) / 100;
      hold = hold + add;
      hold = inBound(hold);
      addDelay = currentM;
    }
    output = hold;
  }
}

int inBound(int checkValue) {
  if (checkValue > maxMS) {
    checkValue = maxMS;
  }
  else if (checkValue < minMS) {
    checkValue = minMS;
  }
  return checkValue;
}



void sendtoESC(int finaloutput) {
  finaloutput = inBound(finaloutput);
  ESC1.writeMicroseconds(finaloutput);
  Serial.println(finaloutput);
}

void setissue(int state) {
  issue = state;
  Serial.print(" ");
  Serial.print(state);
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");
  Serial.println(" ");

}

The second of these lines is not in my code

  while (Serial.available() > 0 && newData == false) {
    //Serial.println(Serial.available());
    while (Serial.available() > 18 && recvInProgress==false ) {

There is an old saying "If it works, don't fix it"

...R

I had it just the way it was in your code, which lead to a constantly filled input buffer (and potentially to the issue, who knows), because the arduino recieves data faster than it can process them and because I had it like that before, and it didn't work, adding these lines cant cause the issue. All the lines I added do is clearing the input buffer so only the newest messages will be processed.

Hmm, I don't see anything that could be causing a big delay in the code and therefore causing the Serial buffer to fill.
I notice you are reading the Bluetooth on the hardware serial (as you stated) nice and slow at 9600 baud. Your original post suggested you have SoftwareSerial in use for the ESC, but I assume that that is a typo and you meant Servo. So you have posted the code for the the Nano, how does it look on the Attiny45? I presume you are using SoftwareSerial (or similar) there and have matched the speed of 9600?

Thanks for your answers!
Yup Servo is correct.

//NUNCHUK
#include <SoftwareSerial.h>
#include <Arduino.h>
//-------#####################-------------


//NunchukPins
#define PIN_B1  A3 //Button1
#define PIN_B2  1  //Button 2
#define PIN_POTI  A2 //Potentiometer

//Output
#define PIN_LED  0 //LED
#define PIN_BT  2 //BT Senden

//Serial
SoftwareSerial BTSerial(-1, PIN_BT);

//Val
boolean cc = false;
int value = 0;
int sendval = 0;

//NunChukVal
int maxN = 500;
int minN = 500;

//mode
int SENDmin = 1000;
int SENDmax = 2000;
int diff = 200;
int Mode = 0;

//time
int current = 0;
int lasttime = 0;
void setup() {
  //Delay for startup
  delay(1000);

  //InputsNunchuk
  pinMode(PIN_B1, INPUT_PULLUP);
  pinMode(PIN_B2, INPUT_PULLUP);
  //Outputs
  pinMode(PIN_LED, OUTPUT);
  BTSerial.begin(9600);
  cali();
}


//Main
void loop() {
  current = millis();
  value = analogRead(PIN_POTI);
  sendval = remap(value);
  if (abs(sendval - 1500) >= 20) {
    lasttime = current;
  }
  if ((digitalRead(PIN_B1) == LOW) && ((current - lasttime) > 2000)) {
    switchmode();
    lasttime = current;
  }
  if (digitalRead(PIN_B2) == LOW) {
    cc = true;
    BTSerial.print("<1,");
  }
  else {
    cc = false;
    BTSerial.print("<0,");
  }
  BTSerial.print(remap(value));
  BTSerial.println(">");
}


//noch keine Verwendung
int remap(int unmapped) {
  if (Mode == 0) {
    return map(unmapped, minN, maxN, SENDmin, SENDmax);
  }
  if (Mode == 1) {
    return map(unmapped, minN, maxN, (SENDmin + diff), (SENDmax - diff));
  }
}

//Modeswitch
void switchmode() {
  if (Mode == 0) {
    Mode = 1;
    ledblink(4, 250, PIN_LED);
  }
  else if (Mode == 1) {
    Mode = 0;
    ledblink(2, 750, PIN_LED);
  }
}

//LedOutput 
void ledblink(int times, int lengthms, int pin) {
  int counter = 0;
  while (counter < times) {
    digitalWrite(pin, HIGH);
    delay(lengthms);
    digitalWrite(pin, LOW);
    delay(lengthms);
    counter++;
  }
}

void cali () { //calibrate Potentiometer
  digitalWrite(PIN_LED, HIGH);
  delay(200);
  int n = 0;
  while (analogRead(PIN_POTI) < 620) {
    delay(10);
  }
  while (n < 20) {
    int temp = analogRead(PIN_POTI);
    if (temp > maxN) {
      maxN = temp;
    }
    delay(100);
    n++;
  }
  digitalWrite(PIN_LED, LOW);
  ledblink(4, 100, PIN_LED);
  delay(200);
  digitalWrite(PIN_LED, HIGH);
  n = 0;
  while (analogRead(PIN_POTI) > 380) {
    delay(10);
  }
  while (n < 10) {
    int temp = analogRead(PIN_POTI);
    if (temp < minN) {
      minN = temp;
    }
    delay(100);
    n++;
  }
  digitalWrite(PIN_LED, LOW);
  ledblink(4, 100, PIN_LED);

Here's the nunchuk code.
I doubt that there's an issue. I have now had the recieving arduino running for 1h and outputtung to Serial monitor, without malfunctioning (checked like every 15 minutes, so I might have missed something, but most likely not)

Something that came into my mind: when I first programming the recieving arduino I used SoftwareSerial not Hardware Serial for communication via Bluetooth. That completely messed up the Servo Output to the ESC leading to random behaviour of the motor. When the issue occurs, the motor kind of does the same.

MJLennox:
I doubt that there's an issue. I have now had the recieving arduino running for 1h and outputtung to Serial monitor, without malfunctioning (checked like every 15 minutes, so I might have missed something, but most likely not)

You mean discarding the extra messages has made it run stable? It would still be nice to know why they are accumulating in the first place.
Was the motor in use for the hour as well?

MJLennox:
Something that came into my mind: when I first programming the recieving arduino I used SoftwareSerial not Hardware Serial for communication via Bluetooth. That completely messed up the Servo Output to the ESC leading to random behaviour of the motor. When the issue occurs, the motor kind of does the same.

Well, I'm guessing that that might be due to both using the same hardware timer and therefore treading on each other's toes.

arduarn:
You mean discarding the extra messages has made it run stable? It would still be nice to know why they are accumulating in the first place.

No, I'ver NEVER ran into the issue when just testing the Arduino connected to my computer (without ESC, etc.). It is dark outside, so I cant really test it with motor right now. No it was not connected to the motor. However, throwing away the old messages has made the response a little bit faster. I suppose the input buffer overflow occured because the sending Arduino can send data faster than the recieving arduino can process, or is that technically impossible?

arduarn:
Well, I'm guessing that that might be due to both using the same hardware timer and therefore treading on each other's toes.

Right! But what I was trying to say is, that the issue I now have, and described in my first post in this thread, reminds me of the behaviour when using SoftwareSerial for Bluetooth + Servo.

MJLennox:
I had it just the way it was in your code, which lead to a constantly filled input buffer

Then please post that complete program.

...R

I did in #6

MJLennox:
I did in #6

You need to be clearer about what you are posting. I had assumed the code in Reply #6 was the full version of the snippet in Reply #4

It would help me to help you if you tell us again in detail exactly what happens when you run the program in Reply #6 and what you want it to do that is different.

...R

You never set newData to false in the below section of your code (from reply #6). Specific reason or bug? As a result, the next call to recv() might start overwriting data in the receivedreceivedChars

  else if (issue == 1) {
    recv();
    if (currentMillis - lasttime <= 2000) {
      slowDown(currentMillis, neutral, 2000, 70);
    }
    else {
      output = neutral;
    }
    if (newData == true) {
      setissue(0);
      lasttime = currentMillis;
    }

    Serial.print("e "); //debug
  }

I have a bit of difficulty following your code. Can you guarantee that slowDown() is always called every time loop() is called.

Your nunchuck code is bombarding the longboard with data regardless if there is updated data or not. Why? Will a few milliseconds delay make a difference?

Good catch on that newData bug!
I don't know exactly what youre trying to say with the SlowDown function. This is just to not make it instantly stop when there's no Bluetooth data arriving.
To Robin:
I see, I'll explain again what's going wrong: Sometime, a few minutes after starting, the Motor seems uncontrollable, it just randomly speeds up or breaks, without me giving the signal to do exactly that, even though the Arduino is still running and connection is stable to the nunchuk. I am not sure if this is caused by code or something else (maybe RF generated by the Motor as already mentioned?). I just posted the code for you guys to get an insight and maybe spot a mistake there.

I have not had a lot of time to look at this now and I probably won't have any time tomorrow so this is just based on a brief look at the code in Reply #6 in the context of the description in Reply #17

Your program is a peculiar mixture of functions (which is good) and a lot of complex code in loop() which makes the whole thing hard to figure out. Have a look at how little there is in loop() in Planning and Implementing a Program and how it reads like a summary of the project.

I don't understand the purpose of the variable issue and I suggest you call the function recv() directly from loop() rather than after any tests.

Are you printing out the values of variables so that you can see what changes when the motor does not behave as it should?

Check carefully that you are not writing past the end of an array and corrupting memory.

...R

The variable issue gets set to 1 if there's been no newData for a certain amount of time. This is because I want a way of dealing with connection loss from bluetooth. when issue gets set to 1, the board slows down within 2 seconds and then stays in neutral until new data arrives. I'll check for the other things once I get home