Concatenate Float w/ a String

Been tinkering with this for a while and just about scratching my head at why it's been so difficult.

I am using the Adafruit library to pull sensor data from 2 DHT11's the values returned are float vars

I'm then trying to post them to a remote server using an Ethernet shield with a ENC28J60 chip
using the EtherCard library

did some homework and tinkered with this on and off for a couple weeks, most of the common C methods aren't available and I've yet to find a workable reference

I have a sample of my code as it stands posted at

line 132 makes the GET call (commented out)
lines 121 and after are attempts to concatenate the values, I know this is an easy fix for someone with experience so I'm reaching out for a snippet or reference

thanks

I had some trouble with floats and strings but I got it working. Nick Gammon pointed out that there are some issues with the current string library, it may not be your immediate issue but its worth noteing .
check this out maybe it will help. http://arduino.cc/forum/index.php/topic,146586.0.html

if you look in my code I put a few floats into Strings just after the loop()

Multiply it by 10^n where n is the number of digits after the decimal point.
Assign the resulting value to an int or long depending on the size of n.
Do something like this to actually stuff it into the string:

sprintf(buffer,"MyFloat: %d.%d", myInt/n, myInt%n);

Where myInt is the result from the previous step and n is the number of digits of precision.

Thank you Arch -- seems to have done the trick

have to see about cleaning up my hacky code and pushing it to github

I was having similar problems with sprintf and floats (http://arduino.cc/forum/index.php/topic,148024.msg1112201.html#msg1112201). I was unaware of the problems with floats, sprintf and Arduino.

Arrch certainly pointed me in the right direction and I wrote a function using his principles. It works fine and does just what I want it to do THANK YOU ARRCH!

However I now have a different bug... for some reason I can't pass the integer value of the number of decimal places I want to display. Have a look at the code below. You'll notice that if I pass the number of decimal places as an argument to the function (FtoChar) I lose 0.01 in the values. But if I hard code the decimal places inside the function (FtoChar2) it works fine.

After a bit of debugging I found out where the problem is and I'm almost more confused. If you uncomment my DEBUG lines you'll notice that I get different results from what should a simple calculation of 10 to the power of 2. If I set iDigs to the value of 2 within the function I get 100 (as I should). If I pass iDigs as an argument, with the integer value of 2, to the function then 10 to the power of iDigs becomes 99 instead of 100 thereby screwing up my actual values.

//////////////////////////////////////////////////////
//
// FtoChar
//
// Float to char
//
// fVal  Float value to be converted
// iDigs Number of decimal places
// cF    Char var to be modified
//
char* FtoChar(float fVal, int iDigs, char* cF) {

    // Separator = 10 ˆ Number of decimal places
    long dSep = pow(10, iDigs);

// DEBUG
// Serial.print(F("FtoChar dSep: "));
// Serial.println(dSep);

    // Display value = floal value * separator
    signed long slVal = fVal * dSep;

    sprintf(cF, "%d.%d", int(slVal / dSep), int(slVal % int(dSep)));
}

//////////////////////////////////////////////////////
//
// FtoChar2
//
// Float to char (forced 2 decimal places)
//
// fVal  Float value to be converted
// iDigs Number of decimal places
// cF    Char var to be modified
//
char* FtoChar2(float fVal, char* cF) {

  // Force number of decimal places to full 2
  int iDigs = 2;

  // Separator = 10 ˆ Number of decimal places
  long dSep = pow(10, iDigs);
// DEBUG
// Serial.print(F("FtoChar2 dSep: "));
// Serial.println(dSep);
    
  // Display value = floal value * separator
  signed long slVal = fVal * dSep;

  sprintf(cF, "%d.%d", int(slVal / dSep), int(slVal % int(dSep)));
}

void setup() {
  float fTemp = 18.90; // Temperature (Celsius)
  float fHum = 34.80;  // Humidity (percentage)
  
  char cMsg[254], cTemp[8], cHum[8];
  
  // Init serial monitor
  Serial.begin(9600);
  delay(1000);
  
  Serial.print(F("Temp : "));
  Serial.println(fTemp);
  Serial.print(F("Humidity : "));
  Serial.println(fHum);
  Serial.println();

  // Should output Temp: 18.90C - Humidity: 34.80%
  // But it ouputs Temp: 18.89C - Humidity: 34.79%
  FtoChar(fTemp, 2, cTemp);
  FtoChar(fHum, 2, cHum);
  sprintf(cMsg, "Temp: %sC - Humidity: %s%%.", cTemp, cHum);
  Serial.println();
  Serial.println(F("FtoChar:"));
  Serial.println(cMsg);
  Serial.println();
  
  // Correctly outputs Temp: 18.90C - Humidity: 34.80%
  FtoChar2(fTemp, cTemp);
  FtoChar2(fHum, cHum);
  sprintf(cMsg, "Temp: %sC - Humidity: %s%%.", cTemp, cHum);
  Serial.println();
  Serial.println(F("FtoChar2:"));
  Serial.println(cMsg);
  
}

void loop() {
}

Why is FtoChar() defined as returning a pointer to char when it does no such thing?

pow() is a float function, not an int function. It is a bit like using an elephant gun to swat mosquitoes, anyway.

You can not expect exact integer results.

Whoops. You're right PaulS I originally had returned the char as a pointer rather than passing the char as an argument. That didn't work as I wanted and when I put the char in as the argument I forgot to change the returning variable to void.

I do realise that pow() is a float (double) function. However I am unaware of any other function in Arduino which would allow me to return 10 to the power of X. Obviously I can replace it with a for loop multiplying 10 by itself X number of times. But that just seems a lot less elegant and less logical to me.

If I use pow(10,2), pow(10,4), pow(10,6), ... I always get full integer results even without doing an int(pow(10,2)), ...

However if I use pow(10,iDigs) I always get a "rounded down" result regardless of the value of iDigs.

Thanks.

Using pow with constants means that the compiler can pre-calculate it for you, presumably using non floating point math. Anything the arduino does with pow at runtime will be subject to rounding error.

Thanks to the notes here I've replaced my pow(10,iDigs) call to a for loop as follows:

    // Separator = 10 to the power of number of decimal places
    long dSep = 10;
    for (int i = 1; i < iDigs; i++) {
      dSep *= 10;
    }

This allows me to safely pass the number of decimal places as an argument to the function.

Any other ideas?

DerekErb:
Any other ideas?

You could make an ipow function:

int ipow(int base, int exp)
{
  int result = 1;
  for (int i=0; i<exp; i++)
  {
    result *= base;
  }
  return result;
}

Krupski:
Here's the solution to the floating point problem: http://www.hobbytent.com/viewtopic.php?f=1&t=158#p29

I wouldn't exactly call it a "problem", more of a "limitation". And that seems like an awful lot of work for something that can be done with a couple lines of code.

iPow()? Duh! I wish I thought of that!

Obviously that is the more elegant, and flexible, solution.

I hate you Arrch! I had put my code away for the day and you have made me go back to it!

Works perfectly. Thanks.

Arrch:
I wouldn't exactly call it a "problem", more of a "limitation". And that seems like an awful lot of work for something that can be done with a couple lines of code.

It's a big problem when someone wants to print a floating point number by doing it the standard way and ends up with a question mark.

Then they go over their code with a fine tooth comb, trying to find what's wrong.

Being unable to find anything wrong (because there isn't), they finally post here asking why it doesn't work.

And the problem is solved by simply checking "on" the "Enable floating point support" option in Preferences.

I fail to see how clicking a check box is "an awful lot of work"...

If anything is awful, it's using kludges of extra code to do what a simple "%f" would do.

Oh, yes I know. It would use a few extra bytes of flash and EVERYONE'S sketch is 32767 bytes long and they just can't spare any more.

I swapped to a Mega last month. Checking that box is fine by me.

I'd like to point out here that both serial land sdfat have .print and support floats. My original solution was to just use those to do what i wanted.

Krupski:
It's a big problem when someone wants to print a floating point number by doing it the standard way and ends up with a question mark.

So the effects create problems; that doesn't mean the cause is in itself, a problem. If I write a sketch that uses 3Kb SRAM and it doesn't work, I have a problem, but that doesn't make the memory limitation "the problem".

And the problem is solved by simply checking "on" the "Enable floating point support" option in Preferences.

I don't have that option in my Arduino IDE, so apparently, it's not that simple.

I fail to see how clicking a check box is "an awful lot of work"...

What happens when IDE changes break the "fix"? Where do I go to get the updated "fix"?

If anything is awful, it's using kludges of extra code to do what a simple "%f" would do.

Oh, yes I know. It would use a few extra bytes of flash and EVERYONE'S sketch is 32767 bytes long and they just can't spare any more.

Getting people to think about other ways to approach a problem and practice their problem solving skills through critical thinking is nothing to scoff at. Stuff like this also helps highlight the limitations of the hardware and encourages to think about it while they are writing their code.

What you said makes sense, except that limitation does not exist if they use up to date hardware. The mega came out ages ago then there was the mega 2560. Now we have the DUE.

Seeing as the overall goal of Arduino is to create prototype systems that may warrant actual development I don't see why the additional detail in data(i mean floating point data) availability should be limited.

%f is a lot more intuitive and usable then the horrible kludges I read. Isn't this meant to be an easy to use platform?

Arrch:
Getting people to think about other ways to approach a problem and practice their problem solving skills through critical thinking is nothing to scoff at. Stuff like this also helps highlight the limitations of the hardware and encourages to think about it while they are writing their code.

That's like telling me that, although I always USED to start my car by turning the key, I now have to do it by opening the hood and jumping the starter relay and yanking on the throttle cable.

Working around the missing floating point support is not "sharpening critical thinking skills", it's a waste of time.

Now, I fully agree... IF a person is running up against the memory limit and simply cannot spare another 1.5K, then there's no choice. But otherwise, why not do it the "right way"?

The reason I am so adamant about this is that when I first ran across the "problem", it drove me nuts. I KNEW I was using the "sprintf" format characters properly, but all I was getting was a question mark. Everything else worked (decimals, hex, etc...) so I HAD to be doing something wrong (so I thought). I spent (wasted) close to a WEEK trying to figure out what I was doing wrong. I had a project to finish (not a toy robot, but a real job that I was being paid to do) and I was stuck with the stupid sprintf problem (and I simply was not going to output data with line after line after line of "Serial.print(a_little_piece)" to build up a simple string).

I searched. I Googled. I tried the same code in the GCC compiler for Linux. It worked, so I was sure it was right. Hmmm maybe the Arduino board was bad. So I go and buy another one and had the same problem (i.e. wasted $50 of my customer's money).

Finally, in desperation, I asked about it here... never in a million years would I have guessed that %f was disabled to save a few bytes of code.

After many of the same type of replies (use parsing kludges, use strtof(), etc... finally someone told me the simple fact that floating point was left out on purpose.

I was FURIOUS! How absolutely absurd to do that and not give the user an option to enable it or disable it. And how absurd not to have it documented anywhere!!!

I understand WHY it was left out, but it should have at least had the option to use it or not. And it should be documented!

So, now that I know the problem (and the solution), I see others on here every other day with the same problem and they are given the same advice - kludge it.

User after user faces the same problem and asks the same question, yet the option (which is all of about 5 lines of JAVA code) isn't put into the IDE.

When I offer a simple modification to give the user a checkbox to enable or disable floating point support, everyone starts yelling "My code is 31K already and I just can't spare another byte!"

Baloney! Again, to use the car analogy, it's like buying a new car and then trying to start it, but not finding any place to insert the key. You KNOW there MUST be a key slot SOMEWHERE (all cars have them!), but you look and look and just can't find it. Finally in frustration, you call the dealer and they tell you "Oh your model doesn't have a key - the manufacturer left it out to save weight - gas mileage you know?" and then "There is a jumper cable in the trunk - open the hood and jump the starter".

Are you SERIOUS??? That is completely unacceptable!

I can't speak for others, but I personally do not enjoy beating my head against the wall... especially when there is nothing to gain by doing so.

ahref:
What you said makes sense, except that limitation does not exist if they use up to date hardware. The mega came out ages ago then there was the mega 2560. Now we have the DUE.

... And more expensive. Not every project needs a $10 microcontroller, especially if I can spend ~$1 on an ATTiny and accomplish what I need to with a simple "kludge"

Seeing as the overall goal of Arduino is to create prototype systems that may warrant actual development I don't see why the additional detail in data(i mean floating point data) availability should be limited.

I've always seen Arduino as a entry point to get people into the world of microcontrollers. If I want to actually put something into development, it wouldn't make much sense to spend 10x the amount on the chip for additional software support that can be handled with a "kludge" like this:

const int precision = 100;
int psuedoFloat = myFloat * precision ;
sprintf(buffer, "My Float: %d.%d", psuedoFloat / precision , psuedoFloat % precision);

Personally I find no issue with using C style strings. I am a NEWBIE to C and C++ and although the Strings are handy (lazy?) when it comes to C or C++, learning how to deal with array's or C style strings is much more important than saving a line of typing to waste 1.5K of program memory. The function to deal with that bad old issue... could well be a macro saved in what I call a "Snippet Library" (I use a sketch and add code as I find it and prove it). Keeping it in a sketch works for me as a macro/code library.
I am NOT defending the POOR choice made by the Arduino development team, not by any means but with 2K of sram in an Uno you encourage people with less programming experience than you to make uninformed decisions that waste more time than simply learning how to deal with simple arrays and the rather simple Arduino Crutch that makes sprintf work...
Time spent by the person writing the sketch to as well as the people involved in debugging it. wasted.

Bob

Isn't this meant to be an easy to use platform?

Sure. But take a look at the Arduino-specific functions, like digitalRead() and analogRead(). What types do those functions return? What about millis()? micros()? Can you find a single Arduino-specific function that returns a float?

PaulS:
Sure. But take a look at the Arduino-specific functions, like digitalRead() and analogRead(). What types do those functions return? What about millis()? micros()? Can you find a single Arduino-specific function that returns a float?

I'm beating the dead horse but......

A lot of the programs that I write are for student projects or for lab hardware. Lots of these have a user interface (usually a serial terminal).

The interface is usually arranged as menu pages with individual choices on each page.

Many of these menu pages display things like "Setting 1 is xxx.xx, Setting 2 is yyy.yy", etc. Usually there are many options to set and most of these are floating point values (like one I did the user can set individual solenoid valves to go "on" or "off" with intervals ranging from 0.01 seconds to 99.9 seconds).

So you can imagine what a nightmare it would be to generate a menu page with dozens of individual Serial.print calls to build up each little piece.

Instead, a simple sprintf mask does the whole thing in one shot.

Here is one function from one of my programs. The program has DOZENS of pieces like this:

// menu: set serial baud rate
void set_baud_rate(void)
{
        byte x;
        const char *message[2] = {
                PSTR("     SETUP MODE     "),
                PSTR("SET SERIAL BAUD RATE")
        };
        lcd_print(message);
        update_params(); // refresh params from EEPROM
        cls(); // clear terminal screen
        const char *menu[5] = {
                PSTR(
                        "\r\n  Four Channel Independent Controller - Serial baud rate\r\n"
                        "\r\n  Current serial rate is %ld baud."
                ),
                PSTR(
                        "\r\n"
                        "\r\n"
                        "  Please select an option:\r\n"
                        "\r\n"
                ),
                PSTR("    (%d)  %6ld Baud\r\n\r\n"),
                PSTR(
                        "    (enter) Return to main menu\r\n"
                        "\r\n"
                        "    Option: "
                ),
                PSTR(
                        "\r    Setting serial rate to %ld baud\r\n"
                        "\r\n    You must reconfigure your terminal baud rate now! "
                )
        };
        sprintf_P(buffer, menu[0], baud_rate);
        Serial.print(buffer);
        sprintf_P(buffer, menu[1]);
        Serial.print(buffer);

        // print available baud rates
        for(x = 0; x < (sizeof(rates) / sizeof(unsigned long int)); x++) {
                sprintf_P(buffer, menu[2], (x + 1), rates[x]);
                Serial.print(buffer);
        }

        sprintf_P(buffer, menu[3]);
        Serial.print(buffer);
        param = get_user_input();

        switch((int) param) {
        case 0:
                do_menu();
                break;
        default:
                if((int)(param) > (sizeof(rates) / sizeof(unsigned long int))) {
                        unrecognized_cmd();

                } else {
                        sprintf_P(buffer, menu[4], rates[(int)(param-1)]);
                        Serial.print(buffer);
                        _delay_ms(5000);
                        EEPROM.writeWord(baud,(int)(param-1));
                        _delay_ms(100); // let EEPROM write finish
                        asm volatile(" jmp 0");  // restart program
                }

                break;
        }
}

Can you imagine the mess I would have if I used dozens of individual Serial.print calls to build up all that text? And this particular menu doesn't even use floating point numbers! :slight_smile: