Serial read and process lines that end with \r\n

Hi,
I'm quite new to Arduino, so my apologies if i ask a stupid question.
I have a device (digital smart meter) that outputs serial data every 15 seconds. (a telegram that contains multiple lines)
Every line is terminated by a \r\n
Example of the data:
b'1-0:1.8.1(000858.642kWh)\r\n'
b'1-0:1.8.2(001264.926
kWh)\r\n'
b'1-0:2.8.1(000421.788kWh)\r\n'
b'1-0:2.8.2(000162.755
kWh)\r\n

I'm using Serial.readStringUntil('\n') and then have code process the string to get the code, value and metric.
But for some reason it is not reading he whole telegram, only a few lines are processed. I assume this is because my code to process the data is to slow to read the following lines.
How can i handle in the best way?
What i want to get out of it is for example:

  • If I find 1.7.0 then the i create a description saying "Real time power consumption" and a valuestring of "0.461" and a metric string "kW"
msg1 = Serial2.readStringUntil('\r');
      
      Serial.print("msg1="); Serial.println(msg1);
      codeString=msg1.substring(5,msg1.indexOf('(')); 
      Serial.print("codeString="); Serial.println(codeString);
      if (ValidCode(codeString)){ //the function ValidCode checks if the code, for example 2.8.0 is something I want to process
          description = GetDescription(codeString); "returns a description for example "Real time energy consumption
          Serial.print("description="); Serial.println(description);
          //get the value
          valueSensor = msg1.substring(msg1.indexOf('(')+1,msg1.indexOf('*')); //extracts the value out of the message, for example 846.57
          Serial.print("Value="); Serial.println(valueSensor);
          //get the metric
          valueMetric = msg1.substring(msg1.indexOf('*')+1,msg1.indexOf(')'));  //returns the metric, for example kW
          Serial.print("Metric="); Serial.println(valueMetric);                         
          packageMessage = description + " " + valueSensor + " " + valueMetric;                
          send_mqtt_message(packageMessage.c_str()); //sends MQTT message
        }

Is there really a b' at the beginning of each line ?
Is it a P1 port telegram ?

There are a number of sketches for that, but not always good sketches.
Often the function sscanf() is used to get the numbers out of the telegram.

The .readStringUntil() has a timeout and the behavior of that function is not well documented. Therefor I prefer to use plain 'C' code to read data from the serial port.

Please always show the full sketch.
There is even a website for it: https://snippets-r-us.com/.

Serial2.readStringUntil(’\r’); can block for upto 1sec waiting for input

Try the non-blocking code below instead (reading from Serial for testing)
Here is some sample output

Sample data lines (terminated by '\r' '\n', Arduino monitor both NewLine and carrage Return ending)
b'1-0:1.8.1(000858.642*kWh)
b'1-0:1.8.2(001264.926*kWh)
 got a line of input 'b'1-0:1.8.1(000858.642*kWh)'
codeString=1.8.1
Value=000858.642
Metric=kWh
Real time energy consumption 000858.642 kWh

See my tutorial on Taming Arduino Strings Use String& to pass Strings and use local Strings in processing methods to recover memory.

Your call to send_mqtt_message may also be blocking the loop and causing you to miss input data.
See my tutorial Arduino Serial I/O for the Real World for how to print debug output without blocking the loop.
It has code for increasing the input TX buffer so you can capture all the input data in one go and then process it a line at a time.
Arduino Serial I/O for the Real World also has a loopTimer you can use to see how long your send_mqtt_message is taking.

/**
   https://forum.arduino.cc/index.php?topic=732163.0
  b'1-0:1.8.1(000858.642*kWh)\r\n
  b'1-0:1.8.2(001264.926*kWh)\r\n
  b'1-0:2.8.1(000421.788*kWh)\r\n
  b'1-0:2.8.2(000162.755*kWh)\r\n

  not sure about the ' after the \n in your example data?
*/
String msg1;
void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("PowerReadings.ino"));
  Serial.println(F("Sample data lines (terminated by '\\r' '\\n', Arduino monitor both NewLine and carrage Return ending)"));
  Serial.println(F("b'1-0:1.8.1(000858.642*kWh)"));
  Serial.println(F("b'1-0:1.8.2(001264.926*kWh)"));
  msg1.reserve(40); // expected line size
}

// read Serial until until_c char found or limit char read, returns true when found/limited else false
// non-blocking, until_c is returned as last char in String, updates input String with chars read
bool readStringUntil(String& input, char until_c, size_t char_limit) {
  while (Serial.available()) {
    char c = Serial.read();
    input += c;
    if (c == until_c) {
      return true;
    }
    if (input.length() >= char_limit) {
      return true;
    }
  }
  return false;
}

bool ValidCode(String& codeString) {
  return (codeString.length() > 0); // for now
}

bool processInput(String& input) {
  input.trim();
  if (input.length() == 0) {
    return false;
  }
  int startIdx = input.indexOf(':');
  int endIdx = input.indexOf('(');
  if (startIdx < 0) {
    return false;
  }
  startIdx++; // step over :
  String codeString = input.substring(startIdx, endIdx);
  Serial.print("codeString="); Serial.println(codeString);
  if (ValidCode(codeString)) { //the function ValidCode checks if the code, for example 2.8.0 is something I want to process
    //String description = GetDescription(codeString); "returns a description for example "Real time energy consumption
    //Serial.print("description="); Serial.println(description);
    String description = "Real time energy consumption";
    //get the value
    if (endIdx < 0) {
      return false; // invalid data
    }
    startIdx = endIdx + 1;
    endIdx = msg1.indexOf('*');
    String valueSensor = msg1.substring(startIdx, endIdx); //extracts the value out of the message, for example 846.57
    Serial.print("Value="); Serial.println(valueSensor);
    //get the metric
    if (endIdx < 0) {
      return false; // invalid data
    }
    startIdx = endIdx + 1;
    endIdx = msg1.indexOf(')');
    String valueMetric = msg1.substring(startIdx, endIdx); //returns the metric, for example kW
    Serial.print("Metric="); Serial.println(valueMetric);
    // dont use + as it uses extra memory see
    // Taming Arduino Strings https://www.forward.com.au/pfod/ArduinoProgramming/ArduinoStrings/index.html
    // packageMessage = description + " " + valueSensor + " " + valueMetric;
    String packageMessage =  description;
    packageMessage += " ";
    packageMessage += valueSensor;
    packageMessage += " ";
    packageMessage += valueMetric;
    Serial.println(packageMessage.c_str());
    //send_mqtt_message(packageMessage.c_str()); //sends MQTT message
  }
  return true;
}

void loop() {
  if (readStringUntil(msg1, '\n', 40)) { // read until find newline or have read 40 chars
    if (msg1.lastIndexOf('\n') >= 0) {   // input terminated by '\n'
      msg1.trim();
      Serial.print(F(" got a line of input '")); Serial.print(msg1); Serial.println("'");
      if (!processInput(msg1)) {
        Serial.print(F(" Corrupted input '")); Serial.print(msg1); Serial.println("'");
      }
    } else {
      Serial.print(F(" Corrupted input '")); Serial.print(msg1); Serial.println("'");
    }
    msg1 = ""; // clear after processing for next line
  }
}

That b in front might come from python. When I played a little with python, I've seen it; but can't remember what the meaning was or how I got rid of it.

@OP, if you used a python script to read it, I suggest to start by using a a normal terminal program to show you the data. If the device sends that_b_, you will have to strip it of.

With the code in #2 it does not matter if the b is there or not, it is ignored.

Thank! I'll try the above. Yes the 'b' comes from a serial read with python, it means it's a byte string.

So it actually is not in the data. I leave the conclusion to you :wink:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.