How to parse a floating-point value onto a 7-segment display?

Hello.

I have an Arduino Due connected to both a DS1631 12-bit temperature sensor(I2C) and a MAX7219 8-Digit 7-segment LED Display Driver(SPI) and I want to collect measures from the DS1631 which is done by a library returning a type float value.

I have searched online without being able to find a solution to this and I really can't figure out this my self, I want at least 1 decimal value displayed but all I have been able to come up with is this which only displays a rounded value between 00 and 99.

Notes:
I simply collect the supposedly float value into a int and write the first and second hole numbers out to the display, the weird IF statement in the loop() is there because the I2C wires are long and runs next to a lot of motors which causes the I2C bus to fail in some way(simply my unsubstantiated theory) resulting in the display getting stuck reading "00".

/*  Desktop LED light temperature monitor sketch.
 *    This code uses a DS1631 12-bit output temperature sensor mounted to the heatsink 
 *    of a DIY LED desktop light to monitor it's temperature and display that temperature
 *    in °C on a 8 digit 7-segment display.
 *    
 *    DS1631 wiring:
 *    Connect DS1631 SDA pin to Arduino Due pin D20 (SDA)
 *    Connect DS1631 SCL pin to Arduino Due pin D21 (SCL)
 *    
 *    MAX7219 wiring:
 *    Connect MAX7219 DIN pin to Arduino Due pin D4
 *    Connect MAX7219 CS/LOAD pin to Arduino Due pin D3
 *    Connect MAX7219 CLK pin to Arduino Due pin D2
*/

#include <Wire.h>
#include <DS1631.h>
#include <LedControl.h>

#define DIN 4
#define CS 3
#define CLK 2


DS1631 sensor(0);  // initialize DS1631 object, with bus address equal to pins A2,A1,A0 all tied to ground.

LedControl _7segment = LedControl(DIN, CLK, CS, 1);


void setup()
{
  Wire.begin();  // open the I2C bus.
  
  sensor.writeConfig(0b00001101);  // Sets configuration of DS1631 as 12-bit, 1-shot mode.
  
  _7segment.shutdown(0, false);  // turn on display.
  _7segment.setIntensity(0, 5); // 5(max 15) = brightest.
  
  _7segment.setDigit(0, 0, 0, false);  // Initialize 1st digit from the right.
  _7segment.setDigit(0, 1, 0, false);  // Initialize 2nd digit from the right.
  
  /* these digits are currently not used:
  _7segment.setDigit(0, 2, 0, false);
  _7segment.setDigit(0, 3, 0, false);
  _7segment.setDigit(0, 4, 0, true);
  _7segment.setDigit(0, 5, 0, false);
  _7segment.setDigit(0, 6, 0, false);
  _7segment.setDigit(0, 7, 0, false);
  */
    
}

void loop()
{
  
  int temp = sensor.readTempOneShot();  // Start a conversion and store the resulting data.

  
  // This IF statement solves the problem of the display getting stuck showing "00".
  if (temp)
  {
    _7segment.setDigit(0, 0, (temp % 10), false); // write the lowest decimal digit to the 7-segments most-right digit.
    _7segment.setDigit(0, 1, (temp / 10), false); // write the highest decimal digit to the next 7-segment digit.
  }

  else
  {
   Wire.endTransmission(0); // Close down the I2C bus.
   Wire.begin();            // re-open the I2C bus.
  }
  
}

But I am not happy with this, I want to display the value of °C to be displayed with at least 1 decimal value, preferably 2(I'm not sure I want 2 decimals but I would like to see what it looks like on the display, but 1 decimal is fine).

Does anyone know of a way to accomplish this?

I have been thinking about this for a week now and I simply can't manage to imagine a solution.

I really appreciate any and all inputs on this.

Arduino is a nightmare for creating formatted numbers.
Let's face it. Right justified numbers always look better.

Use dtostrf() function to create the required width and precision. Print the result to Serial so you can be happy.

Then it is simply a question of displaying the ascii buffer on your MAX7219.
From LedControl library documentation

/* Display a character on a 7-Segment display.
 * Params:
 *   addr  address of the display
 *   digit the position of the character on the display (0..7)
 *   value the character to be displayed.
 *   dp    sets the decimal point.  
*/ 
void setChar(int addr, int digit, char value, boolean dp);

Note that you can use dtostrf() to format integers too. But some of the ARM implementations don't work very well. (I think it is one of the STM32 cores)

David.

david_prentice:
Use dtostrf() function to create the required width and precision. Print the result to Serial so you can be happy.

Then it is simply a question of displaying the ascii buffer on your MAX7219.

You sure that's not overkill?

This appears to be an XY problem. http://xyproblem.info/

I see this line:

  int temp = sensor.readTempOneShot();  // Start a conversion and store the resulting data.

Try replacing it with:

  int temp = ((sensor.readTempOneShotInt() >> 4) & 0x0FFF); // raw temperature in sixteenths of a degree
  if (temp >= 0x0800) temp -= 0x1000; // adjustment to handle negative temperatures
  temp = (temp * 10) / 16; // convert to tenths of a degree

Now, you have the temperature to the nearest tenth of a degree, and all without floats.
For instance, if the temperature is 19.5 degrees, the value of temp will be 195.
If the temperature is 20 degrees, the value of temp will be 200. And so forth.
Display the temperature as a three-digit integer, and insert a decimal point immediately after the second digit.

Try reading up on the readTempOneShotInt(); function.
Read the comments in this file: DS1631/DS1631.h at master · millerlp/DS1631 · GitHub
That's how I figured out this trick for you.

Of course it is overkill. So what?

You read a sensor periodically. Processing the f-p maths is not significant.
A Uno has got 31.5kB Flash available for your app. You can afford the flash used for f-p.

Yes, you can do your formatting and display with integer maths.
You can read your temperature sensor with integer maths.

The important tip is: if you already use f-p, you don't add any extra flash for the convenience.
If you are running on a Tiny85, you need to be frugal with flash.

David.

david_prentice:
Of course it is overkill. So what?

You read a sensor periodically. Processing the f-p maths is not significant.
A Uno has got 31.5kB Flash available for your app. You can afford the flash used for f-p.

I was thinking more in terms of replacing an "unsolved problem" (displaying a floating-point number) with a "solved problem" (displaying an integer).

We know that the OP has potentially an eight-digit display on which he (she?) is already able to display a two-digit integer. So now all OP has to do is press a third digit into service and light up a decimal point.
Oh yeah, that and replace this
(temp / 10)which gives the tens digit of a two-digit integer
with this
((temp % 100) / 10)which gives the tens digit of an integer with at least two digits.

I will have to write this all down and read it through a few times to be able to think it through and see what you have provided me with but it sure looks like I have two possible solutions.

Thank you very much, I will return and post the solution I extract from your answers.

I really like and really hate programming...

I had much the same problem, and overlooked the extremely obvious solution above to placing a decimal point.

lc.setDigit(0,2,0,true); //setChar(int addr, int digit, char value, boolean dp); true or false for Decimal Point!

This fragment shows the digit 0 with a dot after it.

trullt:
and overlooked the extremely obvious solution above to placing a decimal point.

you overlooked two additional things:

lc.print(123.45);