serial data stream - how to find and extract data

Dear all,

I am reading serial data from my electric meter over an IR interface. After finding the right setup for the hardware, I am at a point now where I get the meter data shown in the serial monitor. I am using SoftwareSerial and simple functions like Serial.read (byte by byte) and write this into the serial monitor.

My goal is to extract the actual power consumption and write it to a thingspeak channel later (will be done using ESP8266).

Situation: I get a long message each time I am sending a request for data to the meter. I need to extract the actual power consumption only and write it to a variable (float).

The message in the serial monitor looks like this (and is aprox. 100 lines in total):

... 0.9.1(204912) 0.9.2(200415) 1.7.0(0.280*kW) 1.8.0(0031862.222*kWh) ...

But I just need the values in the bracket after indicator "1.7.0", so I tried to run this over if-cases, byte by byte. The code looks messy and it is not working, but I hope my intention gets clearer. (This was to test if the serial monitor is now only showing the power readings.)

Question: Is there a better way (I am sure there is), and how might that look?

Thanks in advance!

void loop() {
 float actual =0.000;
  if (ir.available()) 
  {
    int inByte = ir.read();
    if (inByte == '1') //Byte "1"
    {
      inByte = ir.read(); //Byte "."
      if (inByte == '.')
      {
        inByte = ir.read();
        if (inByte == '7') //Byte "7"
        {
          inByte = ir.read();
          if (inByte == '.') //Byte "."
          {
            inByte = ir.read();
            if (inByte == '0') //Byte "0"
            {
              inByte = ir.read(); // read and disregard, this is "("
              inByte = ir.read(); // first digit of power
              Serial.write(inByte);
              //actual = ir.read();
              Serial.write(0);
              inByte = ir.read(); // first decimal
              Serial.write(inByte);
              //actual = actual + ir.read()/10;
              inByte = ir.read(); // second decimal
              Serial.write(inByte);
              //actual = actual + ir.read()/100;
              inByte = ir.read(); // third decimal
              Serial.write(inByte);
              //actual = actual + ir.read()/1000;
            }
          }
        }
      } 
    }
  }
}

Using String and .substring could work. Look at this: https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/substring/

The use of String is restricted to big controllers, an ESP may be usable, but a char array for buffering a single line of text should be sufficient. This buffer can be searched for the delimiters and the numbers can be extracted as text. For number conversion, if required, the lower level atof() functions can be used, or simpler homebrew algorithm like the one already implemented in the comments.

DrDiettrich: The use of String is restricted to big controllers

Can You outline that a little bit? Recently an OP used .substring, indexed it wrong but I found the fault. Don't remember which Arduino he used.

Yes, using a char Array and filtering chars means more code but will certainly work.

Small RAM will be fragmented by dynamic re-allocations, until no more contiguous space exists for a request.

@DrDiettrich Is there any way to declare/allocate a limited, defined string and play around inside that string? I have a background from Business Basic and other string friendly Basics like the old Quick Basic. There strings can be used nicely.

You can preallocate a string buffer, but the best practice is to use a char array.

Using a char Array, like OP wants, calls for quite some more code. The function .substring looks very versatile .

Handling of data in char arrays is a basic task in many C programs. I don't see how substrings will considerably simplify coding of text processing, in contrast to explicit indices or pointers and length.

Why not try

float actual;
ir.setTimeout(5000);

if (ir.find("1.7.0(")) {
  actual = ir.parseFloat();
  Serial.print("found:");
  Serial.println(actual, 3);
  }
else {
  Serial.println("Not found!");
}

Dear all, thank you very much for your valuable thoughts and the support! Great community!

@PaulRB: I really like your idea, it is simple and works very well! Thank you so much, this was exactly what I needed. I think I can take it from here.

One remaining question is: Would this also allow to search for a second string, i.e. "1.8.0" and put the float that follows after 1.8.0 into another variable? (So I can extract multiple floats out of the serial stream?)

le_1982: Would this also allow to search for a second string, i.e. "1.8.0" and put the float that follows after 1.8.0 into another variable? (So I can extract multiple floats out of the serial stream?)

Yes, easy, provided that each & every message always contains all the values you need, and they are always in the same sequence (from your description, it sounds like that's true). Simply repeat the above code to search for and read each value in the order they appear in the message.