How to parse serial data string to Floating values

hiii, I am getting serial data on my node mcu from the DC energy meter in the below form.
C1.8D1V0.0DWh0.0DAh0.0D
where C is current
V is voltage
wh is watthour
ah amperehour

I want to separate current, voltage, watt-hour and ampere-hour into Float values. So that i can link all these values to Blynk.

/*
   Serial Receive Example

   no start tag
   [Tag Value ValueEndtag]
   separated values by ValueEndeTag D
   no dedicated end tag

   C1.8D1V0.0DWh0.0DAh0.0D
   where C is current
   V is voltage
   wh is watthour
   ah amperehour

   this variant parses each value during the incoming packet

   by noiasca
   2022-06-26 https://forum.arduino.cc/t/how-to-parse-serial-data-string-to-integers/1006661
*/
#define BLYNK_TEMPLATE_ID "TMPLC7diGR3d"
#define BLYNK_DEVICE_NAME "SOLAR POWER"
#define BLYNK_AUTH_TOKEN "74xTPoFU8i7s_Ea0NQ6zTnTJHdDgS7Zk"

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SoftwareSerial.h>

char auth[] = "74xTPoFU8i7s_Ea0NQ6zTnTJHdDgS7Zk";
char ssid[] = "Aamir";
char pass[] = "12345678";
SimpleTimer timer;

void myTimerEvent()
{
  Blynk.virtualWrite(V1, millis() / 1000);
}

constexpr int dataSize = 16;   // size of data array

class Receiver {
  protected:
    static const byte numChars = 36;   // receiver buffer size ( we process just <+integer+blank so a short buffer is ok
    char receivedChars[numChars];      // an array to store the received data
    byte ndx = 0;                      // length of received message
    //boolean newData = false;           // flag to indicate when new data is complete
    Stream &stream;                    // a reference to the serial interface
    void (*cbOnNewData)();             // gets called after we received a full message

    void delBuffer()
    {
      memcpy(receivedChars, "", sizeof(receivedChars));
      ndx = 0;
    }

    void debugDataHex()
    {
      for (size_t i = 0; i < ndx; i++)
      {
        if (receivedChars[i] < 0x10) Serial.print('0');
        Serial.print(receivedChars[i], HEX);
        Serial.print(' ');
      }
      Serial.println();
    }

    void parseData()                   // parse data and store to internal variables
    {
      receivedChars[ndx] = '\0';
      switch (receivedChars[0])
      {
        case 'C':
          current = atof(receivedChars + 1);
          break;
        case 'V':
          voltage = atof(receivedChars + 1);
          break;
        case 'W':
          watthour = atof(receivedChars + 2);
          break;
        case 'A':
          amperehour = atof(receivedChars + 2);
          break;
      }
      delBuffer();
    }

  public:
    // payload data
    float current = 0;
    float voltage = 0;
    float watthour = 0;
    float amperehour = 0;

    Receiver (Stream &stream) : stream(stream)
    {
      delBuffer();
    }

    void setOnNewData(void (*cbOnNewData)())     // set a callback function. Gets called when new data was received
    {
      (*this).cbOnNewData = cbOnNewData;
    }

    void update()                                     // run this member function in loop()
    {
      constexpr byte endMarker = 'D';                 // end flag
      if (stream.available() > 0) {
        char rc = stream.read();
        if (rc == 'C' || rc == 'V' || rc == 'W' || rc == 'A')                     // new message
        {
          delBuffer();
          receivedChars[ndx] = rc;
          ndx++;
        }
        else if (rc == endMarker)  // end of value
        {
          receivedChars[ndx] = rc;
          ndx++;
          parseData();  // parse data after each delimiter
          if (rc == endMarker && cbOnNewData) cbOnNewData();  // optional call callback when we have received a full message
        }
        else if (ndx < numChars)                     // append incoming character to receivedChars
        {
          receivedChars[ndx] = rc;
          ndx++;
        }

        // sanity check
        if (ndx >= numChars) delBuffer();
      }
    }
};

// create the sensor object and hand over the Serial interface to use:
HardwareSerial &mySerial = Serial;          // just a refernce to Serial called "mySerial"
Receiver device(mySerial);                  // use Hardware Serial (for example for testing with the PC)

//#include <SoftwareSerial.h>               // on an Uno you can use SoftSerial with SLOW speeds
//SoftwareSerial mySerial(3, 1);            // RX, TX
//Receiver device(mySerial);

// On a Mega you can simply use
// a Reference to an existing HW Serial:
//HardwareSerial &mySerial = Serial1;
//Receiver device(mySerial);

void output()                           // simple output of data
{
  Serial.println(device.current);
  Serial.println(device.voltage);
  Serial.println(device.watthour);
  Serial.println(device.amperehour);
  Blynk.virtualWrite(V0, device.current);
  Blynk.virtualWrite(V1, device.voltage);
  Blynk.virtualWrite(V2, device.watthour);
  Blynk.virtualWrite(V3, device.amperehour);
}

void timerOutput()                     // a timer to output data to serial
{
  static uint32_t previousMillis = 0;  // time management
  const uint16_t interval = 5000;      // interval to display data
  if (millis() - previousMillis >= interval)
  {
    previousMillis = millis();
    output();
  }
}

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  Blynk.begin(auth, ssid, pass);
  timer.setInterval(2000L, myfunction1);
  //device.setOnNewData(output);  // register a callback function which gets called when a new telegram was received
}
void myfunction1()
{
  
}
void loop() {
  device.update();                // you must call the .update() method in your loop()
  timerOutput();
      Blynk.run();
    timer.run();
}

I am able to parse data when and only if i type on the serial monitor and click on the send, then i get the floating values, as you can see i am getting serial data from the DC energy meter but it is not parsing.

Your topic was MOVED to its current forum category as it is more suitable than the original as it has nothing to do with Covid

Please be more careful in future when starting new topics

The first thing that I notice is that the data appears to consist of floats rather than integers

Take a look at Serial input basics - updated
to see how to receive and parse Serial data

So the above is a character array? and each value is delimited by the character "D" ?

What Is the "1" after the first "D" ?

I bet it's not. More likely it is what shows up on the Serial monitor

please provide a link to that meter and a link to the datasheet with a description of that protocol.
Otherwise it makes no sense to invest time if we do not know where we can get this meter from ...

let me clear
C1.8D means 1.8 is value of dc current
1V0.0D means0.0 is the value of the dc voltage
Wh0.0D means 0.0 is the value of the dc watt-hour
Ah0.0D means 0.0 is the value of the ampere-hour

this is what i am getting on my serial monitor.

1
2

that 1 is for overcurrent detection.

do you see if the transmitted data will end with some kind of carriage return and/or linefeed?

The first step in parsing the data to variables is to get it into an array of chars. Is the data terminated by a Carriage Return, Linefeed or both ?

I dont find any carriage return. all i know is at the end of every value there is "D".

Please post the sketch that you are using to read the data

this will read such a message from Serial and print the values every 3 seconds to serial

/*
   Serial Receive Example

   no start tag
   [Tag Value ValueEndtag]
   separated values by ValueEndeTag D
   no dedicated end tag

   C1.8D1V0.0DWh0.0DAh0.0D
   where C is current
   V is voltage
   wh is watthour
   ah amperehour

   this variant parses each value during the incoming packet

   by noiasca
   2022-06-26 https://forum.arduino.cc/t/how-to-parse-serial-data-string-to-integers/1006661
*/

constexpr int dataSize = 16;   // size of data array

class Receiver {
  protected:
    static const byte numChars = 36;   // receiver buffer size ( we process just <+integer+blank so a short buffer is ok
    char receivedChars[numChars];      // an array to store the received data
    byte ndx = 0;                      // length of received message
    //boolean newData = false;           // flag to indicate when new data is complete
    Stream &stream;                    // a reference to the serial interface
    void (*cbOnNewData)();             // gets called after we received a full message

    void delBuffer()
    {
      memcpy(receivedChars, "", sizeof(receivedChars));
      ndx = 0;
    }

    void debugDataHex()
    {
      for (size_t i = 0; i < ndx; i++)
      {
        if (receivedChars[i] < 0x10) Serial.print('0');
        Serial.print(receivedChars[i], HEX);
        Serial.print(' ');
      }
      Serial.println();
    }

    void parseData()                   // parse data and store to internal variables
    {
      receivedChars[ndx] = '\0';
      switch (receivedChars[0])
      {
        case 'C':
          current = atof(receivedChars + 1);
          break;
        case 'V':
          voltage = atof(receivedChars + 1);
          break;
        case 'W':
          watthour = atof(receivedChars + 2);
          break;
        case 'A':
          amperehour = atof(receivedChars + 2);
          break;
      }
      delBuffer();
    }

  public:
    // payload data
    float current = 0;
    float voltage = 0;
    float watthour = 0;
    float amperehour = 0;

    Receiver (Stream &stream) : stream(stream)
    {
      delBuffer();
    }

    void setOnNewData(void (*cbOnNewData)())     // set a callback function. Gets called when new data was received
    {
      (*this).cbOnNewData = cbOnNewData;
    }

    void update()                                     // run this member function in loop()
    {
      constexpr byte endMarker = 'D';                 // end flag
      if (stream.available() > 0) {
        char rc = stream.read();
        if (rc == 'C' || rc == 'V' || rc == 'W' || rc == 'A')                     // new message
        {
          delBuffer();
          receivedChars[ndx] = rc;
          ndx++;
        }
        else if (rc == endMarker)  // end of value
        {
          receivedChars[ndx] = rc;
          ndx++;
          parseData();  // parse data after each delimiter
          if (rc == endMarker && cbOnNewData) cbOnNewData();  // optional call callback when we have received a full message
        }
        else if (ndx < numChars)                     // append incoming character to receivedChars
        {
          receivedChars[ndx] = rc;
          ndx++;
        }

        // sanity check
        if (ndx >= numChars) delBuffer();
      }
    }
};

// create the sensor object and hand over the Serial interface to use:
HardwareSerial &mySerial = Serial;          // just a refernce to Serial called "mySerial"
Receiver device(mySerial);                  // use Hardware Serial (for example for testing with the PC)

//#include <SoftwareSerial.h>               // on an Uno you can use SoftSerial with SLOW speeds
//SoftwareSerial mySerial(2, 3);            // RX, TX
//Receiver device(mySerial);

// On a Mega you can simply use
// a Reference to an existing HW Serial:
//HardwareSerial &mySerial = Serial1;
//Receiver device(mySerial);

void output()                           // simple output of data
{
  Serial.println(device.current);
  Serial.println(device.voltage);
  Serial.println(device.watthour);
  Serial.println(device.amperehour);
}

void timerOutput()                     // a timer to output data to serial
{
  static uint32_t previousMillis = 0;  // time management
  const uint16_t interval = 3000;      // interval to display data
  if (millis() - previousMillis >= interval)
  {
    previousMillis = millis();
    output();
  }
}

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  //device.setOnNewData(output);  // register a callback function which gets called when a new telegram was received
}

void loop() {
  device.update();                // you must call the .update() method in your loop()
  timerOutput();
}

if you change the lines of the "myserial" to a softserial or another HW serial on a Mega, this might work

Assuming you have the input in a character array...

char input []    = "C1.8D1V-0.045DWh4.03DAh2.065D";
char prefix[][2]  = {"C", "V", "W", "A"};
float values[4];
char *ptr;
uint8_t idx = 0;


void setup()
{
  Serial.begin(115200);
  Serial.println("Starting");
}


void loop()
{
  char    chFloat[10];
  uint8_t chCount = 0;

  ptr = strstr(input, prefix[idx]);
  if (ptr)
  {
    while (*ptr != 'D')
    {
      ptr++;
      if ((*ptr >= '0' && *ptr <= '9') || *ptr == '-' || *ptr == '.')
      {
        chFloat[chCount++] = *ptr;
      }
    }
    chFloat[chCount] = 0x00;

    values[idx] = atof(chFloat);
  }
  Serial.print(prefix[idx]);
  Serial.print(" = ");
  Serial.println(values[idx],4);

  idx++;

  if (idx > 3)
  {
    Serial.println("Done");
    while (1);
  }
}

Result

22:52:51.732 -> Starting
22:52:51.732 -> C = 1.8000
22:52:51.732 -> V = -0.0450
22:52:51.732 -> W = 4.0300
22:52:51.732 -> A = 2.0650
22:52:51.732 -> Done

Thanks for the help, But how i suppose to parse the data which is coming from the serial port, the data which i can see on the serial monitor. please guide.






I am using your code but data which I am receving from the DC energy meter is not parsing into float value, only values which i manually inserting on serial monitor through send tab. Your code is very good but failed to fetch serial data n converting it into float values. please guide!


circuit diagram

So you don't I'm assuming?

Guess you need to figure out how to read Serial data.

https://www.google.com/search?q=arduino+reading+serial+input