NMEA Checksum error. Strange behavior (SOLVED)

In the loop () if not enabled the makeGNRMC () the checksum for $ACHDT sentence is correct = 22
But if enabled the makeGNRMC() function the checksum for $ACHDT becomes wrong = 4E
In all cases the checksum for $GNRMC still correct = 1A.
Changing the order of the functions does not change the error....
Could our friends give me a help about what is happening ?


//============================================================
const byte buff_len = 100; // aqui é do CRC calculador
char CRCbuffer[buff_len];
String cr = "";
String msg = "";
String HEADING;
//================================
void setup() {
  Serial.begin(115200); 
}
//================================
void loop() {
//makeGNRMC(); // se descomentar atlatera o CRC da seguinte string ACHDT mesmmo se ela for executada antes
makeACHDT();   
delay (500);
} // fim do loop
//===============================
void outputMsg(String msg) {
  msg.toCharArray(CRCbuffer, sizeof(CRCbuffer)); // put complete string into CRCbuffer
  byte crc = convertToCRC(CRCbuffer); // call function to compute the crc value
  cr= ""; //zera a  substring 
  if (crc < 16) cr="0";   //acrescenta o zero se menor que 16
  cr = cr + String(crc, HEX); // acrescenta o crc 
  cr.toUpperCase(); // converte a string cr para maiusculas
 }
//=============================
byte convertToCRC(char *buff) {
  // NMEA CRC: XOR each byte with previous for all chars between '$' and '*'
  char c = 0 ;
  byte i = 0 ;
  byte start_with = 0;    // index of starting char in msg
  byte end_with = 0;      // index of starting char in msg
  byte crc = 0;
  for (i = 0; i < buff_len; i++) {
    c = buff[i];
    if (c == '$') {start_with = i; }
    if (c == '*') {end_with = i;   }
  }
  if (end_with > start_with) {
    for (i = start_with + 1; i < end_with; i++) {     // XOR every character between '$' and '*'
      crc = crc ^ buff[i] ;  // xor the next char
    }
  }
  else { // else if error, print a msg
    Serial.println("CRC ERROR");
  }
  return crc;
  // based on code by Elimeléc López - July-19th-2013
}
//=====================================
void makeGNRMC() {
msg ="";
cr ="";
msg = "$GNRMC,153815.000,A,2259.11572,S,04312.66106,W,201009,000.0,E,A*";
    // resultado da string acima é 1A

outputMsg(msg);  // envia a msg e chama função que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}
//====================================
//======================================
void makeACHDT(){
msg ="";
cr= "";
HEADING = "75.55";
// o resultado da string com o valor acima é 22  =  $ACHDT,75.55,T*22 
msg = "$ACHDT";
msg.concat (",");
msg.concat (HEADING);
msg.concat (",");
msg.concat ("T");
msg.concat ("*");
outputMsg(msg);  // envia a msg e chama funcção que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}

It does do it right the very first time, but then it repeats the '4E' checksum afterward.

Post Edit --
(Single-letter Variables are easy to type but hard to talk about or to revise later.)
It is because of the variable i
in

byte convertToCRC(char *buff) {
  // NMEA CRC: XOR each byte with previous for all chars between '$' and '*'
  char c = 0 ;
  byte i = 0 ;
  byte start_with = 0;    // index of starting char in msg
  byte end_with = 0;      // index of starting char in msg
  byte crc = 0;
  for (i = 0; i < buff_len; i++) {
    c = buff[i];
    if (c == '$') {start_with = i; }
    if (c == '*') {end_with = i;   }
  }
  if (end_with > start_with) {
    for (i = start_with + 1; i < end_with; i++) {     // XOR every character between '$' and '*'
      crc = crc ^ buff[i] ;  // xor the next char
    }
  }
  else { // else if error, print a msg
    Serial.println("CRC ERROR");
  }
  
  Serial.print("\r\ni = ");
  Serial.println(i);
  
  return crc;
  // based on code by Elimeléc López - July-19th-2013
}

When the final value of i is printed out you can see it's corrupted after the first pass.

The loop runs over the entire CRCbuffer array because it is controlled by buff_len.

So whatever is left behind in the array also impacts the calculation. Instead of buff_len, use strlen(CRCbuffer).

1 Like

@wildbill
That's The Ticket.

const byte buff_len = 100; // aqui é do CRC calculador
char CRCbuffer[buff_len];
String cr = "";
String msg = "";
String HEADING;
String XX = "";
//================================
void setup() {
  Serial.begin(19200); 
}
//================================
void loop() {
//makeGNRMC(); // se descomentar atlatera o CRC da seguinte string ACHDT mesmmo se ela for executada antes

makeACHDT();   
makeGNRMC();
delay (2000);
} // fim do loop
//===============================
void outputMsg(String msg) {
  msg.toCharArray(CRCbuffer, sizeof(CRCbuffer)); // put complete string into CRCbuffer
  byte crc = convertToCRC(CRCbuffer); // call function to compute the crc value
  cr= XX; //zera a  substring 
  if (crc < 16) cr="0";   //acrescenta o zero se menor que 16
  cr = cr + String(crc, HEX); // acrescenta o crc 
  cr.toUpperCase(); // converte a string cr para maiusculas
 }
//=============================
byte convertToCRC(char *buff) {
  // NMEA CRC: XOR each byte with previous for all chars between '$' and '*'
  char c = 0 ;
  byte i = 0 ;
  byte start_with = 0;    // index of starting char in msg
  byte end_with = 0;      // index of starting char in msg
  byte crc = 0;
  for (i = 0; i < strlen(CRCbuffer); i++) {
    c = buff[i];
    if (c == '$') {start_with = i; }
    if (c == '*') {end_with = i;   }
  }
  if (end_with > start_with) {
    for (i = start_with + 1; i < end_with; i++) {     // XOR every character between '$' and '*'
      crc = crc ^ buff[i] ;  // xor the next char
    }
  }
  else { // else if error, print a msg
    Serial.println("CRC ERROR");
  }
  
  //Serial.print("\r\ni = ");
  //Serial.println(i);
  
  return crc;
  // based on code by Elimeléc López - July-19th-2013
}
//=====================================
void makeGNRMC() {
msg = XX;
cr = XX;
msg = "$GNRMC,153815.000,A,2259.11572,S,04312.66106,W,201009,000.0,E,A*";
    // resultado da string acima é 1A

outputMsg(msg);  // envia a msg e chama função que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}
//====================================
//======================================
void makeACHDT(){
msg = XX;
cr = XX;
HEADING = "75.55";
// o resultado da string com o valor acima é 22  =  $ACHDT,75.55,T*22 
msg = "$ACHDT,";
//msg.concat (",");
msg.concat (HEADING);
msg.concat (",T*");
outputMsg(msg);  // envia a msg e chama funcção que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}

Very good !!! Thanks a lot. You have solved the problem. Then the new code becomes :


//============================================================
const byte buff_len = 100; // aqui é do CRC calculador
char CRCbuffer[buff_len];
String cr = "";
String msg = "";
String HEADING;
//================================
void setup() {
  Serial.begin(115200);
 }
//================================
void loop() {


makeGNRMC(); 
makeACHDT(); 
 delay (200);
} // fim do loop
//===============================
void outputMsg(String msg) {
  msg.toCharArray(CRCbuffer, sizeof(CRCbuffer)); // put complete string into CRCbuffer
  byte crc = convertToCRC(CRCbuffer); // call function to compute the crc value
  cr= ""; //zera a  substring 
  if (crc < 16) cr="0";   //acrescenta o zero se menor que 16
  cr = cr + String(crc, HEX); // acrescenta o crc 
  cr.toUpperCase(); // converte a string cr para maiusculas
 }
//=============================
byte convertToCRC(char *buff) {
  // NMEA CRC: XOR each byte with previous for all chars between '$' and '*'
  char c = 0 ;
  byte i = 0 ;
  byte start_with = 0;    // index of starting char in msg
  byte end_with = 0;      // index of starting char in msg
  byte crc = 0;
  for (i = 0; i < strlen(CRCbuffer); i++) {
    c = buff[i];
    if (c == '$') {start_with = i; }
    if (c == '*') {end_with = i;   }
  }
  if (end_with > start_with) {
    for (i = start_with + 1; i < end_with; i++) {     // XOR every character between '$' and '*'
      crc = crc ^ buff[i] ;  // xor the next char
    }
  }
  else { // else if error, print a msg
    Serial.println("CRC ERROR");
  }
  
  return crc;
  // based on code by Elimeléc López - July-19th-2013
  // with  the BUG solved by WILDBILL  changed  buff_len to  strlen(CRCbuffer) 
}
//=====================================
void makeGNRMC() {
msg ="";
cr ="";
msg = "$GNRMC,153815.000,A,2259.11572,S,04312.66106,W,201009,000.0,E,A*";
    // resultado da string acima é 1A

outputMsg(msg);  // envia a msg e chama função que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}
//======================================
void makeACHDT(){
msg ="";
cr= "";
HEADING = "75.55";
// o resultado da string com o valor acima é 22  =  $ACHDT,75.55,T*22 
msg = "$ACHDT";
msg.concat (",");
msg.concat (HEADING);
msg.concat (",");
msg.concat ("T");
msg.concat ("*");
outputMsg(msg);  // envia a msg e chama função que calcula e acrescenta a CRC a string
msg.concat(cr); // acrescenta o CRC calculado
Serial.println (msg); // imprime a string completa
}