Converting a string to an int or a float

Hi im having a load of grief converting a string that i have parsed from the serial port into a float number that i can do math with.

The project is an FPV antenna tracker, im hooking a gps to a pro mini, then sending the gps infor over my radio control link's serial passthru port. (its there, why not use it). on the ground another arduino will take the serial data, toss out any errors, do math, and point the antenna in the correct position.

It's also a project to help teach me to code.

I'm now at a stumbling block, i thought there would be an easy way to convert the numbers in my string into a float (lat lon, so needs 6sig fig).

Ive eventually done a work around so i only need a large integer rather than a float.

heres my code, any ideas what im doing wrong (P.S this is just me testing the parsing before adding it to my main function)

// testing ways to parse serial input for the gps tracker

#include <stdlib.h>
#include <string>

int lat, lon, check;
float flat, flon;
int alt;
String latStream, lonStream, altStream, checkStream;

void setup(){
   Serial.begin(9600);
   delay(50);
   Serial.println("Serial Parsing test");
}

//@lat?lon$alt%check#
  String inputStream;

void loop(){
  
  if (Serial.available()){
    //@lat?lon$alt%check#
    //@52126411?1247066$100%53373477# TEST values, input in serial monitor
    char c;
    while(c != '@'){
      Serial.println(c);
      c = Serial.read();
      delay(500);
    }
    
   latStream = Serial.readStringUntil('?'); 
   lonStream = Serial.readStringUntil('

);
  altStream = Serial.readStringUntil('%');
  checkStream = Serial.readStringUntil('#');
 
  lat = latStream.toInt();
  lon = lonStream.toInt();
  alt = altStream.toInt();
  check = checkStream.toInt();
 
 
 
  flat = lat / 1000000;
  flon = lon / 1000000;
 
  Serial.println("strings");
  Serial.println(latStream);
  Serial.println(lonStream);
  Serial.println(altStream);
  Serial.println(checkStream);
  Serial.println();
 
  Serial.println("integers");
  Serial.println(lat);
  Serial.println(lon);
  Serial.println(alt);
  Serial.println(check);
  Serial.println();
 
  Serial.println("floats");
  Serial.println(flat);
  Serial.println(flon);
  Serial.println(alt);
  Serial.println(check);
  Serial.println();
 
 
  }
}

Can we get one thing straight before we go any further ?
You are using Strings not strings. A String is an object created by the String library. A string is a null terminated array of chars.

Given that, what happens when you run your program ?

I agree with Bob: The String class just has too much overhead for what it brings to the party. Make them null-terminated character arrays and then check out dtostrf().

The new forum silently discards replies that are made at the same time another reply is being made. Irritating as all get out.

The stupid forum saves drafts automatically, even though I've turned that option off. Is there a way to retrieve a saved draft? Of course not. Just another stupidity of the new forum.

Anyone, enough complaining. Get rid of the Strings. Use null terminated arrays of chars, as populated by readBytes() or readBytesUntil(), and then use atof() to convert a string to a float, or atoi() to convert a string to an int.

Is there a way to retrieve a saved draft?

Of course there is Paul. All you have to do is go into your Profile, select EDIT (once it reveals itself) next to FORUM SETTING, then Show Drafts and edit the one you want. It's hardly hidden at all. I am really surprised that you didn't know that :slight_smile:

I have to agree that the String class is quite a bit of overhead and wasted memory you don't need. atoi(), atof(), and atol() will be your friends. To convert back, use itoa()

Thanks for the replies, I've adjusted the code to create a char array, then perform atof() and atoi() as appropriate. This has produced two new problems however.

Problem 1: i think im getting overflow of the memory location, as on the serial monitor when i print latStream i get most of what is input to the serial monitor. The same with the others aswell.

Problem 2: im getting the number rounded to two sig fig. As its a latitude an longitude i need all decimal places.

attached is updated code and a copy of the serial monitor contents.

// testing ways to parse serial input for the gps tracker

#include <stdlib.h>


float lat, lon, check;
float flat, flon;
int alt;
//String latStream, lonStream, altStream, checkStream;

void setup(){
   Serial.begin(9600);
   delay(50);
   Serial.println("Serial Parsing test");
}

//@lat?lon$alt%check#

void loop(){
  
  if (Serial.available()){
    //@lat?lon$alt%check#
    //@52.126411?1.247066$100%53.373477# TEST values, input in serial monitor
    char c;
    while(c != '@'){
      Serial.println(c);
      c = Serial.read();
      delay(500);
    }
   // create a null terminated charachter array big enough to hold each variable 
   char latStream[10];
   char lonStream[10];
   char altStream[5];
   char checkStream[10];
   
   //load the array byte by byte until the end charachter
   Serial.readBytesUntil('?', latStream, 10); 
   Serial.readBytesUntil('
@52.126411?1.247066$100%53.373477#


Serial Parsing test
€
strings
52.126411!1.24706653.373477
1.24706653.373477
1000~52.126411!1.24706653.373477
53.373477

floats
52.13
1.25
1000
53.37

, lonStream, 10);
  Serial.readBytesUntil('%', altStream, 5);
  Serial.readBytesUntil('#', checkStream, 10);
 
  // n = atof (buffer);
  //convert the null terminated char array into a float / int variable
 
  lat = atof (latStream);
  lon = atof(lonStream);
  alt = atoi(altStream);
  check = atof(checkStream);
 
 
 
  //flat = lat / 1000000;
  //flon = lon / 1000000;
 
  Serial.println("strings");
  Serial.println(latStream);
  Serial.println(lonStream);
  Serial.println(altStream);
  Serial.println(checkStream);
  Serial.println();
 
  Serial.println("floats");
  Serial.println(lat);
  Serial.println(lon);
  Serial.println(alt);
  Serial.println(check);
  Serial.println();
 
 /* Serial.println("floats");
  Serial.println(flat);
  Serial.println(flon);
  Serial.println(alt);
  Serial.println(check);
  Serial.println();
 */
 
 }
}


§DISCOURSE_HOISTED_CODE_1§

Alternately is there a better way to communicate between two arduino's, using the serial interface, that i ought to be using?

Problem 2: im getting the number rounded to two sig fig. As its a latitude an longitude i need all decimal places.

The problem is in the serial.print statement. The default for a float is 2 decimal places. Use the format serial.print(value,n) to print out n decimal places.

Great, so when I do math with it it'll still use the full 6 d.p. ?

Problem 1: i think im getting overflow of the memory location, as on the serial monitor when i print latStream i get most of what is input to the serial monitor. The same with the others aswell.

There is something wrong with how you are handling the serial input and the length of Serial.readBytesUntil().

This revision of your sketch loads the serial input cleanly.

#include <stdlib.h>


float lat, lon, check;
int alt;

// create character arrays big enough to hold each variable 
char latStream[15];
char lonStream[15];
char altStream[15];
char checkStream[15];

void setup(){
  Serial.begin(9600);
  delay(50);
  Serial.println("Serial Parsing test");
}

void loop(){

  while (Serial.available()){

    //52.126411?1.247066$100%53.373477# TEST values, input in serial monitor

    Serial.readBytesUntil('?', latStream, 10); 
    Serial.readBytesUntil('

Output

strings
52.126411
1.247066
100
53.373477

floats
52.1264
1.2471
100
53.3735

, lonStream, 9);
   Serial.readBytesUntil('%', altStream, 4);
   Serial.readBytesUntil('#', checkStream, 10);

}
 //convert the char array into a float / int variable

lat = atof (latStream);
 lon = atof(lonStream);
 alt = atoi(altStream);
 check = atof(checkStream);

Serial.println("strings");
 Serial.println(latStream);
 Serial.println(lonStream);
 Serial.println(altStream);
 Serial.println(checkStream);
 Serial.println();

Serial.println("floats");
 Serial.println(lat,4);
 Serial.println(lon,4);
 Serial.println(alt);
 Serial.println(check,4);
 Serial.println();

delay(5000);
}


Output

strings
52.126411
1.247066
100
53.373477

floats
52.1264
1.2471
100
53.3735

It looks to me as though the problem is that although Serial.readBytesUntil() puts the bytes that it reads into a buffer the buffer is not zero terminated. So when trying to print the buffer as if it is a C style string (a zero terminated array of chars) the printing carries on until a zero is encountered.

Increasing the buffer array size to 15 just happens to work because when declared the buffer will be full of zeroes. If that is the case then the correct way of dealing with this is to terminate the array using the value returned by the call to SerialreadBytesUntil() function to determine how many bytes have actually been read which enables the termination to be inserted in the right place.

I was under the impression that read bytes until would stop either when my special character is reached, it reads the number of bytes for the buffer, or it reaches the /0.

With the longitude I need to have the ability to read 1 extra charachter as it may become 10.456789 for example.

Helibob how do I add the termination?

Helibob how do I add the termination?

Based on my speculation as to what is wrong, try this.

byte latLength = Serial.readBytesUntil('?', latStream, 10); 
latStream[latLength + 1] = '\0';
Serial.println(latStream);

Let us know if it works.

byte latLength = Serial.readBytesUntil('?', latStream, 10); 
latStream[latLength + 1] = '\0';
Serial.println(latStream);

UKHeliBob is correct about the need to add null termination, but because the array is zero indexed, and the function return counts up from 1, the added termination needs to be at latStream[latLength] = '\0'

You still need to initially declare the array to some size larger than the expected data string length before the function tells you exactly how large it is, but with the explicit termnation, the length can change.

I was under the impression that read bytes until would stop either when my special character is reached, it reads the number of bytes for the buffer, or it reaches the /0.

The readBytesUntil() method will read some maximum number of characters, or return when the specified character is received, or return when there is no more data to read for a defined period of time.

The readBytes() method will read some maximum number of characters, or return when there is no more data to read for a defined period of time.

There is NEVER a NULL actually sent, so expecting a NULL is unrealistic.

UKHeliBob:
... just happens to work because when declared the buffer will be full of zeroes...

Not a safe assumption...

char myArray[30];
The compiler only allocates memory for myArray and does no initialization.

It is not an assumption, it is a fact. When a global array is declared, say something like this

char anArray[30];

all of its elements are set to zero. However, this is not the case when an array is declared as above within a function. In that case the elements of the array may contain junk.

However, I would suggest that it is not good practice to rely on the auto initialisation method and that the zero should be inserted specifically. This is, of course, particularly the case when an array is used several times in a program or it is declared within a function.

I have Googled this standard C solution to initilizing a local array.
Does it work in Arduino?

char anArray[1024] = {0};

Yes, Arduino uses a C/C++ compiler, and you can leave it to default initialization also:

char anArray[1024] = {};

This will set the first two elements to 1, the rest are default initialized to 0:

char anArray[1024] = {1,1};

When using dynamic memory, you can default initialize arrays like this:

char *ptr = new char[ 1024 ]();