dtostrf() output not as expected.

Hi all,

I am trying to convert float values into character arrays. The float values are gps coordinates from a gps module. The output of my code is not what I expect, the last part of the float is always off by a few numbers.

Here is my code:

  float lon = 34.123456;
  float lat = -138.654321;

  char lonBuffer[20];
  char latBuffer[20];

 
void setup() {

  dtostrf(lon,9, 6, lonBuffer);//(float,lengthIncDecimalPt,intsAfterDecimalPt,array)
  dtostrf(lat,11, 6, latBuffer);
 
  Serial.begin(9600);

  Serial.print("LON: ");
  Serial.println(lonBuffer);
  Serial.print("LAT: ");
  Serial.println(latBuffer);
  Serial.println();
  
 
}
 
void loop(){ 

}

The output in the serial monitor is:

LON: 34.123455
LAT: -138.654330

I would have expected:

LON: 34.123456
LAT: -138.654321

Could anybody point out where about i'm going wrong?

Thanks.

Floating point precision limitations.

Any way to get around this?

The Arduino website says:

"Floats have only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point."

Wouldn't my code work since it has six digits after the decimal point.

I played around with this and only got the expected output when I had 5 digits after the decimal point.

Why not six digits after the decimal point?

Thanks

Have you tried to use double?

A double is no good, the Arduino website says its the same size as the float. I get the same problem when using a double.

Thanks

Does the GPS module actually give Floating point numbers or string formats? In others is there any reason to make them floating point in the first place?

Im using a gps module I got off eBay and the TinyGps library. I want to convert the latitude and longitude floats to char arrays so I can then iterate through the arrays transmitting morse code of each digit.

I understand that tinyGps is also capable of delivering long values (see: TinyGPS | Arduiniana). That would make your program compacter and you would only have to translate from integral to angular coordinates. I doubt that the floating point will be sufficient to solve your problem.

Ok, if the latitude and longitude were long type how would I convert to an array. I want to do something like this long lat = 138123456; then convert this an array, lat={1,3,8,1,.....}.

Any ideas on how to do that?

I was planning of using a "for" loop to iterate through the array and then a switch statement for the morse code from 0 to 9.

Thanks.

mcleja:
The Arduino website says:

"Floats have only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point."

Wouldn't my code work since it has six digits after the decimal point.

I played around with this and only got the expected output when I had 5 digits after the decimal point.

Why not six digits after the decimal point?

Thanks

Look at the second sentence of the quote in your post

Duh I can see that now.

this might help : Degrees, Minutes, Seconds to decimal Degrees calculator and vice-versa

This is the underlying code:

function azimuth(deg,min,sec) {
if (deg < 0)
   {
   azi= -1.0 * deg + 1.0 * min/60.0 + 1.0 * sec/3600.0;
   return -1.0 * azi;
   }
else
   {
   azi=1.0 * deg + 1.0 * min/60.0 + 1.0 * sec/3600.0;
   return azi;
   }
}

function round(number,n) {
return Math.round(number*Math.pow(10,n))/Math.pow(10,n);
}

function computeform(form) {
if (checkform(form)) {

fazimuth  = azimuth (form.degrees.value,form.minutes.value,form.seconds.value);

form.fazimuth.value       =round(fazimuth,4);
form.fazimuth5.value      =round(fazimuth,5);
form.result6.value        =round(fazimuth,6);

// reverse calculation: results are resultdeg, resultmin, resultsec and resultmindec
integerdeg = Math.floor(form.degreesdecin.value,0);
form.rd.value = 1.0 * integerdeg;
form.rd2.value = 1.0 * integerdeg;

resultminutes = 60.0 * (form.degreesdecin.value - integerdeg);
integermin  = Math.floor(resultminutes);
form.rm.value = integermin;

resultseconds = 60.0 * (resultminutes - integermin);
form.rs.value = resultseconds;

form.rf.value = resultminutes;

}

Ok, that is how to get the degrees,minutes and seconds.

I have written the following code for the morse code:

void dit(){
  tone(5,500);
  delay(50);
  noTone(5);
 delay(50); 
}

void dah(){
  tone(5,500);
  delay(150);
  noTone(5);
  delay(50);
}

void one(){
  dit();
  dah();
  dah();
  dah();
  dah();
  delay(50);
}

void two(){
  dit();
  dit();
  dah();
  dah();
  dah();
  delay(50);  
}

void three(){
  dit();
  dit();
  dit();
  dah();
  dah();
  delay(50); 
}

void four(){
  dit();
  dit();
  dit();
  dit();
  dah();
  delay(50);
}

void five(){
  dit();
  dit();
  dit();
  dit();
  dit();
  delay(50);
}

void six(){
  dah();
  dit();
  dit();
  dit();
  dit();
  delay(50);
  
}

void seven(){
  dah();
  dah();
  dit();
  dit();
  dit();
  delay(50);
  
}

void eight(){
  dah();
  dah();
  dah();
  dit();
  dit();
  delay(50);
}

void nine(){
  dah();
  dah();
  dah();
  dah();
  dit();
  delay(50);
}

void zero(){
  dah();
  dah();
  dah();
  dah();
  dah();
  delay(50);
  
}

Any suggestions to then output with morse code?

Thanks

You might want to consider a 2 dimensional array as you are only transmitting numbers like:

var MorseCodes[10][5] = {{0,0,0,0,0} ,   // 0
                                   {1,1,0,0,0} ,   // 1
                                        etc
                                   {0,0,0,0,1}};  // 9
//
// send code
//
void sendCode(int code) {
    for (int i = 0; i < 5; i++){
        sendTone(MorseCodes[code][i]);
    }
}
//
// send the tone 
//
void sendTone(int tone) {
    //
    //
    //
    tone(5,500);
    if (tone == 1) {
        delay(50);      // dih
    } else {
        delay(150);    // dah 
  noTone(5);
  delay(50);
}

[/code]

Thats a good way of doing it, thanks. I am going to convert the long latitude and longitude longs using the ltoa function. Then iterate through and transmit the morse code.

Thanks for all the help.