How can I replace dtostrf()

Good evening,

I am actually using a function. Here is the code

// Serialize the lat, long, altitude to a CSV string that can be published to the specified feed.
void logLocation(float latitude, float longitude, float altitude, Adafruit_MQTT_Publish& publishFeed) {
  // Initialize a string buffer to hold the data that will be published.
  char sendBuffer[120];
  memset(sendBuffer, 0, sizeof(sendBuffer));
  int index = 0;

  // Start with '0,' to set the feed value.  The value isn't really used so 0 is used as a placeholder.
  sendBuffer[index++] = '0';
  sendBuffer[index++] = ',';

  // Now set latitude, longitude, altitude separated by commas.
  dtostrf(latitude, 2, 6, &sendBuffer[index]);
  index += strlen(&sendBuffer[index]);
  sendBuffer[index++] = ',';
  dtostrf(longitude, 3, 6, &sendBuffer[index]);
  index += strlen(&sendBuffer[index]);
  sendBuffer[index++] = ',';
  dtostrf(altitude, 2, 6, &sendBuffer[index]);

  // Finally publish the string to the feed.
  sprint(F("Publishing location: "),2);
  sprintln(sendBuffer,2);
  if (!publishFeed.publish(sendBuffer)) {
    sprintln(F("Publish failed!"),2);
    txFailures++;
  }
  else {
    sprintln(F("Publish succeeded!"),2);
    txFailures = 0;
  }
}

Since I am not using SoftwareSerial, the function dtostrf()() bufs and teh Serial Terminal return me an error

error: 'dtostrf' was not declared in this scope

How can replace dtostrf()?
I think sprintf() can help, but I do not understanf exactely how works dtostrf(), particularely regarding the 2nd and 3rd parameter.

Someone can give me a exemple how a way to use dtostrf()?

Thanks a lot

What Arduino IDE are you using?
Post all your code.

But this call is unlikely to work correctly because width is too small:

dtostrf(altitude, 2, 6, &sendBuffer[index]);

see the reference for dtostrf here:

I think what you have there is an example of using dtostrf(). You should be able to use it, it's part of stdlib.h. The error is puzzling, perhaps a previous line had an error.

Thanks for your replies.

I added this at the top of my ino file

#include <stdlib.h> // because dtostrf()

But it did not solve the problme

I also comment all dtpstrf() and the error message desapear. If I comment one of them, the error message comeback. So I do not think a precious line make the error.

I am using Adafruit Feather MO with IDE1.6.5. Feather MO is like Arduino Due and I can not use SoftwareSerial. I am using HardwareSerial

What I do not understand, and what does the 2nd and 3rd parameter of dtostrf(). I guess the first parameter is copyied to the 4th caracter depending of the 2nd and the 3rd

It's time you posted your whole program, don't you think ?

Yes, I am sorry I saw it, but I am still confuse about the second caracter.
Ok the 3rd is the number of digit after the decimal sign. But wath about the second.
"width" it means the number of digit before the decimal sign? that right?
I think it make sens...

So I suppose this make the same

dtostrf(latitude, 2, 6, &sendBuffer[index]);
sprintf(&sendBuffer[index], "%.6f", latitude);
dtostrf(longitude, 3, 6, &sendBuffer[index]);
sprintf(&sendBuffer[index], "%.6f", longitude);
dtostrf(altitude, 2, 6, &sendBuffer[index]);
sprintf(&sendBuffer[index], "%.6f", altitude);

pierrot10:
Thanks for your replies.

I added this at the top of my ino file

#include <stdlib.h> // because dtostrf()

But it did not solve the problme

It's already included by default in the IDE. So of course, it made no difference.

Delta_G:
So if you want 6 digits after the decimal, then width will have to be more than 8 to include a digit before the decimal and the decimal point itself.

I've just been playing with this, trying to work it out too.

I did some tests. If 'width' is larger than the total number of chars including the decimal point, it pads out the beginning of the string with spaces. (See the last test below.)
If 'width' is smaller than the total number of characters, it has no effect.

void setup()
{
    //  dtostrf(val, width, precision, buffer);

    Serial.begin(115200);
    double dVal = 123.4567;
    char buffer[10];

    dtostrf(dVal, 5, 2, buffer);  // Prints "123.46"
    Serial.println(buffer);

    dtostrf(dVal, 4, 2, buffer);  // Prints "123.46"
    Serial.println(buffer);

    dtostrf(dVal, 2, 3, buffer);  // Prints "123.457"
    Serial.println(buffer);

    dtostrf(dVal, 6, 2, buffer);  // Prints "123.46"
    Serial.println(buffer);

    dtostrf(dVal, 7, 2, buffer);  // Prints " 123.46"
    Serial.println(buffer);
}

Delta_G:
I guess it did say width was a "minimum width".

Yep, the keyword was "minimum".

So I suppose you could use a 0 in there to guarantee you never got any white spaces.

Yes again. 'width' is a means of adding white spaces. To guarantee you get none, 'width' can be set at 0.

dVal=0.12345;
char buffer[10];
dtostrf(dVal, 0, 2, buffer);  // Prints "0.12"
Serial.println(buffer);

inspired by oldSteve my test sketch

//
//    FILE: dtostrf.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo dtostrf(val, width, precision, buffer);
//    DATE: 2015-12-12
//     URL: http://forum.arduino.cc/index.php?topic=365226.0
//
// Released to the public domain
//

void setup()
{
  Serial.begin(115200);
  Serial.print("Start ");
  Serial.println(__FILE__);

  double dVal = 123.4567;
  char buffer[24];

  for (int width = 0; width < 20; width++)
  {
    dtostrf(dVal, width, 2, buffer);
    Serial.print(width);
    Serial.print("\t");
    Serial.print(strlen(buffer));
    Serial.print("\t\"");
    Serial.print(buffer);
    Serial.println("\"");
  }
  Serial.println("Done...");
}

void loop()
{
}

output

Start dtostrf.ino
0	6	"123.46"
1	6	"123.46"
2	6	"123.46"
3	6	"123.46"
4	6	"123.46"
5	6	"123.46"
6	6	"123.46"
7	7	" 123.46"
8	8	"  123.46"
9	9	"   123.46"
10	10	"    123.46"
11	11	"     123.46"
12	12	"      123.46"
13	13	"       123.46"
14	14	"        123.46"
15	15	"         123.46"
16	16	"          123.46"
17	17	"           123.46"
18	18	"            123.46"
19	19	"             123.46"
Done...

Very nice, Rob. That'll be very useful for others in the future, when this thread has disappeared into the depths.
I kept a copy of my tests in my Sketchbook as a reminder. Now I'll add your's as well.

The documentation on 'dtostrf()' wasn't too clear at all on the meaning of 'width'. I originally thought the same as Delta_G, but was proven wrong when I did those tests earlier.

Thanks for taking the time.

[off topic]

Hi Steve, think most SW people have such collections.

But if you are searching for some "old" code or whatever you can always try the wayback machine

e.g - Wayback Machine

robtillaart:
[off topic]
Hi Steve, think most SW people have such collections.

Yep. I keep examples for everything that I think I might forget. (And believe me, that's a lot of stuff.)

But if you are searching for some "old" code or whatever you can always try the wayback machine
e.g - Wayback Machine

Thanks for that link Rob. A very interesting site. I've added it to my collection. :slight_smile:

Hello,
I stil have issue with my function

As dtostrf reports a error, I am tryinf to work with sprintf.

I have a doubt regarding the format of sprintf

For exemple, if we look at this one

 sprintf(&sendBuffer[index], "%.6f", latitude);

latitutude as a value of 46.123456

Then here %.6f:
% is the value of latitude
.6 is number of decimal
f is floast.

But it does not work

If I do

logLocation(46.123456, 6.234567,443.00);

The below finction should print

MQTT
Publishing location: 0,46.123456,6.234567,443.0000000

But it print

MQTT
Publishing location: 0,,,

So it look that I badly use the sprint function

void logLocation(float latitude, float longitude, float altitude, Adafruit_MQTT_Publish& publishFeed) {
 // Initialize a string buffer to hold the data that will be published.
 char sendBuffer[120];
 memset(sendBuffer, 0, sizeof(sendBuffer));
 int index = 0;

 // Start with '0,' to set the feed value.  The value isn't really used so 0 is used as a placeholder.
 sendBuffer[index++] = '0';
 sendBuffer[index++] = ',';

 // Now set latitude, longitude, altitude separated by commas.
 //dtostrf(latitude, 2, 6, &sendBuffer[index]);
 sprintf(&sendBuffer[index], "%.6f", latitude);
 index += strlen(&sendBuffer[index]);
 sendBuffer[index++] = ',';
 //dtostrf(longitude, 3, 6, &sendBuffer[index]);
 sprintf(&sendBuffer[index], "%.6f", longitude);
 index += strlen(&sendBuffer[index]);
 sendBuffer[index++] = ',';
 //dtostrf(altitude, 2, 6, &sendBuffer[index]);
 sprintf(&sendBuffer[index], "%.6f", altitude);

 // Finally publish the string to the feed.
 sprint(F("Publishing location: "),2);
 sprintln(sendBuffer,2);
 if (!publishFeed.publish(sendBuffer)) {
   sprintln(F("Publish failed!"),2);
   txFailures++;
 }
 else {
   sprintln(F("Publish succeeded!"),2);
   txFailures = 0;
 }
}

The how can I correctly use sprintf to get 6 decial?

I can post all of my code here, but it a lot.

Go at the buttom of setup(), just below
#ifdef ADAIO

The logLocation() is at the end.

This

dtostrf(altitude, 2, 6, &sendBuffer[index]);

return me that error message

mo808.ino: In function 'void logLocation(float, float, float, Adafruit_MQTT_Publish&)':
mo808:787: error: 'dtostrf' was not declared in this scope
'dtostrf' was not declared in this scope

Hope it help

Yes, it complain for the 3. If I comment the 3, I do not have a such error message.
But may be the good way to format sprintf will solve my issue. I am trying..

dtostrf() emulation using sprintf() for non-AVR targets:

#ifndef ARDUINO_ARCH_AVR
#include <avr/dtostrf.h>
#endif

Yes may be, I thought about it as well >:(
But there is no another easy way to do with tout dtostrf()

I could solve my problem as the following, but I have not 100% convinced.

First I use a function to convert float to char

void convertFloatToChar(float source, char * destination, int nbDec)
{
  int ndec = nbDec;
  String r = "";
  int v = (int)source;
  
  r += v;     // whole number part
  r += '.';   // decimal point 
  
  int i;
  for (i=0;i<ndec;i++) {
    // iterate through each decimal digit for 0..ndec 
    source -= v;
    source *= 10; 
    v = source;
    r += v;
  }
  
  //Convert to char  
  r.toCharArray(destination,r.length()+1);
  
  //SerialUSB.println(destination);

}

What do you think about it.

The I change my logLocation as this:

void logLocation(float latitude, float longitude, float altitude, Adafruit_MQTT_Publish& publishFeed) {
  // Initialize a string buffer to hold the data that will be published.

  char destination[11];
  char sendBuffer[120];
  memset(sendBuffer, 0, sizeof(sendBuffer));
  int index = 0;

  strncpy(sendBuffer,"0",1);
  strcat(sendBuffer,",");

  convertFloatToChar(latitude, destination,6);
  strcat(sendBuffer,destination);
  strcat(sendBuffer,",");

  convertFloatToChar(longitude, destination,6);
  strcat(sendBuffer,destination);
  strcat(sendBuffer,",");

  convertFloatToChar(altitude, destination,6);
  strcat(sendBuffer,destination);

  
  SerialUSB.println(sendBuffer); // Print : 0,46.215808,6.143939,0.000000



/* I do not want to publish yet
  // Finally publish the string to the feed.
  sprint(F("Publishing location: "),2);
  sprintln(sendBuffer,2);
  if (!publishFeed.publish(sendBuffer)) {
    sprintln(F("Publish failed!"),2);
    txFailures++;
  }
  else {
    sprintln(F("Publish succeeded!"),2);
    txFailures = 0;
  }
  */
}

Here is the initial valur

Initial Latitude: 46.215808
Initial Longitude: 6.143940

And here is the result

0,46.215808,6.143939,0.000000

But as you can see, the longitude is not exact, I should have printed

6.143940

OK, it's not a big difference, it would be nice to be exact.

What do you tink about it?

PS: I hesitated to use strncat() instead of strcat()...

Floating point values on the Arduino are single precision and have only 6-7 meaningful digits.
The difference between 6.143939 and 6.143940 is not significant.
It is an accident that 46.215808 is reproduced exactly.