[SOLVED] Convert mV to volts with a single decimal WITHOUT float

Im trying to figure out a way to use a voltage divider to read actual battery voltage (8-18v) and then take the ADC in mV and put a decimal place on it with out float.

I know that I can map it out to mV, but I dont know how to place a decimal on it so 1280mV is converted to 12.8v at which point I can send that value in text to a display (so its not a numerical value anymore, just ASCII text). I can get the voltage in and know how to write the final value to the display, but placing a decimal without float seems to be a lot harder than I thought it would be.

Or not?

1280mV is converted to 12.8v

So the voltage divider is 10 to 1?

Here is a way to do what you want. Using integer division and the modulo function and 2 ways to print the result. One with just the print method and one with sprintf function.

int voltageMv = 1280;

void setup()
{
   Serial.begin(115200);
   int volts = voltageMv / 1000;
   int mV = voltageMv % 1000;
   Serial.print(volts);
   Serial.print('.');
   Serial.print(mV);
   Serial.println(" Volts");
   // or
   char buffer[10];
   sprintf(buffer, "%d.%d Volts", volts, mV);
   Serial.println(buffer);
}

void loop()
{
}

groundFungus:
1280mV is 1.28 Volts in my universe.

Here is a way to do what you want. Using integer division and the modulo function and 2 ways to print the result.

int voltageMv = 1280;

void setup()
{
  Serial.begin(115200);
  int volts = voltageMv / 1000;
  int mV = voltageMv % 1000;
  Serial.print(volts);
  Serial.print('.');
  Serial.print(mV);
  Serial.println(" Volts");
  // or
  char buffer[10];
  sprintf(buffer, "%d.%d Volts", volts, mV);
  Serial.println(buffer);
}

void loop()
{
}

Yes I screwed up explaining the first part, point is I need to use a 0-5v ADC and map it to 8-18v, no big deal.

I am using an HMI that will NOT accept floats, only integers so I need to send the value as text in a single value with everything done prior so all the HMI recieves is "12.8", "14.1" or "8.3" (not those only, its a map Im using, but I hope you get the picture)in text. Now I know how to send the values to the HMI in text, but I need to get the value there.

I suppose the better question to ask is, "how do I map ADC to decimals without float"? I dont care about writing it, just the proper function to do it, if there is one.

I also do not want to use floats on the Arduino as it slows it way down, I already figured that out so Im considering it off limits.

@groundFungus

You missed to take care of the leading zeros of the fractional part. :wink:

int voltageMv = 1001;

void setup() {
  Serial.begin(115200);
  int volts = voltageMv / 1000;
  int mV = voltageMv % 1000;
  Serial.print(volts);
  Serial.print('.');
  if (mv < 100) {
    Serial.write('0');
    if (mv < 10) {
      Serial.write('0');
    }
  }
  Serial.print(mV);
  Serial.println(" Volts");
  // or
  char buffer[10];
  sprintf(buffer, "%d.%03d Volts", volts, mV);
  Serial.println(buffer);
}

void loop() {}

Whandall, thanks. I just tested my code with 12085mV and you are right, hmmm, got 12.85. Got to think about that.

edit; Thanks for the solution.

This is what Im shooting for

map (Voltage, 0, 1023, 80, 180)

And then stuff a decimal on it to make it 8.0, 18.0

Dont need any help with Serial, just how to add a decimal to the value of 80-180 at which point I can send it as a text value as opposed to a numerical value that the HMI will not accept.

In that simple case we are nearly back to the original (no leading zeors in the fractional part).

int voltageMv = 180;

void setup() {
  Serial.begin(115200);
  int volts = voltageMv / 10;
  int mV100 = voltageMv % 10;
  Serial.print(volts);
  Serial.print('.');
  Serial.print(mV100);
  Serial.println(" Volts");
  // or
  char buffer[10];
  sprintf(buffer, "%d.%d Volts", volts, mV100);
  Serial.println(buffer);
}

void loop() {}

Are you shure a reading of zero equals 8V?

You can not get that with a simple divider.

Whandall:
In that simple case we are nearly back to the original (no leading zeors in the fractional part).

int voltageMv = 180;

void setup() {
  Serial.begin(115200);
  int volts = voltageMv / 10;
  int mV100 = voltageMv % 10;
  Serial.print(volts);
  Serial.print('.');
  Serial.print(mV100);
  Serial.println(" Volts");
  // or
  char buffer[10];
  sprintf(buffer, "%d.%d Volts", volts, mV100);
  Serial.println(buffer);
}

void loop() {}




Are you shure a reading of zero equals 8V?

You can not get that with a simple divider.

Im still prototyping this so I may not hit those exact numbers but I'm gonna try to get close, lets disregard the accuracy of the map and just stick with the function though as I could map it from 200-900 or 1000-10000, I just want to get a decimal placed to add a tenth.

Do I need to serial print in to do that? If so, then thats what I have to do, I just wanna make sure that there isnt a function other than that to add a decimal.

I had the same problem with lcd which accepts only int numbers… I managed to solve it easy with the dtostrf function which converts float numbers to char array with decimal point…

float value;

value = analogRead(A0);

char buffer[10];

dtostrf(value, 3, 1, buffer); // converts a float to an char array with one decimal point

Serial.print(buffer); // prints out char array

You have explanation here http://www.hobbytronics.co.uk/arduino-float-vars, hope it helps

VTI_16V:
I had the same problem with lcd which accepts only int numbers… I managed to solve it easy with the dtostrf function which converts float numbers to char array with decimal point…

float value;

value = analogRead(A0);

char buffer[10];

dtostrf(value, 3, 1, buffer); // converts a float to an char array with one decimal point

Serial.print(buffer); // prints out char array

You have explanation here http://www.hobbytronics.co.uk/arduino-float-vars, hope it helps

I may have to do this even though I dont want to use floats and will be what I do unless there is a better option.

For the others that have contributed

I need to place the value into this format to get it on the display

Serial.print(volt.txt=);
Serial.print("14.2");
Serial.write(0xff);
Serial.write(0xff);
Serial.write(0xff);

The 14.2 part is what I am worried about getting to. I could print it as “142” but the decimal is better.

I hope there is a clean solution without floats though.

mattyb0:
I need to place the value into this format to get it on the display

Serial.print(volt.txt=);

Serial.print("14.2");
Serial.write(0xff);
Serial.write(0xff);
Serial.write(0xff);

No. That does not even compile.

int voltageMv = 180;

void setup() {
  Serial.begin(115200);
  int volts = voltageMv / 10;
  int mV100 = voltageMv % 10;
  Serial.print(F("volt.txt=\""));
  Serial.print(volts);
  Serial.write('.');
  Serial.print(mV100);
  Serial.write('"');
  Serial.write(0xff);
  Serial.write(0xff);
  Serial.write(0xff);
}
void loop() {}

mattyb0:
I may have to do this even though I dont want to use floats and will be what I do unless there is a better option.

For the others that have contributed

I need to place the value into this format to get it on the display

Serial.print(volt.txt=);

Serial.print("14.2");
Serial.write(0xff);
Serial.write(0xff);
Serial.write(0xff);




The 14.2 part is what I am worried about getting to. I *could* print it as "142" but the decimal is better. 

I hope there is a clean solution without floats though.

I don't see a reason for not using float numbers... You are not using them within display, you are using them with arduino, then you convert them in char array which you send to display... Is that a nextion display that you are using? If so, this will works fine what i wrote you, but do as you please...

VTI_16V:
I don’t see a reason for not using float numbers

Because they :

  1. are Slow
  2. are Inherently inaccurate
  3. bloat code

You will almost always be better off on a micro to avoid them.

Got it figured out. Whandall was damn close to what needed to be done.

int VoltagePin = A0;  
int VoltageValue;  
int Voltage = 0;
void setup() {  
  
  Serial.begin(9600);   
  Serial.print("baud=115200"); 
  Serial.write(0xff);  
  Serial.write(0xff);
  Serial.write(0xff);
  Serial.end();  
  Serial.begin(115200);  
} 

void loop() {  
  delay(20);  
  VoltageValue = analogRead(VoltagePin);  
  Voltage = map (VoltageValue, 0, 1023, 80, 180); 
  

    Serial.print("t0.txt=");  
    Serial.print("\"");  
    Serial.print(Voltage/10);  
    Serial.print(".");
    Serial.print(Voltage%10);
    Serial.print("\"");  
    Serial.write(0xff);  
    Serial.write(0xff);
    Serial.write(0xff);

}

No float, no bloat and doesn't make our little uC tear its brains out trying to rationalize an irrational number.

Thanks guys!

EDIT: Since its obvious to some that a voltage divider wont accurately fall withing the mapped range, I will do what I can to get close and constrain it to be accurate within the range.

By using string constants without the F() macro, you are wasting memory.

All these constants will end up in RAM and flash without the F() macro.

I see no difference between your version and mine, besides the difference in RAM usage.

Whandall:
By using string constants without the F() macro, you are wasting memory.

All these constants will end up in RAM and flash without the F() macro.

I see no difference between your version and mine, besides the difference in RAM usage.

So by doing this

int VoltagePin = A0; 
int VoltageValue; 
int Voltage = 0;
void setup() { 
 
  Serial.begin(9600);   
  Serial.print("baud=115200");
  Serial.write(0xff); 
  Serial.write(0xff);
  Serial.write(0xff);
  Serial.end(); 
  Serial.begin(115200); 
}

void loop() { 
  delay(20); 
  VoltageValue = analogRead(VoltagePin); 
  Voltage = map (VoltageValue, 0, 1023, 80, 180);
 

    Serial.print(F("t0.txt=")); 
    Serial.print("\""); 
    Serial.print(Voltage/10); 
    Serial.print(".");
    Serial.print(Voltage%10);
    Serial.print("\""); 
    Serial.write(0xff); 
    Serial.write(0xff);
    Serial.write(0xff);

}

I improve RAM usage?

Yes, that keeps the constant strings from eating up your very limited RAM memory. You just saves 7 bytes of RAM. Don't seem like much now, but in a big program it can make or break you.

Serial.print(F("baud=115200"));

This one would save 12 more bytes if you did it too.

  Serial.print("baud=115200"); // 12 byte
  Serial.print("\"");   // 2 byte
  Serial.print(".");    // 2 byte
  Serial.print("\"");   // 2 byte

Still 18 bytes wasted (single chars could be emitted with write() anyway).

Is there any reason not to use that macro for all Serial.print operations? Im trying to read up on this, but seems like youd want to use this as often as possible.

mattyb0:
Is there any reason not to use that macro for all Serial.print operations? Im trying to read up on this, but seems like youd want to use this as often as possible.

You want to use it on all print operations involving constant strings. It doesn't do anything but create an error for a variable. Think about that one for a minute and see if you can figure why you can't keep variables out of RAM.