0

I'm using dtostrf() to transcribe a float into a string for my touchscreen. I'd leave it as a float, but my screen orientation library function only works with a string.

So I have a char array, then a float value. then dtostrf().

char buffer[6]
float X; //changes each loop with incoming sensor data from pixy camera
float thingposition = X * .0064  //float calculations are performed.

void loop(){
Camera.Get_X();   //retrieves X from the camera. waits until X has been recieved via UART serial
dtostrf(buffer, 5, 2, thingposition) // writes float to string with a controled decimal place and all that - dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf);
Tft.drawString(buffer, 70, 215, 5, WHITE);   //print output to screen
}

Note that this is a simplified version. In my code I also have methods for overwriting my last string value on the LCD when the new one has changed. I also have several "if" conditionals that control whether I write the buffer value to the screen or instead send an error message. Those things do not seem to be causing me problems though, except to say that my buffer overflows only occur when I cause the sensors to flip the conditionals at just the right interval.

For example, when I place my hand in front of the camera, that triggers the error message. Take my hand away, and then the number "thingposition" is printed to the screen. But if I time my hand just right, the buffer overflows and numbers loop across the screen. Usually this freezes the whole sketch. The best I can figure is that since dtostrf() does not check bounds, and since the calculated float values can be longer than the buffer has room for, that sometimes dtostrf() tries to write too many digits to the buffer. Something timing-related seems to trigger the problem, but what I need is some way to prevent the overflow in the first place.

I've tried rounding "thingposition" to a certain number of sigfigs, but I can't seem to truncate it at the desired decimal point. That shouldn't matter though, since dtostrf has controls for the minimum total length including the decimal point, as well the number of digits after the decimal. I also tried making sure that the last byte in my char array is null by adding this right after dtostrf():

stringval[5] = NULL;   //make sure buffer is null-terminated to prevent buffer overflow

I've tried doubles, different ways of rounding, and anything I could find, but the problem has persisted for several weeks and a few steady days worth of searching for a solution. I'll be glad to provide further information if anyone has any ideas on what to try. Thanks everyone.

1
  • 1
    If thingposition is 100000 or more, then dtostrf() will use more than 5 characters to represent the number. It will use no more than 2 decimal digits (less if they won't fit in 5 characters), but it won't compromise the main number to fit it inside your buffer. If you don't want that, clip the number to less than 100000 Jul 14, 2016 at 10:04

1 Answer 1

1

The problem with using "simplified code" is that your 'simplification' may not have captured your problem. But I will take your code as given and assume that it's correct (which I'm afraid it isn't).

var
    char buffer[6]  // Won't compile - no semicolon (';')
    float X;        // Uninitialised value - could be 1000000000000
    float thingposition = X * .0064 // No ';', and .0064 of 10000000000 is huge!

void loop(){
    Camera.Get_X(); // Should this be "X = Camera.Get_X();" ?
    dtostrf(buffer, 5, 2, thingposition)  // No ';'. You NEED to recalculate thingposition!

If the above code is a good representation of what you've got, you seem to be assuming that you define the formula once (in the thingposition line), and it automatically applies that formula, like Excel, when you change its input, X. Sorry, programs don't work that way.

Once you calculate thingposition, it becomes independent of where it came from. If you change other variables, thingposition kees its value until you explicitly change it.

How do you fix it? You recalculate thingposition every time:

var
    char buffer[6];      // Hopefully big enough - see below
    float X;             // Used by Camera.Get_X()
    float thingposition;

void loop(){
    X = Camera.Get_X();        // Get X from Camera
    thingposition = X * .0064; // Calculate thingposition
    dtostrf(buffer, 5, 2, thingposition);

Note that dtostrf() will try to obey your size parameters, BUT. If the float is larger than your maximum size, it WILL print out the full number anyway. That is, if thingposition is 1000, it will produce 5 characters all right: "1000.". But if it's 1000000, it will produce "1000000" which is seven characters. You need to ensure that buffer is big enough to handle larger numbers than you expect.

Do the maths. If X is guaranteed to be between 0 and 255, then multiplying it by 0.0064 will result in a maximum value of 1.632, so the above dtostrf() woud produce " 1.63". No problem!

6
  • I'm interested in your comments on dtostrf() and buffer size. Not much of the other stuff applies, because of course I calculate at every loop. Apologies for not including everything, but I didn't want to take up all your time and bore you. Also it's proprietary. I didn't think much of laying out the whole thing because I had assumed anyone would overlook all that and focus on the buffer, assuming all else is functional. Jul 14, 2016 at 16:20
  • My floats are almost always guaranteed to be a little longer than the buffer after the calculation is performed. I'd like to cut them off at the appropriate digit after rounding if I could because I think that would solve the problem. dtostrf() seems to handle it well... until it doesn't. Jul 14, 2016 at 16:20
  • And there's your problem. dtostrf() won't "lie" in its output. Rounding's OK, but truncating orders of magnitude? Like I said: either increase the buffer size going into dtostrf(), or be explicit and truncate the number first: do a thingposition = fmod(thingposition, 100000); to knock off the hundred-thousands-and-above digits that won't fit. Then the onus is on you rather than the function! Jul 15, 2016 at 0:49
  • I'm a little confused. You said that dtostrf() would produce "1.63" from 1.632. But you also are saying that dtostrf() can't lie to adjust its output. Is that a contradiction? And I'm not sure what you mean by "truncating orders of magnitude". the float value 1.63 is the same magnitude as 1.632, it's just been reduced an order of precision. Also, the function fmod() does not seem to work for truncating ending digits, and I can't find a function that does. In fact, your example seems to add more digits. Serial.println(fmod(x, 100000),24); output: 9.123456954956054687500000 Jul 18, 2016 at 17:44
  • That is, if x = 9.123456789 Jul 18, 2016 at 19:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.