Trying to understand serial behavior (parsing from hardware serial)

Arduino board: Mega 2560. Trying to read UART battery status from this monitor:

My objective is to parse the data in the form ("$ SmartUPS V3.10,Vin NG,BATCAP 57,Vout 5198 $") to variables: int batcap = 57, int Vout = 5198.

Reading UART code that works: outputs UART status once every second.

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

unsigned long previousMillisD = 0;     
int intervalD = 1000;

char incomingByte[63] ; //number of characters obtained from Serial3.available();
int serialBatteryIndex = 0;

void serialBatteryRead(){

  while (Serial3.available() > 0) {
    
  incomingByte[serialBatteryIndex] = Serial3.read();

  Serial.print(incomingByte[serialBatteryIndex]);

  serialBatteryIndex++;
  
}
  
  }


//============

void setup() {
  
  Serial.begin(115200);
  Serial3.begin(9600);

}

void loop() {

  unsigned long currentMillis = millis();

if(currentMillis - previousMillisD > intervalD) {
   
  previousMillisD = currentMillis;  

  serialBatteryRead();
    
  }

}

Output:

$ SmartUPS V3.10,Vin NG,BATCAP 57,Vout 5207 $
$ SmartUPS V3.10,Vin NG,BATCAP 57,Vout 5211 $
$ SmartUPS V3.10,Vin NG,BATCAP 57,Vout 5198 $

The output goes on indefinitely.

What I've tried so far: just Serial.print the battery capacity numbers.

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

unsigned long previousMillisD = 0;        
int intervalD = 1000;

char incomingByte[63] ; //number of characters obtained from Serial3.available();
int serialBatteryIndex = 0;

void serialBatteryRead(){

  while (Serial3.available() > 0) {
    
  incomingByte[serialBatteryIndex] = Serial3.read();

  Serial.print(incomingByte[31]);
  Serial.println(incomingByte[32]);

  serialBatteryIndex++;
  
}
  
  }


//============

void setup() {
  
  Serial.begin(115200);
  Serial3.begin(9600);

}

void loop() {

  unsigned long currentMillis = millis();

if(currentMillis - previousMillisD > intervalD) {
   
  previousMillisD = currentMillis;  

  serialBatteryRead();
    
  }

}

Output:


5
57

The output stops after a second.

What am I doing wrong?

So is there a hidden '/n' in there for the line break?

-jim lee

Nope. Eventually your code reaches the end of SRAM then starts writing at the beginning until the top of the stack is reached at which point your application enters "undefined behaviour mode".

You need to perform bounds checking on serialBatteryIndex.

You possible looking for something like this?

http://www.cplusplus.com/reference/cstdlib/strtol/

-jim lee

Once you have your line in char array,
simply use strstr() in order to find first occurence of "keyword" and then atoi() to convert substring to integer

Example:

  char str[] = "$ SmartUPS V3.10,Vin NG,BATCAP 57,Vout 5207 $";
  char * pch;
  int value;

  pch = strstr(str, "BATCAP");
  value = atoi(pch + strlen("BATCAP"));
  Serial.print("Text found at pos: ");
  Serial.println(pch - str + 1);   // pch - str give the start position
  Serial.print("Value: ");
  Serial.prinln(value);
  
  // The next value
  pch = strstr(str,"Vout");
  value = atoi(pch + strlen("Vout"));
  Serial.print("Text found at pos: ");
  Serial.println(pch - str + 1);   // pch - str give the start position
  Serial.print("Value: ");
  Serial.prinln(value);

Regarding serial chars readings, i would use something like this:

1 Like

wow you are right, that was dumb of me! thanks for pointing that out. I actually figured it out once you reminded me of that. THANKS!

This is the solution for everyone else but I think it might not work for the 2 cases of battery level = 100 or a single digit number. For that I think I'd need to do something like read everything after BATCAP and before a ",".

But for everything else (10-99%):

unsigned long previousMillisD = 0;        
int intervalD = 1000;

const byte numChars=45; //counting characters, not number of characters from Serial3.available();

char incomingByte[numChars] ; //size of array
int serialBatteryIndex = 0;
int batCap ;

void serialBatteryRead(){

  while (Serial3.available() > 0) {
    
  incomingByte[serialBatteryIndex] = Serial3.read(); //write Serial3 into array

  if(serialBatteryIndex==numChars){ //this ensures output only once per intervalD
 
  byte index;

  int batcap1 = incomingByte[42]-'0';
  int batcap2 = incomingByte[43]-'0';
  batCap = batcap1*10+batcap2;

  Serial.print(batCap);
  Serial.println();
  
    }

  serialBatteryIndex++;

  if(serialBatteryIndex>numChars){
    
  serialBatteryIndex=0; //reset index
  
    }
  
}
  
}


//============

void setup() {
  
  Serial.begin(115200);
  Serial3.begin(9600);

}

void loop() {

  unsigned long currentMillis = millis();

if(currentMillis - previousMillisD > intervalD) {
   
  previousMillisD = currentMillis;  

  serialBatteryRead();
    
  }

}

wow thanks, I figured it out a different way that has some limitations, but this is a much more elegant solution that seems to account for cases of BATCAP < 10 and BATCAP = 100.

  int batcap, Vout;
  if (Serial.find("BATCAP "))
    batcap = Serial.parseInt();
  if (Serial.find("Vout "))
    Vout = Serial.parseInt();
2 Likes