HC05 + ELM327 - weird response after a while

Hi,
This is my first post, so hello everyone :).
Recently I struggle with a weird response from ELM327. It is acting correctly when I send/receive commands every 500+ms, but when I try do the same with 100ms intervals (I know, that ECU should not be queted more often than 100ms) - finally I get weird resonse. Additionaly, reseting NANO does not help. It starts acting properly after a few minutes of being disconnected.
The goal: get 5 PIDs with ~100ms intervals between each other.
Circuit: NANO, HC05 connected with 1k/2k voltage devider. Microswitch connected to D10 activates power (D12) to HC05 (through transistor).

The full code here:

#include <SoftwareSerial.h>
#define RxD 3
#define TxD 4
#define LED_BLINK_INTERVAL 500
#define LED_PIN 5
#define RPM "010C" 
#define IAT "010F"
#define CLT "0105"
#define MAF "0110" 
#define SPD "010D"
#define TPS "0111"
#define OBD_FREQ 100
#define MAX_INPUT 30
#define PID_ATTEMPTS_MAX 5
#define MSWITCH 10
#define BTSWITCH 12

SoftwareSerial bluetooth(RxD, TxD);

int btint = -1;
int ledblink = 0;
bool connection = false;
String ELManswer = "";
unsigned long now = 0;
unsigned long nowLED = 0;
bool PIDcorrect = false;
int currentPID = 0;
byte pid_read_switch = 0;
int PIDattempts = 0; 

void readData() {
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;
  byte inByte;
  if (bluetooth.available() > 0) {
    inByte = bluetooth.read();
    switch (inByte) {
      case '\n':   // end of text
        for (int i = 0; i < input_pos; i++) {
          ELManswer = ELManswer + input_line[i];
        }
        input_line [input_pos] = 0;  
        input_pos = 0;
        while (bluetooth.available() > 0) {
          bluetooth.read();
        }
        break;
      case '\r':   
        for (int i = 0; i < input_pos; i++) {
          ELManswer = ELManswer + input_line[i];
        }
        input_line [input_pos] = 0; 
        input_pos = 0;
        while (bluetooth.available() > 0) {
          bluetooth.read();
        }
        break;
      default:
        if (input_pos < (MAX_INPUT - 1))
          input_line [input_pos++] = inByte;
        break;
    }
  }
}

void ELMinit() { 
  ELManswer="";
  bluetooth.println("ATZ");
  now = millis();
  Serial.println("ATZ sent...");
  while (ELManswer.substring(1, 4) != "ELM" && ELManswer.substring(0, 3) != "ELM" && digitalRead(MSWITCH) == HIGH && millis() - now <= 3000) { 
    readData();
    LED_blink();
  }
  Serial.println(ELManswer);
  if (ELManswer.substring(1, 4) == "ELM" || ELManswer.substring(0, 3) == "ELM") {
    Serial.println("ELM connected, sending ATSP0 and 0100...");
    bluetooth.println("ATSP3");
    now = millis();
    while (millis() - now <= 1000 && digitalRead(MSWITCH) == HIGH) {
      readData();
      LED_blink();
    }
    ELManswer = "";
    bluetooth.println("0100");
    now = millis();
    while (millis() - now <= 5000 && digitalRead(MSWITCH) == HIGH) {
      readData();
      LED_blink();
    }
    connection = true;
    digitalWrite(LED_PIN, HIGH);
    Serial.println("Connected and initialized");
    pid_read_switch = 0;
  }
  else {
    Serial.println("no connection");
  }
  ELManswer = "";
}

void getPID(String PID) { 
  if (millis() - now >= OBD_FREQ) { 
    bluetooth.println(PID);
    now = millis();

    while (ELManswer == "" && digitalRead(MSWITCH) == HIGH) { 
      readData();
    }

    String checkPID = ELManswer.substring(9, 15); 
    checkPID.replace(" ", "");
    if (checkPID == ("41" + PID.substring(2, 4))) {
      PIDcorrect = true;
      PIDattempts = 0;
    }
    else {
      PIDcorrect = false;
      PIDattempts++;
      Serial.println(PIDattempts+ELManswer);
      if (PIDattempts > PID_ATTEMPTS_MAX) {
        connection = false;
        PIDattempts = 0;
      }
    }

    if ((PID == RPM || PID == MAF) && PIDcorrect == true) {
      String pidString = ELManswer.substring(15, 21);
      pidString.replace(" ", "");
      currentPID = strtol(pidString.c_str(), NULL, 16);
      if (PID == RPM) {
        currentPID = currentPID / 4;
      }
      else { //MAF
        currentPID = currentPID / 100;
      }
    }
    else if ((PID == CLT || PID == IAT) && PIDcorrect == true) {
      String pidString = ELManswer.substring(15, 18);
      pidString.replace(" ", "");
      currentPID = strtol(pidString.c_str(), NULL, 16) - 40;
    }
    else if (PID == TPS && PIDcorrect == true) {
      String pidString = ELManswer.substring(15, 18);
      pidString.replace(" ", "");
      currentPID = strtol(pidString.c_str(), NULL, 16) * 100 / 255;
    }
    else if (PID == SPD && PIDcorrect == true) {
      String pidString = ELManswer.substring(15, 18);
      pidString.replace(" ", "");
      currentPID = strtol(pidString.c_str(), NULL, 16);
    }
//    Serial.println(PID + ":" + ELManswer);
    ELManswer = "";
  }
  else {
    PIDcorrect = false;
  }
}

void LED_blink() {
  if (millis() - nowLED >= LED_BLINK_INTERVAL) {
    if (digitalRead(LED_PIN) == HIGH) {
      digitalWrite(LED_PIN, LOW);
      nowLED = millis();
    }
    else {
      digitalWrite(LED_PIN, HIGH);
      nowLED = millis();
    }
  }
}

void setup() {
  bluetooth.begin(9600);
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  pinMode(MSWITCH, INPUT_PULLUP);
  pinMode(BTSWITCH, OUTPUT);
  pinMode(RxD, INPUT);
  pinMode(TxD, OUTPUT);
}

void loop() {

  if (digitalRead(MSWITCH) == LOW) {
    delay(250);
    btint = btint * (-1);
  }

  if (btint == 1) {
    //    digitalWrite(LED_PIN, HIGH);
    digitalWrite(BTSWITCH, HIGH);
    if (connection == false) ELMinit();
  }
  else
  {
    digitalWrite(LED_PIN, LOW);
    digitalWrite(BTSWITCH, LOW);
    connection = false;
  }

  if (connection == true) {
    switch (pid_read_switch) {
      case 4: //RPM
        getPID(RPM);
        if (PIDcorrect == true) {
          Serial.print("RPM:");
          Serial.println(currentPID, DEC);
          pid_read_switch++;
        }
        break;
      case 3: //MAF
        getPID(MAF);
        if (PIDcorrect == true) {
          Serial.print("MAF:");
          Serial.println(currentPID, DEC);
          pid_read_switch++;
        }
        break;
      case 2: //TPS
        getPID(TPS);
        if (PIDcorrect == true) {
          Serial.print("TPS:");
          Serial.println(currentPID, DEC);
          pid_read_switch++;
        }
        break;
      case 1: //CLT
        getPID(CLT);
        if (PIDcorrect == true) {
          Serial.print("CLT:");
          Serial.println(currentPID, DEC);
          pid_read_switch++;
        }
        break;
      case 0: //IAT
        getPID(IAT);
        if (PIDcorrect == true) {
          Serial.print("IAT:");
          Serial.println(currentPID, DEC);
          pid_read_switch++;
        }
        break;
      case 5: //SPD
        getPID(SPD);
        if (PIDcorrect == true) {
          Serial.print("SPD:");
          Serial.println(currentPID, DEC);
          pid_read_switch = 0;
        }
        break;
    } 
  }
}

For sure my code contains a lot of beginner mistakes, but I’m doing my best.

As you can see on the image attached, everything works fine for a while and then crashes. “ATZ7 v2.1” (at the bottom) resonse is supposed to be “ELM327 v2.1” (on top). When I dosconnect NANO, it still replies with “ATZ…”. I have to leave it disconnected for a while to work fine again (for a few seconds).

EDIT: forgot to mention, that it is not ELM327 fault itself. It works and responses fine (when I send commands by my phone).

I have no idea why it happens. Really appreciate your help.
Thank you in advance.

Start by getting rid of your 'String' variables and learn how to use C/C++ strings (nul terminated char arrays) since the String class will eventually corrupt your limited memory and lead to odd results.

blh64:
Start by getting rid of your ‘String’ variables and learn how to use C/C++ strings (nul terminated char arrays) since the String class will eventually corrupt your limited memory and lead to odd results.

Thank you for the hint. I modified my code not to use string anymore. I use char arrays instead. Put all pinted strings into F(), but result is still the same - it crashes after a few seconds.
How is this possible, that a code is working fine, crashes after a few seconds, and after that crashes just from the beginning even when I reset Nano or disconnect and connect voltage supply? When I leave it alone for a few minutes it works fine again (for a few seconds), crashes and so on…
Things that I did additionaly:
-replaced Nano - didn’t change anything;
-put a function that returns a free SRAM (returned 1620 everytime I called it).

I will appreciate any suggestions as I am getting frustrated.
Current code below:

#include <SoftwareSerial.h>
#define RxD 3
#define TxD 4
#define LED_BLINK_INTERVAL 500 //co ile ma migać dioda podczas inicjalizacji
#define LED_PIN 5
#define RPM "010C" //2 bajty
#define IAT "010F"
#define CLT "0105"
#define MAF "0110" //2 bajty
#define SPD "010D"
#define TPS "0111"
#define OBD_FREQ 100 //co ile odpytujemy ELM
#define MAX_INPUT 20 //max liczba bajtów do odczytu bt
#define PID_ATTEMPTS_MAX 5 //ile razy próbujesz pozyskać pid zanim zresetujesz połączenie
#define MSWITCH 10
#define BTSWITCH 12

SoftwareSerial bluetooth(RxD, TxD);

int btint = -1;
int ledblink = 0;
bool connection = false;
unsigned long now = 0;
unsigned long nowLED = 0;
bool PIDcorrect = false;
float currentPID = 0;
byte pid_read_switch = 0;
int PIDattempts = 0; //licznik błędnie wygenerowanych pidów
bool dataReady = false;
char input_line [MAX_INPUT];
byte dataPosition = 0;

unsigned int hexToInt(char a, char b, char c, char d) {
  char calcArray[] = {a, b, c, d};
  unsigned int result = 0;
  float temp = 0;
  for (int i = 0; i < 4; i++) {
    if (calcArray[i] >= '0' && calcArray[i] <= '9') {
      temp = calcArray[i] - '0';
    }
    temp = calcArray[i] - '0';

    if (calcArray[i] >= 'A' && calcArray[i] <= 'F') {
      temp = calcArray[i] - 'A' + 10;
    }
    temp = round(temp * pow(16, 3 - i));
    result = result + temp;
  }
  return result;
}

String printData () {
  String ELManswer;
  for (int i = 0; i < dataPosition; i++) {
    ELManswer = ELManswer + input_line[i];
  }
  return ELManswer;
}

void readData() {
  dataReady = false;
  static unsigned int input_pos = 0;
  byte inByte;
  if (bluetooth.available() > 0) {
    inByte = bluetooth.read();
    switch (inByte) {
      case '\n':   // end of text
        input_line [input_pos] = 0;  // terminating null byte
        dataPosition = input_pos;
        input_pos = 0;
        dataReady = true;
        delay(1);
        while (bluetooth.available() > 0) {
          bluetooth.read();
          // delay(1);
        }
        break;
      case '\r':   // discard carriage return
        input_line [input_pos] = '\0';  // terminating null byte
        dataPosition = input_pos;
        input_pos = 0;
        dataReady = true;
        delay(1);
        while (bluetooth.available() > 0) {
          bluetooth.read();
        }
        break;
      default:
        if (input_pos < (MAX_INPUT - 1))
          input_line [input_pos++] = inByte;
        break;
    }
  }
}

void ELMinit() { //inicjalizacja połączenia z ELM
  bluetooth.println("ATZ");
  now = millis();
  Serial.println(F("ATZ sent..."));
  dataReady = false;
  while (dataReady == false && digitalRead(MSWITCH) == HIGH && millis() - now <= 3000) { //wysyłaj ATZ aż nie dostaniesz odpowiedzi lub co 3sek
    readData();
    LED_blink();
  }
  Serial.println(printData());
  if (input_line[0] == 'E' && input_line[1] == 'L' && input_line[2] == 'M') {
    Serial.println(F("ELM connected, sending ATSP0 and 0100..."));
    bluetooth.println("ATSP3");
    now = millis();
    while (millis() - now <= 1000 && digitalRead(MSWITCH) == HIGH) {
      readData();
      LED_blink();
    }
    bluetooth.println(F("0100"));
    now = millis();
    while (millis() - now <= 5000 && digitalRead(MSWITCH) == HIGH) {
      readData();
      LED_blink();
    }
    connection = true;
    digitalWrite(LED_PIN, HIGH);
    Serial.println(F("Connected and initialized"));
    pid_read_switch = 0;
  }
  else {
    Serial.println(F("no connection"));
  }
}

void getPID(String PID) { //pobierz dany pid
  for (int i=0; i<= MAX_INPUT; i++){
    input_line[i] = '\0';
  }
  if (millis() - now >= OBD_FREQ) { //wysyłaj zapytania do ECU nie częściej niż OBD_FREQ
    bluetooth.println(PID);
    now = millis();
    dataReady = false;
    while (dataReady == false && digitalRead(MSWITCH) == HIGH) { //odczytuj dane aż będzie odpowiedź
      readData();
    }

    if (input_line[9] == '4' && input_line[10] == '1' && input_line[12] == PID[2] &&
        input_line[13] == PID[3]) {
      PIDcorrect = true;
      PIDattempts = 0;
    }
    else {
      PIDcorrect = false;
      PIDattempts++;
      Serial.println(PIDattempts+printData());

      if (PIDattempts > PID_ATTEMPTS_MAX) {
        connection = false;
        PIDattempts = 0;
      }
    }

    if ((PID == RPM || PID == MAF) && PIDcorrect == true) {
      currentPID = hexToInt(input_line[15], input_line[16], input_line[18], input_line[19]);
      if (PID == RPM) {
        currentPID = currentPID / 4;
      }
      else { //MAF
        currentPID = currentPID / 100;
      }
    }
    else if ((PID == CLT || PID == IAT) && PIDcorrect == true) {
      currentPID = hexToInt('0', '0', input_line[15], input_line[16]) - 40;
    }
    else if (PID == TPS && PIDcorrect == true) {
      currentPID = hexToInt('0', '0', input_line[15], input_line[16]) * 0.392156862745098;
    }
    else if (PID == SPD && PIDcorrect == true) {
      currentPID = hexToInt('0', '0', input_line[15], input_line[16]);
    }
  }
  else {
    PIDcorrect = false;
  }
}

void LED_blink() {
  if (millis() - nowLED >= LED_BLINK_INTERVAL) {
    if (digitalRead(LED_PIN) == HIGH) {
      digitalWrite(LED_PIN, LOW);
      nowLED = millis();
    }
    else {
      digitalWrite(LED_PIN, HIGH);
      nowLED = millis();
    }
  }
}

void setup() {
  // put your setup code here, to run once:
  bluetooth.begin(9600);
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  pinMode(MSWITCH, INPUT_PULLUP);
  pinMode(BTSWITCH, OUTPUT);
  pinMode(RxD, INPUT);
  pinMode(TxD, OUTPUT);


}

void loop() {

  if (digitalRead(MSWITCH) == LOW) {
    delay(250);
    btint = btint * (-1);
  }

  if (btint == 1) {
    digitalWrite(BTSWITCH, HIGH);
    if (connection == false) ELMinit();
  }
  else
  {
    digitalWrite(LED_PIN, LOW);
    digitalWrite(BTSWITCH, LOW);
    connection = false;
  }

  if (connection == true) {
    switch (pid_read_switch) {
      case 4: //RPM
        getPID(RPM);
        if (PIDcorrect == true) {
          Serial.print(F("RPM:"));
          Serial.println(currentPID, 0);
          pid_read_switch++;
        }
        break;
      case 3: //MAF
        getPID(MAF);
        if (PIDcorrect == true) {
          Serial.print(F("MAF:"));
          Serial.println(currentPID, 1);
          pid_read_switch++;
        }
        break;
      case 2: //TPS
        getPID(TPS);
        if (PIDcorrect == true) {
          Serial.print(F("TPS:"));
          Serial.println(currentPID, 0);
          pid_read_switch++;
        }
        break;
      case 1: //CLT
        getPID(CLT);
        if (PIDcorrect == true) {
          Serial.print(F("CLT:"));
          Serial.println(currentPID, 0);
          pid_read_switch++;
        }
        break;
      case 0: //IAT
        getPID(IAT);
        if (PIDcorrect == true) {
          Serial.print(F("IAT:"));
          Serial.println(currentPID, 0);
          pid_read_switch++;
        }
        break;
      case 5: //SPD
        getPID(SPD);
        if (PIDcorrect == true) {
          Serial.print(F("SPD:"));
          Serial.println(currentPID, 0);
          pid_read_switch = 0;
        }
        break;
    } //koniec switcha
  }

  if (Serial.available()) {
    bluetooth.write(Serial.read());
  }

}

I still see the use of String in the sketch you posted.

Also, your hexToInt() function is probably not doing what you think

unsigned int hexToInt(char a, char b, char c, char d) {
  char calcArray[] = {a, b, c, d};
  unsigned int result = 0;
  float temp = 0;
  for (int i = 0; i < 4; i++) {
    if (calcArray[i] >= '0' && calcArray[i] <= '9') {
      temp = calcArray[i] - '0';
    }
    temp = calcArray[i] - '0';

    if (calcArray[i] >= 'A' && calcArray[i] <= 'F') {
      temp = calcArray[i] - 'A' + 10;
    }
    temp = round(temp * pow(16, 3 - i));
    result = result + temp;
  }
  return result;
}

if calcArray[0] = ‘5’ then you subtract ‘0’ twice.

I stopped reading after that…

blh64:
I still see the use of String in the sketch you posted.

Yes, I use 5x String to represent the command I send to ELM327 (it’s simply easier to send command that way). I will try to replace them by a char arrays. I also use function (that uses String) to print the answer od ELM, but I use it only during connecting to ELM327 (once), and to pint the answer when there is a problem with reading PID value (it is not being called when everything is fine). Is it really much enough to crash the whole code?

blh64:
Also, your hexToInt() function is probably not doing what you think

unsigned int hexToInt(char a, char b, char c, char d) {

char calcArray = {a, b, c, d};
 unsigned int result = 0;
 float temp = 0;
 for (int i = 0; i < 4; i++) {
   if (calcArray[i] >= ‘0’ && calcArray[i] <= ‘9’) {
     temp = calcArray[i] - ‘0’;
   }
   temp = calcArray[i] - ‘0’;

if (calcArray[i] >= ‘A’ && calcArray[i] <= ‘F’) {
     temp = calcArray[i] - ‘A’ + 10;
   }
   temp = round(temp * pow(16, 3 - i));
   result = result + temp;
 }
 return result;
}



if calcArray[0] = '5' then you subtract '0' twice.

I stopped reading after that...

Thank you for pointing it out, the second

temp = calcArray[i] - '0';

shouldn’t be there. I must have put it there by a mistake.