Problems Writing a Float to Serial Port

Hi all!

I am reading data from a humidity sensor via the I2C interface of an Arduino Uno. I am getting the data as a u16 which works fine. Now I want to convert that u16 raw data to the actual humidity. This would look something like this:

let humidity_raw: u16 = 0x3fff; // that would be 100 % rel. humidity
let humidity: f32 = (humidity_raw as f32) * 100.0 / 16383.0;

Now I have problems writing the humidity f32 value to the serial port. I am currently using the ufmt crate which does not support formatting of floats:

ufmt::uwriteln!(&mut serial, "humidity: {}", humidity).void_unwrap();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `uDisplay` is not implemented for `f32`

Is there an alternative for the ufmt crate? I also tried to get the whole part and the fractional part from the humidity value separately but I did not come up with a trivial solution.

dtoa might be what you're looking for if you want efficiency, but core::fmt has support for f32 built in too.

1 Like

Is this the way I have to do it then?

fmt::write(&mut serial, format_args!("humidity = {} %\n", humidity));
               ^^^^^^^^^^^ the trait `core::fmt::Write` is not implemented for `Usart0<MHz16, Floating>`

From there I have no idea what to do :confused:
Do I have to implement the traits?

I also took a look at dtoa but unfortunately it uses std which is not available to me.

Just like println!() for printing formatted text to stdout, you can use writeln!(&mut serial, "humidity = {} %", humidity) to write to the serial port.

This requires bringing core::fmt::Write into scope and assumes your serial implements it.

But it looks like the serial port does not implement fmt::Write, based on OP's last error message.

It's probably only io::Write, in which case OP could still write an adapter that impls fmt::Write in terms of io::Write.

You can't use std::io::Write in a #[no_std] program.

I'm guessing the Usart0 peripheral actually implements embedded_hal::blocking::serial::Write, so you'll need to write the message to a temporary buffer then write it to the serial device from there.

1 Like

Okay, I partly solved my problem with the ufmt_float crate which let's me do pre-formatting in the form of

let myfloat: f32 = 1.2345;
let myfloat_write = uFmt_f32::Three(myfloat);

ufmt::uwriteln!(&mut serial, "{}", myfloat_write);
--> prints 1.234

This works!

But! Now I've got another problem. Converting values from u16 to f32 always gives 0.0. Here is the code I've got:

let myint: u16 = 0x1a86;
let myfloat = myint as f32;
let myfloat_write = uFmt_f32::Three(myfloat);

ufmt::uwriteln!(&mut serial, "{}", myfloat_write).void_unwrap();
--> prints 0.000

What is going on here? Is the compiler playing tricks with me?

Also this nullification seems kind of arbitrary. Compare the two snippets:

    let myint: u16 = 0x1a86;
    let myfloat: f64 = myint as f64;
    let myfloat_write = uFmt_f64::Three(myfloat);
    ufmt::uwriteln!(&mut serial, "myfloat_write: {}", myfloat_write).void_unwrap();
--> prints 6790.000

    let humidity_raw: u16 = 0x000a;
    let hum_float = humidity_raw as f32;
    let hum_write = uFmt_f32::Three(hum_float);
    ufmt::uwriteln!(&mut serial, "hum_write f32: {}", hum_write).void_unwrap();
--> prints 10.000

but changing the variables to f64 in the lower block results in zeros everywhere:

    let myint: u16 = 0x1a86;
    let myfloat: f64 = myint as f64;
    let myfloat_write = uFmt_f64::Three(myfloat);
    ufmt::uwriteln!(&mut serial, "myfloat_write: {}", myfloat_write).void_unwrap();
--> prints 0.000

    let humidity_raw: u16 = 0x000a;
    let hum_float = humidity_raw as f64;
    let hum_write = uFmt_f64::Three(hum_float);
    ufmt::uwriteln!(&mut serial, "hum_write f64: {}", hum_write).void_unwrap();
--> prints 0.000

:tired_face:

What am I missing?