Well, now I've tried to try a few things in my code and have some interesting results. However I'm not done and will test this in various temperatures next.
In the following code I use three methods. All of them uses the same voltage divider so changes due to resistor tolerances and temperature change will be the same for all.
Method 1 is using the code from retrolefty.
Method 2 is using the code from Scott Daniels. This one suggests a method of calibrating which I've done: http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
Method 3 is just using 5V without bothering to know about reference voltage etc.
Powering using USB:
4.52V (measured using my meter)
4.44V (method 1) diff 1.8%
4.51V (method 2) diff 0.2%
4.45V (method 3) diff 1.5%
Powering usning 3S:
11.62V (measured using my meter)
11.46V (method 1) diff 1.5%
11.62V (method 2) diff 0.0%
11.61V (method 3) diff 0.1%
// Boopidoo Ground Station
#include <LiquidCrystal.h>
//LCD display pinout - YM2004A & OV1604A
//VSS LCD pin 1 - Connect to ground
//VDD LCD pin 2 - Connect to +5V
//V0 LCD pin 3 - Connect to potentiometer
//RS LCD pin 4 - Arduino pin D07
//RW LCD pin 5 - Connect to ground
//EN LCD pin 6 - Arduino pin D08
//DB4 LCD pin 11 - Arduino pin D09
//DB5 LCD pin 12 - Arduino pin D10
//DB6 LCD pin 13 - Arduino pin D11
//DB7 LCD pin 14 - Arduino pin D12
//ELA LCD pin 15 - Arduino pin D13
//ELK LCD pin 16 - Connect to ground
//LiquidCrystal lcd(7, NULL, 8, 9, 10, 11, 12);
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int screen_backlight = 13; //pin D13 will control the backlight
float voltage_battery1 = 0.0; //voltage from pin A0
float voltage_reference1 = 0.0; //reference voltage on the arduino 5V-rail
float voltage_battery2 = 0.0; //voltage from pin A0
float voltage_reference2 = 0.0; //reference voltage on the arduino 5V-rail
float voltage_divider = (6780.0+2720.0)/2720.0; //((R1+R2)/R2)*voltage for voltage divider before pin A0
void setup() {
pinMode(screen_backlight, OUTPUT); //LCD Setup
digitalWrite(screen_backlight, HIGH); // turn backlight on. Replace 'HIGH' with 'LOW' to turn it off.
lcd.begin(20,4); // columns, rows. use 16,2 for a 16x2 LCD, etc.
lcd.clear(); // start with a blank screen
}
//method 1
int getBandgap(void) // Returns actual value of Vcc (x100)
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// For mega boards
const long InternalReferenceVoltage = 1115L; // Adjust this value to your boards specific internal BG voltage x1000
// REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc reference
// MUX4 MUX3 MUX2 MUX1 MUX0 --> 11110 1.1V (VBG) -Selects channel 30, bandgap voltage, to measure
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
#else
// For 168/328 boards
const long InternalReferenceVoltage = 1056L; // Adjust this value to your boards specific internal BG voltage x1000
// REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc external reference
// MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Selects channel 14, bandgap voltage, to measure
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
#endif
delay(50); // Let mux settle a little to get a more stable A/D conversion
// Start a conversion
ADCSRA |= _BV( ADSC );
// Wait for it to complete
while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
// Scale the value
int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value
return results;
}
//method 2
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1.1 * (5.00/5.13) * 1023 * 1000 / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
//result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
void read_voltage() {
//method 1 using internal reference voltage
voltage_reference1=(float)getBandgap();
voltage_battery1=map(analogRead(0),0,1023.0,0.0,voltage_reference1) * voltage_divider;
//method 2
voltage_reference2 = readVcc();
voltage_battery2 = map(analogRead(0),0,1023.0,0.0,voltage_reference2) * voltage_divider;
}
void screen_print() {
//printing method 1
lcd.setCursor(0,0);
lcd.print(voltage_reference1 / 100);
lcd.setCursor(0,1);
lcd.print(voltage_battery1 / 100);
//printing method 2
lcd.setCursor(10,0);
lcd.print(voltage_reference2 / 1000);
lcd.setCursor(10,1);
lcd.print(voltage_battery2 / 1000);
//printing original method not bothering with voltage reference
lcd.setCursor(0,3);
lcd.print(analogRead(0)*voltage_divider/1023*5);
}
void loop() {
read_voltage();
screen_print();
}
Also, another issue is that some of the values seem to fluctuate on my LCD screen. This is especially apparent for method 2. Any suggestions on how to reduce this and make the value more stable? I've added a 470n capacitor between A0 and ground.