How to use substring properly

Hi, I'm having a project that can send 3 data to from Arduino to ESP32 and send these data to google sheets using substrings.
This is the data that the Arduino send to the ESP32:

1,3,1

And then ESP32 will send these data to Google Sheets and in Google Sheets will look like this:


But there was a twist, I am still struggling with this, here's how I attempted to use substring:

This is the code of ESP32

#include <WiFi.h>
#include <HTTPClient.h>

const char * ssid = "WIFI_NAME";
const char * password = "WIFI_PASSWORD";
String GOOGLE_SCRIPT_ID = "SHEET_ID";

unsigned long Time;
String param = "";

#define RXD2 16
#define TXD2 17



void setup()
{
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  Time = millis();
  WiFi.mode(WIFI_STA);
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  Serial.println(ssid);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("CONNECTED !!!");
}


// ======================================================
void loop()
{
  if ((unsigned long)millis() - Time > 1000)
  {
    print_speed();
    Time = millis();
  }
}


void print_speed()
{
  if (Serial2.available())
  {
    param  =  Serial2.readStringUntil('\n') ;
    param.trim();
    Serial.println(param);
    int comma = param.indexOf(",");
    int comma3 = param.indexOf("///");
    
    if (comma != -1)
    {
      float fast = param.substring(0, comma).toFloat();
      float slow = param.substring(comma+1).toFloat();
      float missed = param.substring(comma+1, comma+3).toFloat();
      write_to_google_sheet_2(fast, slow, missed);
    }
    if (comma3 != -1)
    {
      float force = param.substring(0, comma3).toFloat();
      write_to_google_sheet_1(force);
    }
  }
  else
  {
    Serial.println("No data !!!");
  }

}
/******/

void write_to_google_sheet_1(float force)
{
  HTTPClient http;
  String url = "https://script.google.com/macros/s/" + GOOGLE_SCRIPT_ID + "/exec?forceData=" + String(force);
  Serial.println(url);

  Serial.println("Data sent to Google Sheets");
  //---------------------------------------------------------------------
  //starts posting data to google sheet
  http.begin(url.c_str());
  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
  int httpCode = http.GET();
  Serial.print("HTTP Status Code: ");
  Serial.println(httpCode);
  //---------------------------------------------------------------------
  //getting response from google sheet
  String payload;
  if (httpCode > 0) {
    payload = http.getString();
    Serial.println("Payload: " + payload);
  }
  //---------------------------------------------------------------------
  http.end();
}

void write_to_google_sheet_2(int fast, int slow, int missed)
{
  HTTPClient http;
  String url = "https://script.google.com/macros/s/" + GOOGLE_SCRIPT_ID + "/exec?fastData=" + String(fast) + "&slowData=" + String(slow) + "&missedData=" + String(missed);
  Serial.println(url);

  Serial.println("Data sent to Google Sheets");
  //---------------------------------------------------------------------
  //starts posting data to google sheet
  http.begin(url.c_str());
  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
  int httpCode = http.GET();
  Serial.print("HTTP Status Code: ");
  Serial.println(httpCode);
  //---------------------------------------------------------------------
  //getting response from google sheet
  String payload;
  if (httpCode > 0) {
    payload = http.getString();
    Serial.println("Payload: " + payload);
  }
  //---------------------------------------------------------------------
  http.end();
}

maybe simpler read text into an array of char and to use sscanf() to parse the data, e.g.

void setup() {
  Serial.begin(115200);
}

void loop() {
  if (Serial.available()) {
    char param[100] = { 0 };
    float x = 0.0f, y = 0.0f, z = 0.0f;
    Serial.readBytesUntil('\n', param, 100);
    Serial.printf("read %s\n", param);
    int converted = sscanf(param, "%f,%f,%f", &x, &y, &z);
    Serial.printf("converted %d x %.2f y %.2f z %.2f\n", converted, x, y, z);
  }
}

a run gives

read 1,2,3
converted 3 x 1.00 y 2.00 z 3.00
read 1,2
converted 2 x 1.00 y 2.00 z 0.00
read 3.1415926, 45, 12345
converted 3 x 3.14 y 45.00 z 12345.00

I'm really not entirely sure what it is you're asking, as your text doesn't match up with your sketch. Why, for example, is your sketch looking for "///" when you don't mention that? And why do you have what appears to be an optional fourth field that you also don't mention?

Just going on your text, to split up three numbers separated by commas using substring, here's an example of how it's done. Let me also say that I don't recommend doing it this way! String in the Arduino world will cause you grief sooner or later. I'd suggest using character arrays and maybe even learning the ins and outs of strtok. Having said that, here's an example of how to do it with the String class and substring and indexOf.

void setup() {
   String param = "1,2,3";

   Serial.begin(115200);
   Serial.println(param);
   int comma1 = param.indexOf(',');
   int comma2 = param.indexOf(',', comma1 + 1);
   if( comma1 != -1 ) {
      float fast = param.substring(0, comma1).toFloat();
      float slow = param.substring(comma1 + 1, comma2).toFloat();
      float missed = param.substring(comma2 + 1).toFloat();
      Serial.println(fast);
      Serial.println(slow);
      Serial.println(missed);
   }
}

void loop() {}

Output:

1,2,3
1.00
2.00
3.00

oh this one is for the force data of my project, it works perfectly fine so it's not really worth mentioning

The main problem is this one, let's make your example is 1,2,3. The output must be 1.00, 2.00, 3.00 right? But for some reason my output shows 1.00, 2.00, 1.00. Let's take another example, the input is 4, 0, 1 but with your code for some reason the output is 4.00, 0.00, 4.00. You get my point right? Anyways I think we're getting closer to my expectation!

That turns out not to be the case.

I showed you the exact output from the listed sketch. It works as described and shown.

I think I'm done here.

Hmm this is really weird, I think I might check out on this one

Oh wait, clumsy me! I found out what caused the problem. I forgot to change a dot to a comma, like the input must be 1, 2, 3 not 1, 2. 3!!
Anyways it works flawlessly, thank you so much!!

This code also works too! So I think there are 2 solutions in this topic I guess. Thank you for helping me out!!

there are multiple way of parsing "1,2,3"
@van_der_decken mentioned using strtok, e.g.

// strtok example - parse tokens into float values

void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
    char str[] ="1,2,3";    // text to tokenise
    char * pch;                     // pointer to tokens
    Serial.print("Splitting string ");
    Serial.print(str);
    Serial.println(" into tokens:");
    pch = strtok (str,",");         // get first token
    while (pch != NULL)
      {
      Serial.print("string found ");
      Serial.println(pch);          // print it
      float x=atof(pch);              // convert to  float
      Serial.print(" float ");
      Serial.println(x);            // print 
      pch = strtok (NULL, ",");     // get next token
     }  
}

void loop() {}

gives

Splitting string 1,2,3 into tokens:
string found 1
 float 1.00
string found 2
 float 2.00
string found 3
 float 3.00

worth noting that the strtok() code will work on a UNO, nano, etc which the code of post 2 won't as sscanf() float conversions do not work on such low power microcontrollers

1 Like

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