Help parsing coordinates from Serial

I need some help. Im trying to parse a string of coordinates that I received from the Serial. What im trying to do is take the data and convert it into waypoints (pair of coordinates) in order to create a route.

I have a python script that takes a large string of coordinates and parses out pairs and sends those pairs to the Arduino and adds a "\n" to the end of them like this:

33.660571,-111.889232,33.660594,-111.889114\n

I have no idea if I'm approaching this correctly, C programming is not my forte.
Here is the code I wrote, the parseCommand() gets the Serial data and parses it. Then it sends the set of coors to the getWaypoint() where it sets the first waypoint.

This prints from the getWaypoint():

33.660572
-111.889228
0.000000
-111.889099

Any advice would be appreciated!

#include <SPI.h>
#include <Wire.h>
#include <FastIO.h>
#include <I2CIO.h>

#include <LiquidCrystal_I2C.h>
//Path planning variables
float start_lat;
float start_lng;
float dest_lat;
float dest_lng;
int nextWaypoint = 1;
int numWaypoints = 0;
bool waypointReached = false;
int waypointsLeft;
double directionToTravel;
bool headingStraightToWaypoint = false;
float getDistance;
float heading;
float compassHeading;


//Coordinates
bool firstRun;
int parseCounter = 0;
String waypoint[24] = {};
bool newWaypoint = false;
bool lastWaypoint = false;
int targetIndex = 0;

String rc;
bool autoPilot = false;

void setup() {

  //open serial port
  Serial.begin(115200);

}
/*************************
  MAIN LOOP
 ************************/
void loop() {
  if(autoPilot == false)
      {
        parseCommand();
        lcd.setCursor(0, 0);
        lcd.print("Heading: ");
        lcd.print(abs(compass()));
        lcd.setCursor(0, 1);
        lcd.print("SteerA: ");
        lcd.print(SteerWheelAngle());
        //Serial.println("Auto pilot");
      }
}

void parseCommand() {
  if (Serial.available() > 0) { 
    rc = Serial.readStringUntil('\n');
      //get route
      const long numChars = 42;
      char* receivedChars;
      newWaypoint = true;
      String dataCoors = rc;
      char string[numChars];
      rc.toCharArray(string, numChars);
      char delimiter[] = ",";
      // initialize first part (string, delimiter)
      char* ptr = strtok(string, delimiter);
      
      while(ptr != NULL) {
        if(String(ptr) == "last\n"){
          lastWaypoint = true;
          //Serial.println("last waypoint recieved");
        }
        else{
          waypoint[parseCounter] = String(ptr);
          Serial.println("wapoint");
          Serial.println(waypoint[parseCounter]);
        }
        ptr = strtok(NULL, delimiter);
        parseCounter++;
        
      }
     parseCounter = 0;
     Serial.flush();
     getWaypoint();
  }
}

void getWaypoint()
{  
  Serial.println("This waypoint is:");
  Serial.println(nextWaypoint);
  Serial.println("Number in array: ");
  Serial.println(parseCounter);


  Serial.println("setting waypoint");
  start_lat = waypoint[0].toFloat();
  start_lng = waypoint[1].toFloat();
  dest_lat = waypoint[2].toFloat();
  dest_lng = waypoint[3].toFloat();
 
    
  Serial.println("setting destination:");
  Serial.println(start_lat, 6);
  Serial.println(start_lng, 6);
  Serial.println(dest_lat, 6);
  Serial.println(dest_lng, 6);
  
  getDistance = CalcDistance(start_lat, start_lng, dest_lat, dest_lng);
  distanceTraveled();
  //get number of remaining waypoints
  waypointsLeft = numWaypoints - nextWaypoint; 
  newWaypoint = false;
  
  if(autoPilot == false )
  {
    startAutopilot();
  }
}

What is the problem with which you want help?

The example #5 of the serial input basics tutorial may be of interest.

1 Like

I need help figuring out why its not setting all the coordinates correctly. Right now it missing one of the four. I guess i wanted to make sure I'm approaching this correctly or does my code have issues.

This is what the Arduino is producing.
33.660572
-111.889228
0.000000
-111.889099

using strtok() and atof() it reads the string OK, e.g.

// strtok example - parse tokens into float values

void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
    char str[] ="33.660571,-111.889232,33.660594,-111.889114";    // 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);              // conver to  float
      Serial.print(" float ");
      Serial.println(x);            // print float
      pch = strtok (NULL, ",");     // get next token
     }  
}

void loop() {}

serial monitor displays

Splitting string 33.660571,-111.889232,33.660594,-111.889114 into tokens:
string found 33.660571
 float 33.66
string found -111.889232
 float -111.89
string found 33.660594
 float 33.66
string found -111.889114
 float -111.89

@kitt3000 Your code is not complete, variable declarations are missing. Please check it.

@MaximoEsfuerzo yeah sorry I updated my post to show all the variables .

@horace Thank you for the example. I will give that a re try and let you know!

Note that Serial.println(x,6); will print a float with 6 decimal places.

Note also that Arduino floats are only good for about 6-7 significant figures, so that inputs like -111.889232 are only stored to 3-4 decimal places, like -111.889228

@horace thank you for the help! Your suggestion seems be working, but is there a way I can get the entire coordinate value? Right now it is just giving me two spaces after the decimal. How could I do this using double? I need the coordinates to be accurate.

right now the console show this:
setting destination:
33.66
-111.89
33.66
-111.89

Floats and doubles are the same 6-7 significant figure type on some Arduinos:

You won't get the full precision you posted unless you do some tricks.

changed to using a double (atof() returns a double) and printing to six digits after decimal point

// strtok example - parse tokens into float values

void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
    Serial.print("size of double ");
    Serial.println(sizeof(double));
    char str[] ="33.660571,-111.889232,33.660594,-111.889114";    // 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
      double x=atof(pch);              // conver to double
      Serial.print(" double ");
      Serial.println(x,6);            // print 
      pch = strtok (NULL, ",");     // get next token
     }  
}

void loop() {}

running on a Node MCU ESP8266 where doubles are 8 bytes (16 to 17 significant digits)

size of double 8
Splitting string 33.660571,-111.889232,33.660594,-111.889114 into tokens:
string found 33.660571
 double 33.660571
string found -111.889232
 double -111.889232
string found 33.660594
 double 33.660594
string found -111.889114
 double -111.889114
1 Like

@horace Thanks for the example, however Im getting the following error:

too few arguments to function 'double strtod(const char, char**)'*

at this line

double x=atof(pch);

Im running an Arduino uno

the program of post 10 compiles and run on my UNO OK - the serial monitor output is

size of double 4
Splitting string 33.660571,-111.889232,33.660594,-111.889114 into tokens:
string found 33.660571
 double 33.660572
string found -111.889232
 double -111.889228
string found 33.660594
 double 33.660591
string found -111.889114
 double -111.889106

note that doubles on a UNO are the same as float being 4 bytes giving 6 to 7 significant digits

no idea where your error message comes from as the program does not use strtod()

ugh, my fault, i didn't save the edit, i re-ran and error is gone.

How would I go about assigning (x, 6) to the wapypoint array? Currently it only assigns 33.66
and -111.89. We can Serial print them in the form I need but can we assign them to the array with the same length?

Please post code which compiles, uses the waypoint array, and includes the corrections you have made to your original posting. Explain with comments what you are trying to assign where.

you can print six digits after the decimal point, e.g.

Serial.println(x,6);            // print 

however you are using a UNO where double and float only use 4 bytes which limits real numbers to 6 or 7 significant digits - any more digits you print will be meaningless
for example your value has 9 digits

111.889232

when stored as a double or float on a UNO the last two digits will be lost
if you require 9 significant digits you will have to move to a microcontroller where doubles use 8 bytes giving 16 or 17 significan digits, e.g. an ESP32

It is possible do 9 significant digits on an uno with `long int' representing microdegrees, but the parsing, handling and printing is more complicated.

The OP could also manage the integer component and the fractional component of each values separately, given that it's initially being parsed from a string anyway. But ultimately, that would be a very clunky and error-likely approach.

@horace oh no, definitely not what I wanted to hear. So if I wanted to stay in the Arduino world and not have to re write my code for a different microcontroller, what are my options? A MEGA?

the Mega is the same family of microcontrollers as the UNO and floats and doubles are 4 bytes (6/7 significant digits), e.g. run of program from post 10 on a Mega

size of double 4
Splitting string 33.660571,-111.889232,33.660594,-111.889114 into tokens:
string found 33.660571
 double 33.660572
string found -111.889232
 double -111.889228
string found 33.660594
 double 33.660591
string found -111.889114
 double -111.889106

I would suggest moving to an ESP32 far more powerful than UNO and has plenty of IO facilities
the UNO uses 5V logic the ESP32 uses 3.3V logic so you need to check your sensors, LCD, etc will work with 3.3V logic

An alternative, though I'm not sure of it's release status, might be

5V compatible, 32 bit processor, sounds like an option. Details are skimpy on compatibility at this time, it seems.