Float to Byte

Hi, i have been working on making a VU meter on Arduino using the multifunctional shield. the analog read from the potentiometer on the shield is the voltage i am reading and when a certain voltage is reached an led turns on. i want to use the 4 digit 7 segment display to cast the voltage but i have a float value and i know i need a byte instead. i am only able to get the segment display to show voltage such as '3' not '3.17' for example.

any one help me converting this float to byte, to cast on the 7 segment. i want to achieve this using no libraries whatsoever.

const int LED      = 13;
const int LED1     = 12;
const int LED2     = 11;
const int LED3     = 10;

const int Button  = A1;

int ButtonState = 0;   // push status to value
int Value       = 0;   // take value from button state
int On          = 0;   // take power status



#define LATCH_DIO 4
#define CLK_DIO  7
#define DATA_DIO  8

const byte SEGMENT_MAP[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};
const byte SEGMENT_SELECT[] = {0xF1,0xF2,0xF4,0xF8};


void setup() {
  Serial.begin(9600);
  pinMode(LED,  OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(Button, INPUT_PULLUP);

 pinMode(LATCH_DIO,OUTPUT);
 pinMode(CLK_DIO,OUTPUT);
 pinMode(DATA_DIO,OUTPUT);
}


void WriteNumberToSegment(byte Segment, byte ValueX)
{
  digitalWrite(LATCH_DIO,LOW);
  shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_MAP[ValueX]);
  shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_SELECT[Segment] );
  digitalWrite(LATCH_DIO,HIGH);
}

void WriteNumber(int Number)
{
 WriteNumberToSegment(0 , Number / 1000);
 WriteNumberToSegment(1 , (Number / 100) % 10);
 WriteNumberToSegment(2 , (Number / 10) % 10);
 WriteNumberToSegment(3 , Number % 10);
}



void loop()
{
Value = digitalRead(Button);  //read the button and copy the state to Value
    
    if (Value == HIGH && On == LOW) //if value from button state is high and the power is off
    {
      ButtonState = 1 - ButtonState;    //determines if power stays on or off
      delay(100);
    }

  On = Value;                  //light and value have same state

    if (ButtonState == HIGH)        //if the button is pressed
    {
          digitalWrite(LED,  HIGH);      
          digitalWrite(LED1, HIGH);      
          digitalWrite(LED2, HIGH);      
          digitalWrite(LED3, HIGH);      
    }
    else
    {
      {
       

float voltage = analogRead(A0) * 0.00488758553;


        Serial.println(voltage);

          if (voltage > 4)
           {
          digitalWrite(LED, LOW);
           }
          else
           {
          digitalWrite(LED, HIGH);
           }
          if (voltage >  3)
           {
          digitalWrite(LED1, LOW);
           }
          else
           {
          digitalWrite(LED1, HIGH);
           }
           if (voltage > 2)
           {
           digitalWrite(LED2, LOW);
           }
           else
           {
           digitalWrite(LED2, HIGH);
           }
          if (voltage > 1)
           {
           digitalWrite(LED3, LOW);
           }
          else
            {
            digitalWrite(LED3, HIGH);
            }
    WriteNumber(voltage);
      }
    }
}

Try

WriteNumber((int)voltage);

or

WriteNumber((byte)voltage);

but WriteNumber expects an int:

void WriteNumber(int Number)

More info here

When casting from a float to an int, the value is truncated not rounded. So both (int) 3.2 and (int) 3.7 are 3.

If you want rounded number do

int rounded_integer = (int)(float_number+0.5);

any one help me converting this float to byte

You can't. If the function takes an integral type, you can not possibly expect it to display a value with digits after the non-existent decimal point.

You COULD multiply the value in the float by 100, and pretend that the 3.17 value, which becomes 317.xxx, and is displayed as 317, really means 3.17

You can convert numbers to ascii text and print that text to the display, look at formatting functions like sprintf.

char buffer[20];
int i = 123;
float f = 123.456;
sprintf(buffer, "%i - %f", i, f);
Serial.println(buffer);

EDIT: Sorry, the code is probably useless for you.

Hey @lesept i want the 4 digit 7 segment to display the voltage reading including decimal not rounded, if at 3.17 i want it to display 3.17 not 3

Any help on that?

I don’t understand your problem.

  • Is it a display problem, i.e. the 4/7 display does not display 3.14 when you ask it to do that ?
  • Or is it a code problem, i.e. you don’t know how to round a float such as 1.23456789 to 2 numbers after the comma such as 1.23 ?

Are you sure that the number will never exceed 9.99? Is it always positive?

What I’d do is the following:

  • First ensure the integer part of your number is less than 9999. If not, you can’t display it. End.
  • If the number is greater or equal to 1000: print its integer part. (EDIT: that’s what your current version of WriteNumber does)
  • If the number is greater or equal to 100, print its integer part and the ‘.’
  • If the number is greater or equal to 10: first print its integer part, then the dot, then the first number of its decimal part.
  • Otherwise: print its integer part, print the dot, print the 2 first numbers of its decimal part.

To compute the first number of the decimal part, use

byte first = (float_number - (int)float_number * 1.0)*10;

To compute the first 2 numbers of the decimal part, use

byte first = (float_number - (int)float_number * 1.0)*100;

Does it help?

My problem is i have the voltage value as a float and to cast it to the 4/7 display i have used writenumber(voltage) but voltage is a float so how do i cast the exact decimal of voltage not just the individual integer.

For example if the voltage is 3.14 i want the segment to display 3.14 but i have found the float can only display 3 therefore i need to convert float to byte
Im confused

My problem is i have the voltage value as a float and to cast it to the 4/7 display i have used writenumber(voltage) but voltage is a float so how do i cast the exact decimal of voltage not just the individual integer.

Write ANOTHER function that takes a float and does something meaningful with it. You are NOT limited to using only functions that someone else wrote.

Your present writeNumber function takes a integer number and prints the thousands at the first space of the display, then prints the hundreds at the second place, the tens at the third place and the units at the last place. As you send a float, it converts it to an integer.

If you send 3.14, it keeps 3 and displays 0003.

Try to understand how that function works, it mainly uses 4 times the WriteNumberToSegment function which prints a one digit number at a given place of the display.

Then try to understand what I suggested earlier (it may well be badly explained, sorry for my bad english) and use that WriteNumberToSegment function accordingly.

You'll have to write a function that displays the dot, I don't think you can do it with your current code.

ok so what i have managed to keep the whole code the same but where i had WriteNumber(voltage); i had changed it to WriteNumber(voltage*100); which displays 3.16 and 316

how do i activate the second decimal from the left eg 03.16

Please give the reference of your display or post a picture of it, we can't figure out what it is otherwise

@OP

Based on the directives of Post#2, I would go in the following way to design your 4-digit CC-type 7-segment Display Unit to display value: 00.00 - 04.99 (0.00 - 4.99).

1. You have:

float voltage = analogRead(A0)*(5/1023);    // range is: 00.00 - 04.99 with 2-digit precision

2. Due to @PaulS of Post#2, we can declare your voltage as:

unsigned int voltage = (unsigned int [s]int[/s])100*(5/1023.0[s]1023[/s])*analogRead(A0); //range: 0000 - 0499

3. The Display Unit (During uploading, remove jumpers from DPin-0, 1; After uploading, put jumpers back at DPin-0, 1)
musds.png
Figure-1: 4-digit Display Unit

4. The sketch (semi-tested)

byte lupTable[10] = //Look up Table: CC-code Vs Digit: 0 - 9
{0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

byte indexDD[4]; //array to hold indices for decimal digits of hexVoltage
byte ccArray[4]; //array to hold cc-codes for the decimal digits of hexVolatge
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  //float voltage = 5/1023.0*analogRead(A0);
  unsigned int hexVoltage = (unsigned int [s]int[/s])100 * (5 / 1023.0) * analogRead(A0); //0000 - 0499
  //----- find indices for the decimal digits of the hexVolatge
  for (int i = 3; i >= 0; i--) //0000 - 0499 = DD0 DD1 DD2 DD3
  {
    indexDD[i] = hexVoltage % 10;  //indexDD[3] contains indes for DD3
    hexVoltage = hexVoltage / 10;
  }
  //------ obtain cc-codes for the above decimal digits and save in ccArray[4];
  for (int i = 0; i < 4; i++ )
  {
    ccArray[i] = lupTable[indexDD[i]];
  }
  ccArray[1] = ccArray[1] | 0b10000000; //add decimal point with digit-1 (leftmost one is digit-0)

  //---transfer the cc-codes on 7-segment Display Unit of Fig-1
  {
    byte x = 0b00001111;   //PB3 - PB0 are at LH-states
    for (int i = 0; i < 4; i++ )
    {
      PORTD = ccArray[i]; //during uploading disconnect jumpers from Dpin-0, 1
      bitClear(x, i);
      PORTB = x;
      timeDelay1ms();  //user defined 1ms time delay using TC1 for digit synchronization.
    }
  }

  delay(1000); //refresh acquisition at 1-sec interval
}

void timeDelay1ms()
{
  //prepare codes using TC1 with suitable pre-scaler and pre-set value
}

muxDisplay.png

musds.png

Not wise using D0 and D1 as they are used for Serial communications even with jumpers.

Why waste one whole digit for just the decimal point.

larryd:
Not wise using D0 and D1 as they are used for Serial communications even with jumpers.

Why waste one whole digit for just the decimal point.

D0/D1 is the symbol for Digit-0/1 (starting from left) and the meanings are limited only in the current sketch.

The OP has asked for 4-digit display unit; in fact, only 3-digit is enough to display the value 0.00 - 4.99). It is not required to place the Digit-0 (DP0 of Fig-1) physically on the board.

I am not wasting a digit for the point; I have an OR function with the content of DP1 to show the point.

ccArray[1] = ccArray[1] | 0b10000000; //add decimal point with digit-1 (leftmost one is digit-0)

GolamMostafa:
2. Due to @PaulS of Post#2, we can declare your voltage as:

unsigned int voltage = (int)100*(5/1023)*analogRead(A0); //range: 0000 - 0499

Oops

AWOL:
Oops

Oops! The declaration should be like this:

unsigned int voltage = (uint_16)100*(5/1023.0[s]1023[/s])*analogRead(A0); //range: 0000 - 0499

or

unsigned int voltage = (unsigned int)100*(5/1023.0[s]1023[/s])*analogRead(A0); //range: 0000 - 0499
unsigned int voltage = (unsigned int)100*(5/1023)*analogRead(A0); //range: 0000 - 0499

Obviously, you have not tried this out. Please do so and let us know how well it works.

jremington:

unsigned int voltage = (unsigned int)100*(5/1023)*analogRead(A0); //range: 0000 - 0499

Obviously, you have not tried this out. Please do so and let us know how well it works.

Yes! I did not test it; test shows a 'nor working code'. I thought that I was asking for the 'unsigned integer part' of the 'float number', and accordingly I should have my RHS expression in the format of 'floating point number' having had a .(point after 1023 (1023.0); but, I was just not enough respectful to Forum Members who might read/scrutinize my post. The following declaration works.

unsigned int voltage = (unsigned int)100*(5/1023.0)*analogRead(A0); //range: 0000 - 0499

SM Output with A0 = 3.3V and unsigned int voltage = (unsigned int)100*(5/1023)*analogRead(A0); //range: 0000 - 0499[/code]
sm-2.png

SM Output with A0 = 3.3V and unsigned int voltage = (unsigned int)100*(5/1023.0)*analogRead(A0); //range: 0000 - 0499[/code]
sm-3.png

K+ for both @AWOL and @jremington .

sm-2.png

sm-3.png

BTW the proper divisor when using the ADC is 1024, not 1023.

And the ADC reference voltage is extremely unlikely to be 5.0V

jremington:
BTW the proper divisor when using the ADC is 1024, not 1023.

And the ADC reference voltage is extremely unlikely to be 5.0V

  1. There is an on-going controversy as to the use of 1023 or 1024; a group of Forum Members correctly thinks that there are 1024 steps ( 0 to 1023); so, the factor should be 1024. Another group ( to whom I belong) argues that when FS voltage is applied to the ADC, the converted value will never exceed 1023; so, the factor should be 1023. (Error is: 1/1024*100 = 0.10%.)

  2. analogReference(DEFAULT) should be overridden in the formula by the measured value of Vcc. (In my UNO Error is: 5-4.98/5 *100 = 2%.)