Maker Pro
Maker Pro

running project on lithium battery and reading constant battery voltage adc

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
Hi I am super confused with reading my lipo battery voltage.

  1. I WANT TO CREATE A FUNCTION TO READ MY 3.7~4.2V,1200mAH BATTERY VOLTAGE THROUGH A MICROCONTROLLER (ARDUINO OR ATTINY44).
  2. THEN I WANT TO CALL THE CREATED BATTERY FUNCTION IN THE FORMULA TO READ PRECISE SENSOR VALUE: int voltage = ((CALLING BATTERY FUNCTION) * (READ SENSOR VALUE)) / 1023;
  3. I AM USING INTERNAL REFERENCE VOLTAGE 1.1V.
  4. I NEED A FIXED VREF SO THAT THE SENSOR(voltage) FORMULA VALUE DOES NOT CHANGE.
NOTE: I want the whole system to run on lithium battery ( i am just creating a prototype on Arduino, if it works, ill be editing the code for my attiny44 using 8mhz external crystal).

Unfortunately, i tried almost all codes available on the internet but i could not read the right voltage. I believe that there is some issue with my connections.1676100452387.png
 

Attachments

  • ReadBatteryVoltage.ino
    1.4 KB · Views: 2

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
Simply use AnalogRead(). You get an integer between 0000 and 1023. Multiply by 5, divide by 1023 et voilá.

i tried almost all codes available on the internet
I doubt that assertion. How lang would you have been at trying all the codes one can find on the net ;)?
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
Simply use AnalogRead(). You get an integer between 0000 and 1023. Multiply by 5, divide by 1023 et voilá.


I doubt that assertion. How lang would you have been at trying all the codes one can find on the net ;)?
Hi, Thank you for your response. You can multiply it by 5V only if you have constant/ stable voltage. My case is diff, i have a battery that depletes with usage and therefore, i needed a system/program that can possibly work for me. I have found the solution to my problem and now it is working perfectly the only issue i have now is that i dont have any spare pin on my attiny that can be used to read battery voltage. I am trying to use reset pin as I/O.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
Nonsense, why should that be so? The ADC value is variable and you can multiply it by 5 easily.

Not the best idea. This may interfere with programming.
Nonsense, why should that be so? The ADC value is variable and you can multiply it by 5 easily.

Not the best idea. This may interfere with programming.
U can multiply it by 5 only when you have constant 5v not otherwise
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
U can multiply it by 5 only when you have constant 5v not otherwise
I can always multiply by 5. I'm afraid I really don't understand the problem. Please explain in detail.

The whole idea of measuring a voltage is based on the voltage not being constant - other wise you wouldn't have to measure it.
Assuming an Arduino Uno (5 V reference) hre's whart you get from analogRead():

input voltageAnalogRead()AnalogRead()/1023×5
0 V00
1 V2051.002
2 V4091.999
3 V6143.001
4 V8143.998
5 V10235.000
Of course the exact values will jitter and some smoothing and rounding to meaningfull accuracy (e.g. 1 decimal only) will be required.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
I can always multiply by 5. I'm afraid I really don't understand the problem. Please explain in detail.

The whole idea of measuring a voltage is based on the voltage not being constant - other wise you wouldn't have to measure it.
Assuming an Arduino Uno (5 V reference) hre's whart you get from analogRead():

input voltageAnalogRead()AnalogRead()/1023×5
0 V00
1 V2051.002
2 V4091.999
3 V6143.001
4 V8143.998
5 V10235.000
Of course the exact values will jitter and some smoothing and rounding to meaningfull accuracy (e.g. 1 decimal only) will be required.
ok, i will explain.

I want to charge my battery from a mini solar panel. i have been using Arduino for prototyping purpose only mainly i will be using attiny IC and the whole system will be powered by a lithium battery 3.7~4.2V respectively. I need to read the voltage coming from the solar panel and add the desired value into the code for day/night detection. for instance, if solar reads (175 ADC count) then it should detect daytime and charge the battery connected to it whereas, if the value is anywhere below 175 ADC count then detect night and do something. Now in order to do so i am using internal voltage reference and have used voltage dividers at battery and solar panel reducing the voltage below 1.1v.


int led = 3;

void setup(){
Serial.begin(9600);
pinMode (led,OUTPUT);
}

void loop() {
//REFS1 AND REFS0 to 1 1 -> internal 1.1V refference
analogReference (INTERNAL);
//ADMUX = B11000000;
//We read A1 (MUX0)
int value = analogRead (1);
//ADMUX = B00000001;
//int sensor = analogRead(A1);
// Start AD conversion
ADCSRA |= B11000000;
// Detect end-of-conversion
while (bit_is_set(ADCSRA,ADSC));
float val = ADCL | (ADCH << 8);
val = val * 5.7; //Multiply by the inverse of the divider
value=val;
Serial.println(val);

float voltage = val * analogRead(2) / 1023;
voltage = ADCL | (ADCH << 8);
int sensor = voltage * 5.7;
Serial.print ("voltage at A0:");
Serial.println(voltage);
Serial.print ("Solar panel voltage:");
//Serial.println(voltage * 5.7);
delay(1000);

if (voltage>=174){ //0.9- 1.1V
digitalWrite (led,HIGH);
}
else{
digitalWrite(led,LOW);
}
}
 

Attachments

  • schematic.JPG
    schematic.JPG
    47.3 KB · Views: 3

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
Now in order to do so i am using internal voltage reference
Then replace 5 by the value of the internal voltage reference.
But you'll require stable 5 V for correct operation of the Arduino anyway, so you can use this as is default in analogRead().

AnalogRead() used as shown in my table will give you the value of the voltage at the analog input pin. As this voltage is a scaled down version of the voltage at the battery (or solar panel or whatever source), you simply multiply the measured value by the inverse of the scale factor.
Example:
scale factor from voltage divider is e.g. sc = 0.175 (as per the resistive divider shown in your post #9), then
voltage = analogRead() / 1023 × 5 V × (1 / 0.175) = analogRead() × 0.0279
Numbers:
source voltage from the panel is e.g Vpanel = 25 V
input voltage to Arduino is results in Vin = panel × 0.165 = 4.375 V
Value from analogRead() is then analogRead() = 4.375 V * 1023 × 5 = 895
Value after multiplication with the factor derived above results in MeasuredValue = 895 × 0.0279 = 24.97 V or rounded to 1 decimal 25.0 V
Exactly what you want an in line with my previous explanation and calculation.

If you still want to use the internal reference, substitute the factor "5" in the above calculations by "1.1". Note that the input voltage to the analog pin of the Arduino must be less than or equal to 1.1 V in this case. Any voltage above will not be registered since the max. result from analogRead() is 1023.
It is imho favourable to use the 5 V reference as this spreads the measuring range of the ADC over a wider volateg range and thus reduces sensitivity to noise. Of course you'd have to modify the voltage divider accordingly.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
Then replace 5 by the value of the internal voltage reference.
But you'll require stable 5 V for correct operation of the Arduino anyway, so you can use this as is default in analogRead().

AnalogRead() used as shown in my table will give you the value of the voltage at the analog input pin. As this voltage is a scaled down version of the voltage at the battery (or solar panel or whatever source), you simply multiply the measured value by the inverse of the scale factor.
Example:
scale factor from voltage divider is e.g. sc = 0.175 (as per the resistive divider shown in your post #9), then
voltage = analogRead() / 1023 × 5 V × (1 / 0.175) = analogRead() × 0.0279
Numbers:
source voltage from the panel is e.g Vpanel = 25 V
input voltage to Arduino is results in Vin = panel × 0.165 = 4.375 V
Value from analogRead() is then analogRead() = 4.375 V * 1023 × 5 = 895
Value after multiplication with the factor derived above results in MeasuredValue = 895 × 0.0279 = 24.97 V or rounded to 1 decimal 25.0 V
Exactly what you want an in line with my previous explanation and calculation.

If you still want to use the internal reference, substitute the factor "5" in the above calculations by "1.1". Note that the input voltage to the analog pin of the Arduino must be less than or equal to 1.1 V in this case. Any voltage above will not be registered since the max. result from analogRead() is 1023.
It is imho favourable to use the 5 V reference as this spreads the measuring range of the ADC over a wider volateg range and thus reduces sensitivity to noise. Of course you'd have to modify the voltage divider accordingly.
From where did you find the value sc = 0.175. In my explanation it is just adc reading that varies. i have a 6v panel.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
From where did you find the value sc = 0.175.

Your voltage divider 1 kΩ and 4.7 kΩ:
[math]sc = \frac{1 k\Omega} {1 k\Omega + 4.7 k\Omega} = 0.175[/math]sc is the factor by which the voltage from the panel is divided before it is fed to the ADC input of the Arduino - at least that's what you show in post #9
In my explanation it is just adc reading that varies.
Of course only the ADC readings change. All other factors are fixed.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
float Vref = 1.1;

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

void loop() {
analogReference (INTERNAL);
ADCSRA |= B11000000;
while (bit_is_set(ADCSRA,ADSC));
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
voltage = ADCL | (ADCH << 8);
// float vbat = voltage * 5.7;
Serial.print ("voltage at A0:");
Serial.println(voltage);
Serial.print ("Solar panel voltage:");
//Serial.println(voltage * 5.7);
delay(1000);
}




please check, is it correct?
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
C:
ADCSRA |= B11000000;
while (bit_is_set(ADCSRA,ADSC));
Why do you manipulate the ADC status register? Sure it can be done but is entirely not necessary on an Arduino. The analog...() functions will do all the required workf for you. analogRead() will return once the conversion is complete.

C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
voltage = ADCL | (ADCH << 8);
The first line reads the ADC and assigns a calculated value to the variable "voltage".
The second line overwrites this value with a new value read directly from the ADC registers.
Correct or not? What do you think?
Hint: check the types. In the first line the variable "voltage" is declared as a float. In the second line you force an int value into a float variable without explict typecasting. You don't think this gives a meaningful result, do you? Even if it worked (no, it doesn't here), it is bad programming practice.

C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
can be simplified to
C:
float voltage = analogRead(A0) * 0.006144;
- hopefully the compiler does that for you, otherwise you'll waste processing time on the Arduino.

C:
Serial.print ("Solar panel voltage:");
//Serial.println(voltage * 5.7);
Useless code as the second line is only a comment. Looks like this is related to what I remarked in my second comment above.
If you want to output the ADC value as well as the calculated voltage, use two different variables. Somewaht like this:
C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
int ADC_value = ADCL | (ADCH << 8);
Serial.print ("voltage at A0:");
Serial.println(voltage);
Serial.print ("Solar panel voltage:");
Serial.println(ADC_value);

A note on the side:
doing float math on an Arduino is very time consuming as the µC does not have a floating point unit. In this simple example it may be irrelevant as there's not much else to do. If you want the µC to perform, however, you should use integer arithmetic and do some formatting only when it is required to output decimal numbers.
We've had this discussion before based on another user's question.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
C:
ADCSRA |= B11000000;
while (bit_is_set(ADCSRA,ADSC));
Why do you manipulate the ADC status register? Sure it can be done but is entirely not necessary on an Arduino. The analog...() functions will do all the required workf for you. analogRead() will return once the conversion is complete.

C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
voltage = ADCL | (ADCH << 8);
The first line reads the ADC and assigns a calculated value to the variable "voltage".
The second line overwrites this value with a new value read directly from the ADC registers.
Correct or not? What do you think?
Hint: check the types. In the first line the variable "voltage" is declared as a float. In the second line you force an int value into a float variable without explict typecasting. You don't think this gives a meaningful result, do you? Even if it worked (no, it doesn't here), it is bad programming practice.

C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
can be simplified to
C:
float voltage = analogRead(A0) * 0.006144;
- hopefully the compiler does that for you, otherwise you'll waste processing time on the Arduino.

C:
Serial.print ("Solar panel voltage:");
//Serial.println(voltage * 5.7);
Useless code as the second line is only a comment. Looks like this is related to what I remarked in my second comment above.
If you want to output the ADC value as well as the calculated voltage, use two different variables. Somewaht like this:
C:
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
int ADC_value = ADCL | (ADCH << 8);
Serial.print ("voltage at A0:");
Serial.println(voltage);
Serial.print ("Solar panel voltage:");
Serial.println(ADC_value);

A note on the side:
doing float math on an Arduino is very time consuming as the µC does not have a floating point unit. In this simple example it may be irrelevant as there's not much else to do. If you want the µC to perform, however, you should use integer arithmetic and do some formatting only when it is required to output decimal numbers.
We've had this discussion before based on another user's question.
Thank you so much for an in-depth response. okay, so, i tried your code and it worked for me.


void setup() {
// put your setup code here, to run once:
Serial.begin (9600);
analogReference (INTERNAL);
}

void loop() {
float voltage = (analogRead(A0)/1023 * (1.1) * (1/0.175));
int ADC_value = ADCL | (ADCH << 8);
//Serial.println(voltage);
Serial.print ("Solar panel voltage:");
Serial.println(ADC_value);
delay(1000);
}
SERIAL OUTPUT

Solar panel voltage:250
Solar panel voltage:250
Solar panel voltage:251
Solar panel voltage:251
Solar panel voltage:251
Solar panel voltage:255
Solar panel voltage:248
Solar panel voltage:46
Solar panel voltage:151
Solar panel voltage:332
Solar panel voltage:287
Solar panel voltage:302
Solar panel voltage:299
Solar panel voltage:293
Solar panel voltage:431
Solar panel voltage:940
Solar panel voltage:898
Solar panel voltage:910
Solar panel voltage:843
Solar panel voltage:345
Solar panel voltage:385
Solar panel voltage:985
Solar panel voltage:945
Solar panel voltage:1023
Solar panel voltage:166
Solar panel voltage:53
Solar panel voltage:54

I will have to test it outside under sunlight as well because my old codes maxed out under little sunlight.

ok, it will be great if you can answer my following questions:


1) My question is that, is it necessary to measure battery voltage as constant voltage ( I mean irrespective of what voltage is at the battery end, the code or IC reads full on value 1023 always) because as per the voltage calculations formula int sensor =( Vref/1024)*ADC; in this formula all values must be constant or else the solar value would change as the battery voltage depletes. Please clarify if its imp to measure battery voltage or we can directly substitute 1.1v in place of vref.


2)1677733216681.png

This is my circuit.

See the above picture, now the issue is that, if i connect battery to solar like this it reads 1.1v, 6.27v respectively.
i need to connect my solar panel to battery because it will be charging the battery during the daytime.


NOTE : I have used LDR in place of solar panel due to the unavailability of the component in my software.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
is it necessary to measure battery voltage as constant voltage
Seems I don't understand the question. What is the pöoint of measuring a voiltage when you assume it is constant? A battery's voltage is never constant. That is (probably) why you measure it.
int sensor =( Vref/1024)*ADC; in this formula all values must be constant or else the solar value would change as the battery voltage depletes.
ADC isn't constant, this is the changing value, the measured quantity from the ADC (I presume, as you haven't given more information).
Assuming further that ADC is the measured value from the panel, then why do you think this value should change with the charge status of the battery? Here's why Vref is being used. Vrfef is supposed to be constant, indeüendently from the battery voltage of supply voltage of the Arduino.
Please clarify if its imp to measure battery voltage or we can directly substitute 1.1v in place of vref.
I'm sorry but it seems that you haven't fully realized the concept of how an ADC works. You need to measure battery voltage only if you want to know its value (and possibly start some action depending on this value). It is absolutely irrelevant for measuring the panel's voltage.
i need to connect my solar panel to battery because it will be charging the battery during the daytime.
Technically you can, but you should never connect the solar panel directly to the battery. Insert a battery charge controller in between to ensure the battery is protected against overcharging and overly discharging. A good solar charge controller will also do MPP tracking to optimize the power output from the solar panel.
When you connect the solar panel directly to the battery the battery will force current into the solar panel if thepanel's voltage is below the battery's voltage. The minimum requirement would be a (Schottky) Diode and a current limiting series resistor. But a simple MPPT module isn't expensive (example).

Note that you can't power an Arduino Uno from the lithium battery directly. The Arduino requires 5 V. You'll need a step-up converter to create the 5 V from the battery voltage.

Another Note: The Arduino's internal reference voltage has an accuracy of 10 %. Assuming you use 1 % resistors, that adds at least another 1 % error to your measurement (an exact calculation will yield up tp 2 % depending on the values). SO the total measurement error will be up to 12 %! If you can't accept that error, you will have to add an external precision voltage reference with much less error and use precisin resistors (0.1 % or better).
To get you started with as little additional effort as possible I suggest you stick to your present configuration but keep the possible measurement error in mind.
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
Seems I don't understand the question. What is the pöoint of measuring a voiltage when you assume it is constant? A battery's voltage is never constant. That is (probably) why you measure it.

ADC isn't constant, this is the changing value, the measured quantity from the ADC (I presume, as you haven't given more information).
Assuming further that ADC is the measured value from the panel, then why do you think this value should change with the charge status of the battery? Here's why Vref is being used. Vrfef is supposed to be constant, indeüendently from the battery voltage of supply voltage of the Arduino.

I'm sorry but it seems that you haven't fully realized the concept of how an ADC works. You need to measure battery voltage only if you want to know its value (and possibly start some action depending on this value). It is absolutely irrelevant for measuring the panel's voltage.

Technically you can, but you should never connect the solar panel directly to the battery. Insert a battery charge controller in between to ensure the battery is protected against overcharging and overly discharging. A good solar charge controller will also do MPP tracking to optimize the power output from the solar panel.
When you connect the solar panel directly to the battery the battery will force current into the solar panel if thepanel's voltage is below the battery's voltage. The minimum requirement would be a (Schottky) Diode and a current limiting series resistor. But a simple MPPT module isn't expensive (example).

Note that you can't power an Arduino Uno from the lithium battery directly. The Arduino requires 5 V. You'll need a step-up converter to create the 5 V from the battery voltage.

Another Note: The Arduino's internal reference voltage has an accuracy of 10 %. Assuming you use 1 % resistors, that adds at least another 1 % error to your measurement (an exact calculation will yield up tp 2 % depending on the values). SO the total measurement error will be up to 12 %! If you can't accept that error, you will have to add an external precision voltage reference with much less error and use precisin resistors (0.1 % or better).
To get you started with as little additional effort as possible I suggest you stick to your present configuration but keep the possible measurement error in mind.
Hiiiiiiiiiiiiiiii, you know what, your code really helped me out, i was outside experimenting the code and the results were very much satisfying. Thank you so much for clarifying my concept actually i am aware on how adc works but there's one link that completely confused me as it says that we need to measure constant battery voltage in order to get constant sensor output (Now it all seems lame to me). Ok, now i can read my sensor value accurately.

Ok, i do have a schottky diode connected to my solar panel, i hope that will help. Now, please tell me how can i connect it to the battery for charging.


1677740381595.png


Is it possible to charge like this??
 

electronoobb

Feb 11, 2023
26
Joined
Feb 11, 2023
Messages
26
1677740896568.png
If i make this sort of setup, my solar always reads 1023 which is not what i want. so, it is a big no to this kind of setup. Please,let me know if the above setup can charge.




Now, i will try the working code with attiny, i hope it works :/
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
As said: use a charge controller, see the link in my post #16.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,098
Joined
Nov 17, 2011
Messages
13,098
If i make this sort of setup, my solar always reads 1023 which is not what i want.
You keep showing parts of your circuit but not the whole circuit. While the single parts may look o.k., teh complete circuit may still not be o.k.
 
Top