[SOLVED] Measure voltage on the same battery powering the arduino.

Hi everybody
new question: is it possible to measure, through a voltage divider, the voltage on the same battery powering the arduino pro mini?

I tried something like that ( you can find the schematics in the attachments)

...

double VPinRead;
double Vbatt; double Vbatt_perc; double VbattMin = 3200, VbattMax = 4200; // [mV]
double R1 = 23.6; double R2 = 1462000;


void setup() {
  Serial.begin(9600);

...

  pinMode(batPin,INPUT);

...

}


void loop() {

...

  VPinRead = map(analogRead(batPin),0,1023,VbattMin,VbattMax); //tra Vmin e Vnax mV massima tensione del pin 
  Vbatt = (VPinRead/1000)*((R2/(R1+R2))) ; // diviso 1000 per riportarlo in V da mV. 
  Vbatt_perc = map(Vbatt*1000,VbattMin,VbattMax,0,100);
  Serial.print(Vbatt);
 
...

}

Weird thing is I always get the Vmax as result for Vbatt, even if I power the Pro Mini with the USB and I update Vmax and Vmin values consistently (5000 mV and 0mV).

OP’s schematic:
b328c46c688848e47c850f332c85bb5f5a191172.png

Well, of course you get the same reading all the time, as the default reference of your ADC is Vcc.

Switch to the internal fixed 1V reference instead, and change the resistors of the voltage divider to get 4.3V (the highest a regular LiPo battery puts out when fully charged) to produce just under 1V on the analog pin you want to use to read the voltage.

Ok thank you, but two questions: my knoledge on the matter is quite limited

  1. ADC ? What's it standing for?
  2. Can you explain your solution like you were explaining it to a kindergrden kid?

How to measure the voltage on the battery powering the Arduino is described in this Thread.

The trick is to use the internal voltage reference.

...R

piepolitb:
Ok thank you, but two questions: my knoledge on the matter is quite limited

  1. ADC ? What’s it standing for?

ADC = Analog Digital Converter.

  1. Can you explain your solution like you were explaining it to a kindergrden kid?

Kindergarten kids don’t know what electricity is, what voltage are, etc. So sorry, that’s way too much of explaining to do. At the very least you need high school physics level.

Actually, I don’t even understand your schematic and your code is too much cut to make much sense of it. There’s a voltage divider of 1M / 23.8 Ohm, giving you a near-Vcc voltage on A1, and there’s another that’s done with a pot.

For your voltage measurement get a voltage divider of say 100k + 22k, 100k to Vcc, 22k to GND, mid point to an analog pin. Rather high values but you don’t want to drain your battery through it. This gives you 0.77V on any of your analog pins when your battery is full (4.3V) and about 0.49V when empty (2.7V).

Then enable the internal reference (do look up the documentation of how the ADC works!) and you can measure this.

Also see:
https://code.google.com/archive/p/tinkerit/wikis/SecretVoltmeter.wiki

//http://code.google.com/p/tinkerit/wiki/SecretVoltmeter


long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println( readVcc(), DEC );
  delay(1000);
}

The hardcore way of reading an analog input :slight_smile:

wvmarle:
ADC = Analog Digital Converter.

Kindergarten kids don’t know what electricity is, what voltage are, etc. So sorry, that’s way too much of explaining to do. At the very least you need high school physics level.

Actually, I don’t even understand your schematic and your code is too much cut to make much sense of it. There’s a voltage divider of 1M / 23.8 Ohm, giving you a near-Vcc voltage on A1, and there’s another that’s done with a pot.

For your voltage measurement get a voltage divider of say 100k + 22k, 100k to Vcc, 22k to GND, mid point to an analog pin. Rather high values but you don’t want to drain your battery through it. This gives you 0.77V on any of your analog pins when your battery is full (4.3V) and about 0.49V when empty (2.7V).

Then enable the internal reference (do look up the documentation of how the ADC works!) and you can measure this.

I was kidding of course: my studies on the matter is a little bit rusty. Plus english is not my Language, so I get some difficulties with achronyms.

Here is my full code: there are oter things going on, like the pot: it has nothing to do with the voltage divider.

#include <RH_ASK.h>
#include <TM1637Display.h>

// iniszializzazioni pin
#define potPin A0
#define batPin A1                                                                                                                                                                                                                                                                                                                                                                                                        
#define btnPin 2
#define txPin 13
#define pinCLK 8
#define pinDIO 9

// inizializzazione librerie RadioHead per trasmissione
RH_ASK tx(2000,1,txPin,1,true);

//inizializzazione libreria TM1637Display del display
TM1637Display digitalDisplay(pinCLK, pinDIO);
#define SEG_A   0b00000001   //      A   
#define SEG_B   0b00000010  //     ---
#define SEG_C   0b00000100  //  F |   | B
#define SEG_D   0b00001000  //     -G-
#define SEG_E   0b00010000  //  E |   | C
#define SEG_F   0b00100000  //     ---
#define SEG_G   0b01000000  //      D
//-- per scrittura “OFF” 
// ogni dgit dell’array displayOFF (quindi quale segmento è acceso di ogni digit) è definito con OR-bitwise “|” dei segmenti interessati
const uint8_t displayOFF[] = {
  0b00000000,
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,   // O
  SEG_A | SEG_E | SEG_F | SEG_G,                   // F
  SEG_A | SEG_E | SEG_F | SEG_G,                   // F
  };

// inizializzazione comandi throttle
int motJuice = 0; int motJuice_perc;
int btnState; String msgStr;

// inizializzazioni partitore di tensione
double VPinRead;
double Vbatt; double Vbatt_perc; double VbattMin = 3200, VbattMax = 4200; // [mV]
double R1 = 23.6; double R2 = 1462000;


void setup() {
  Serial.begin(9600);
  pinMode(btnPin,INPUT);
  pinMode(batPin,INPUT);
  pinMode(potPin,INPUT);

  digitalDisplay.setBrightness(0x0f);  //! Sets the brightness – 0x0f =7, massima lumx;

  // SETTAGGI DI TRASMISSIONE
  if (!tx.init()){ Serial.println("init failed");}
}

void loop() {
  // ----------------------- COMANDI THROTTLE ---------------------------//
  btnState = digitalRead(btnPin);
  
  if(btnState == LOW) // se tasto non premuto comando da throttle
  {
    motJuice = analogRead(potPin);
    motJuice = map(motJuice,0,1023,2000,1100);
    
    motJuice_perc = map(motJuice,1250,2000,0,100);
    if(motJuice_perc < 5){motJuice_perc = 0;} // filtro 
    
    digitalDisplay.showNumberDec( motJuice_perc , false); // stampa percentuale throttle
    
  } else if(motJuice_perc > 0 && btnState == HIGH) // se tasto premuto, comando -> motore spento
  {
    motJuice = 0;
    digitalDisplay.setSegments(displayOFF);
  }
  // --------------------------------------------------------------------//
  //
  // ------------------- MISURA BATTERIA con PARTITORE di TENSIONE ---------------------//
  VPinRead = map(analogRead(batPin),0,1023,VbattMin,VbattMax); //tra 0 e 5000 mV massima tensione del pin 
                                                    // in mV per avere + cifre significative
  Vbatt = (VPinRead/1000)*((R2/(R1+R2))) ; // diviso 1000 per riportarlo in V da mV. 
                                                     // * 3.85/3.91 - fattore di calibrazione a misura multimetro
  Vbatt_perc = map(Vbatt*1000,VbattMin,VbattMax,0,100);
  //Serial.print(Vbatt);Serial.print("\t");  Serial.println(analogRead(batPin));
  
  if(motJuice_perc == 0 && btnState == HIGH) // se throttle == 0 e bottone premuto, stampa misura batt
  {
    displayBATperc((int) (Vbatt_perc)); 
  } 
  // ----------------------------------------------------------------------------------//

  //TRASMISSIONE
  // costituzione messaggio di invio (1 digit)
  msgStr=String(motJuice); // conversione in stringa
  
  char msg[msgStr.length()+1]; //dichiarazione  char
  msgStr.toCharArray(msg,msgStr.length()+1); //conversione in char
  tx.send((uint8_t *)msg, msgStr.length()+1); // invio del messaggio 
  tx.waitPacketSent();       // attesa conclusione invio   

// -------- STAMPA MESSAGGIO DI INVIO
/*
  Serial.print("Sent:  ");Serial.print(msgStr);Serial.print(" - ");
  for (int i=0; i<= msgStr.length(); i++)
  {
   Serial.print(char(msg[i]));
  }
  Serial.println();
  */
// --------------------------------- //
  
  /*if(Serial.available()>0)
  {
   msg = Serial.readString();

    motJuice = msg.toInt();
  }*/
}

void displayBATperc(int Vbatt_perc)
{
  uint8_t displayMSG[] = { 0xff, 0xff, 0xff, 0xff };  // 0xff = 255 – tutto acceso?

  // digit 0 : stampa "b"
  displayMSG[0] = SEG_F | SEG_E | SEG_C | SEG_D | SEG_G;

  // primo digit - cenitnaia
  if(Vbatt_perc >= 100)
  {
    int centinaia = (int) (Vbatt_perc/100);
    displayMSG[1] = digitalDisplay.encodeDigit((int) (Vbatt_perc/100));
  }
  else 
  {
    displayMSG[1] = 0b00000000; //digit delle centinaia tutto spento
  }
  
  // secondo digit - decine
  if(Vbatt_perc >= 10)
  {
    int decine = (int) (Vbatt_perc/10) - ((int) (Vbatt_perc/100)) * 10;
    displayMSG[2] = digitalDisplay.encodeDigit(decine); 
  }
  else 
  {
    displayMSG[1] = 0b00000000; //digit delle centinaia tutto spento
  }
  
  // terzo digit - unità
  int unita = (int) Vbatt_perc - ((int) (Vbatt_perc/10)) * 10;
  displayMSG[3] = digitalDisplay.encodeDigit(unita); 

  digitalDisplay.setSegments(displayMSG);
}

Why R2>>>R1 is wrong? don’t I want to measure Vcc ? Also in this way there wouldn’t be power consumption over the R2, i guess.

Also please help me get this straight and correct me if I’m wrong: You want to lower the voltage (0.77V - 0.49V) over R2, lowering R2 value. Then you want to compare it with a lower reference value (internal reference of 2.56V for ATMEGA328P).

piepolitb:
Why R2>>>R1 is wrong? don't I want to measure Vcc ?

Because you don't get the measured voltage in the range you want it to be. Just do the math.

Sure you want a large overall resistor value but it's the ratio of the two that counts. Also your ADC measures in steps of about 1 mV (0-1023 over 0-1.1 V using the internal reference). 23.8Ω + 1 M gives a resulting voltage of way less than 1 mV.

wmarle, thank you I did everything you said and it worked.
And I finally get how it works.

Thanks for the suggestion - will add this feature to my next project!!

for anybody who may be in needs, here is the code relative to the partition divider

#define batPin A1

// inizializzazioni partitore di tensione
double Vbatt; double Vbatt_perc; double V_R2;
double VbattMax = 4.3; double VbattMin = 2.7; 
double resolutionVoltage = 0.00107422; // resolution = AREF / 1024 = 1.1V / 1024
double R1 = 20000; double R2 = 100000;


void setup() {
  Serial.begin(9600);
  pinMode(batPin,INPUT);
  analogReference(INTERNAL); // tensione interna di riferimento a 1.1V per confronto lettura analogica
}

void loop() 
{
  // ------------------- MISURA BATTERIA con PARTITORE di TENSIONE ---------------------//
  V_R2 = analogRead(batPin) * resolutionVoltage; // V ai capi di R2 = [0-1023] * risoluzione  
  Vbatt = (V_R2)*(((R1+R2) / R1)) ; // per il partitore di tensione
  Vbatt_perc = 100 * (Vbatt - VbattMin) / (VbattMax - VbattMin); // carica percentuale
  Serial.print(Vbatt);Serial.print("\t");Serial.print(V_R2);Serial.print("\t");  Serial.println(analogRead(batPin));
}

Glad to hear it worked.

On Arduinos (ATmega processors) there is no difference between a double and a float. They're all floats - 4-byte floating point values.

larryd:
Also see:
Google Code Archive - Long-term storage for Google Code Project Hosting.

//http://code.google.com/p/tinkerit/wiki/SecretVoltmeter

long readVcc() {
 long result;
 // Read 1.1V reference against AVcc
 ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
 delay(2); // Wait for Vref to settle
 ADCSRA |= _BV(ADSC); // Convert
 while (bit_is_set(ADCSRA,ADSC));
 result = ADCL;
 result |= ADCH<<8;
 result = 1126400L / result; // Back-calculate AVcc in mV
 return result;
}

void setup() {
 Serial.begin(9600);
}

void loop() {
 Serial.println( readVcc(), DEC );
 delay(1000);
}

Hi,
Could I use this for measuring VCC on my standalone Atmega328P-AU (running at 8Mhz, powered directly from a 3.7v lipo battery)? With no voltage devider and all that?

I want to measure the battery voltage, and display the battery capacity using five LEDs in my project.

Also, in this example, if I want to make a code like:
if X > 4000 (4.0 volts)
digitalWrite(LED, HIGH);
What is the word for X that the voltage value is set to?

HansiFansi:
Could I use this for measuring VCC on my standalone Atmega328P-AU (running at 8Mhz, powered directly from a 3.7v lipo battery)? With no voltage devider and all that?

Yes.

...R