Go Down

Topic: dtostrf() help (Read 140023 times) previous topic - next topic

Nebula

Hi everyone,

I've written this code to send two floating points over the serial as a string, separated by a semi-colon. At the moment this code returns values like:
0.182710.4698 ; 10.4698
0.411215.7008 ; 15.7008
0.730820.9269 ; 20.9269

Which is basically XvalueYvalue ; Yvalue, whereas what I actually want is Xvalue ; Yvalue. How would I make it print in this way?

This is the code so far (ignore the maths, that's to convert servo angle and ping distance to an x and y value):

Code: [Select]
#include <Servo.h>
#include <math.h>

Servo myservo;

float xval;
float yval;
float rad;

int distance = 3;  //This is just to test the code, I'll add ping code later
int deg = 0;

char x [6];
char y [6];


void setup(){
 
 myservo.attach(9);
 Serial.begin(9600);
 
}


void loop(){
 while (deg <180){
   myservo.write(deg);
   rad = deg * 0.0174532925;
   xval = 300 - ((distance * cos(rad))*100);  
   yval = (distance * sin(rad)) * 100;
   dtostrf(xval, 6, 4, x);// This is where I get confused, I read that the 6 is the length of the number
   dtostrf(yval, 6, 4, y); // and that the 4 is the number of numbers after decimal point? I'm sure this is wrong?
   Serial.println(String(x) + " ; " + String(y));
   deg++;
 }

}


Thanks.

dc42

1. Your character buffers x and y are 1 character too short, they need to be at least 1 character longer than the number of characters you are writing to them (to allow for the trailing null). Declare them as [7] instead of [6].

2. Don't use the String class (IMO it should not be part of the Arduino library). It will work OK when used in simple ways like this, but do something more complicated and you are likely to run out of RAM. Use this instead:

Code: [Select]

Serial.print(x);
Serial.print(" ; ");
Serial.println(y);
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

PaulS

Why are you using dtostrf() at all? The Serial.print() function is perfectly capable of sending a float to the serial port.
The art of getting good answers lies in asking good questions.

Nebula

@ dc42, thanks for the help, I never thought of writing out this way, that makes life much simpler!

@PaulS, I only needed them to try and write all the values as one string - writing them as floating point values works just as well when I'm using dc42's method of printing the values.

Thanks for the help :)

jamilm9

I have a question about dtostrf(). I am trying to get a temperature sensor (lm35) to send data using a ethernet shield to thingspeak. To send data to thingspeak, it must be a string.
Right now, I am converting a float to a long to a string. This however removes the decimal points (I think). I am trying to use dtostrf() to convert my float directly to a string so that there are decimal points.
Code: [Select]
int reading = analogRead(sensorPin);
  float voltage = reading * 5.0;
  voltage /= 1024.0; 
int  temperatureC = (voltage) * 100;
String temperatureS = dtostrf(temperatureC, 2, 2, 10);
String temperature = (tem, DEC);

However it fails to compile because it says "invalid conversion from 'int to 'char*'
Does anyone know how I can fix this?
Thank You

PaulS

So, the first question is why you are using dtostrf to convert an int to a string. The dtostrf() function is to convert a double (or float) to a string.

The second question is whether or not you have read the documentation on dtostrf. It appears not.

Code: [Select]
char * dtostrf (double __val, signed char __width, unsigned char __prec, char *__s)
Quote
The dtostrf() function converts the double value passed in val into an ASCII representationthat will be stored under s. The caller is responsible for providing sufficient storage in s.

Conversion is done in the format "[-]d.ddd". The minimum field width of the output string (including the '.' and the possible sign for negative values) is given in width, and prec determines the number of digits after the decimal sign. width is signed value, negative for left adjustment.

The dtostrf() function returns the pointer to the converted string s.

10 is not a buffer that dtostrf can write to, is it?
The art of getting good answers lies in asking good questions.

jamilm9

Sorry for the error. I accidentally had it converting a integer. I am pretty new to arduino programing and I am wondering if you would be able to tell me what to put after my float in the dtostrf()
If it is supposed to be dtostrf(FLOAT,WIDTH,PRECSISION,BUFFER);
I know the FLOAT in my case would be temperatureC but what would the WIDTH, PRECSISION and BUFFER be?
I read the dtostrf() documentation but I don't understand what it means.
Thank you for the help.

PaulS

WIDTH is the number of characters to use in the output. For a value in the range 0.000 to 5.000, the width should be the precision +2. More is OK.

PRECISION is the number of characters after the decimal point. For temperature, 1 would be sufficient.

BUFFER is where the write the characters to.

Code: [Select]
char buffer[10];
dtostrf(temperatureC, 5, 1, buffer);


There is no reason to make a String out of buffer.

Code: [Select]
String temperature = (tem, DEC);
This is rubbish. It looks like you took Serial.print(tem, DEC), and stripped the Serial.print off the front. You can't do that, and produce anything meaningful.
The art of getting good answers lies in asking good questions.

jamilm9

Thank you for your help. I finally got it to work. This is the code which worked for me.
Code: [Select]
int reading = analogRead(sensorPin);
  float voltage = reading * 5.0;
  voltage /= 1024.0; 
float  temperatureC = (voltage) * 100;
char buffer[10];
String tem = dtostrf(temperatureC, 5, 2, buffer);
String temper = (tem);


Here is the chart: https://www.thingspeak.com/channels/1531/charts/2

Once again, thank you.

PaulS

What kind of temperature sensor are you using that discerns temperature variations as small as 0.01 degrees?

Why are you converting the char array to a String object?

Why are you then copying that String object?
The art of getting good answers lies in asking good questions.

davebaldwin

I am trying to use dtostrf to print out values to a Nokia 5110 LCD.

Here's a snippet of my code:
Code: [Select]

/* Get distance to the destination */
double distance_meters = TinyGPSPlus::distanceBetween(tinygps.location.lat(), tinygps.location.lng(), DEST_LATITUDE, DEST_LONGITUDE);
/* get bearing to destination */
double bearing = TinyGPSPlus::courseTo(tinygps.location.lat(), tinygps.location.lng(), DEST_LATITUDE, DEST_LONGITUDE);

Serial.print("Distance to dest: ");
Serial.println(distance_meters);     

// generate chars for easy printing
int dist_m_int = (int)distance_meters;
float dist_km = distance_meters / 1000.0;   
int dist_km_int = (int)dist_km;   
int bearing_int = (int)bearing;

// generate char arrays for printing to LCD screen
char m_buffer[sizeof(dist_m_int)+1];
char km_buffer[sizeof(dist_km_int)+1];
char brng_buffer[sizeof(bearing_int)+1];

dtostrf(distance_meters, 1, 0, m_buffer);
dtostrf(dist_km, 1, 0, km_buffer);
dtostrf(bearing, 1, 0, brng_buffer);



What I'm seeing (and can verify in the serial monitor) is that the distance_meters value coming out of the GPS function is reasonable (say, ~2017 kilometres), but then m_buffer seems to get a value of ~20137. I've tried this for several different locations (i.e. different distances) and I am consistently getting values in the char buffer that are off by a factor of 10.

However, brng_buffer content, however, is exactly as expected. Which seems ridiculous to me since the instructions processing the distance_meters and bearing values look to be exactly the same.

Any ideas?  I've been stuck on this for a couple of days.  Thanks in advance.

PaulS

Frankly, printing a float to zero decimal places looks pretty stupid. Just use ints if you don't want the accuracy of the float.
The art of getting good answers lies in asking good questions.

davebaldwin


Frankly, printing a float to zero decimal places looks pretty stupid.

Thanks!

Just use ints if you don't want the accuracy of the float.

Use ints where? The dtostrf function documentation indicates a double (same as float) is required as the first argument. Can I pass in an int instead?
I am using the sizeof(int var) to size the char buffers because I only want to see integers on the LCD.

PaulS

Quote
Can I pass in an int instead?

You could, but why?

Quote
I am using the sizeof(int var) to size the char buffers because I only want to see integers on the LCD.

Why, then, do you need to use floats? Why do you need to use dtostrf()? Just use the print() method of the LCD class to print to the LCD, whether you are printing floats or ints.
The art of getting good answers lies in asking good questions.

davebaldwin

The LCD library I'm using won't print floats. I need to pass in a character array.

Go Up