Want to have Arduino check 9v battery status, suggestion?

Sometimes I run my Arduino off 9v battery and I've always wanted to have a battery level check. One likely solution is to run a wire from the + terminal of DC jack, using voltage divider to bring the battery current below 5v, then feed it in analog pin. But I am not quite sure what's right for resistor or wiring.

Sometimes I run my Arduino off 9v battery

The problem with this is you may never be able to run your project for long enough to adequately test it.
Chapter and verse

CtrlAltElite:
The problem with this is you may never be able to run your project for long enough to adequately test it.

In which case OP knows the battery's flat so that is the test :wink:

Start with 220k for R1 and 22k for R2. Use the internal analog voltage reference. Tune the calculation in the sketch to get the right voltage. Use the average of 5 to 100 samples.

Thanks for some info and schematic, it's something for me to work with.

CtrlAltElite:
The problem with this is you may never be able to run your project for long enough to adequately test it.
Chapter and verse

9v off 6x 'AA' battery pack, 5v switching regulator rather than cheap linear regulator, my project was drawing between 25mA and 55mA depending on what's being used. Barring any problem this is about 35 hours continuous operation off alkaline batteries.

Wilykat, you said 9v battery.... Maybe you meant 9v battery pack.

Wilykat, you said 9v battery.... Maybe you meant 9v battery pack.

(A 9V radio/smoke-alarm battery is typically a pack of 6 AAAA cells.)
Still, it would've been nice to have been told what sort of battery we were talking about.
Rather than a switch-mode PSU, I'd've used three AAs, for a 4.5 V supply.

One nice thing about a bench power supply with variable voltage, I can simulate voltage range. Assuming I wired my Arduino correctly, 220k from 9v source to A0, 22k from A0 to ground, plus a few LEDs + resistors from 9v to ground just to provide load on the power supply. I did put my multimeter on the 9v supply to verify voltage reading while I changed the level and read the serial monitor.

Used this code:

/*
  AnalogReadSerial

  Reads an analog input on pin 0, prints the result to the Serial Monitor.
  Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu).
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/AnalogReadSerial
*/

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(100);        // delay in between reads for stability
}

I was reading:
110 6v
120 6.5
129 7v
138 7.5v
146 8v
155 8.5v
164 9v

So roughly 9 value for every 1/2 voltage level via resistor divider. I did allow several seconds each step (and 10 readings per second), and most of the time the value remained unchanged once I stopped turning the dial and Arduino settled on new value.

incidentally I checked analog pin with my meter, it was about 0.9v on analog pin when I had 9v setting.

My final code will be if it's below 110 then lcd.write("LOW! Replace it!"), else take the range and convert it to 1-100% scale (where 1% is 6v) and display block bar graph on LCD. Using only solid 5x8 pixels block, each block would be about 6.25% between full and 6v, or close to .2v per block.

220k from 9v source to A0, 22k from A0 to ground

Why not just a simple divide by two, with 220K on both legs?

@CtrlAltElite, because I prefer the internal analog voltage reference to measure a voltage.

because I prefer the internal analog voltage reference to measure a voltage.

OK, it's just that neither your code nor your results reflects that preference.

I guess that wileycat agreed with me and continued with the 220k and 22k that I proposed, or he just wanted to test what happened with those values.

The numbers 220k and 22k is a high resistor value and less accurate. That means there is very little current drain from the battery, but the resulting measurement is a little more inaccurate. You can compensate that with the average of a few samples.

For a good analog value, the impedance at a analog pin should be 10k or less. That is not a very strict value, and 22k is still okay.

Is it with an Arduino Uno ?

The next code is not tested.

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

void loop() 
{
  unsigned int total = 0;
  const int n = 10;                      // maximum 30 will fit into a unsigned int
  for( int i=0; i<n; i++)
  {
    total += analogRead( A0);
  }
  float averageValue = float( total) / float( n);
  float voltageAtPin = averageValue / 1024.0 * 1.1;     // adjust the 1.1 for the actual reference voltage
  float voltageBattery = voltageAtPin / 22E+3 * (22E+3 + 220E+3);  // voltage divider

  Serial.println( voltageBattery);
  delay( 1000);
}

CtrlAltElite:
OK, it's just that neither your code nor your results reflects that preference.

I forgot about setting the voltage reference.

Koepel:
Is it with an Arduino Uno ?

Close. I am using Uno's close cousin Nano. Breadboard friendly Uno basically.

The next code is not tested.

internal 1v1 is only supported on a Mega. Uno and similar board can only use INTERNAL by itself. Otherwise the code works but I think the math is off.

4.34
11.68
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.05
5.63
0.00
0.00
0.00
0.00
0.00
0.00
4.20
11.67
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
7.27
0.00
0.00
0.00
0.00
0.00
0.02
1.69
9.85
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
12.09
8.91
1.47
0.00
0.00
0.00
0.00
0.00

The value keeps shifting between 12 and 0 while my power supply remained fixed at 9v.

EDIT: NM forgot to reconnect ground pin from the power supply to Arduino's ground so the analog pin was floating. At 9v I was getting steady 8.99 and at 6v I was getting steady 6.01 so the math is done right.

@CtrlAltElite and @wilycat, well, that is luck. The internal voltage reference is medium accurate, but the absolute voltage of that reference can be something between 1.0 and 1.2 V. After setting the INTERNAL reference, can you measure the AREF pin ? and put that value in the calculation ?
You have to do that for each Arduino board, just once.

You could also measure the actual values of the resistors and put that in the calculation.
Suppose you measure 21.9 kΩ and 221.5 kΩ, then the calculation could be done like this:

const float R1 = 221.5E+3;
const float R2 = 21.9E+3;
float voltageBattery = voltageAtPin / R2 * (R1 + R2);    // voltage divider

With those measured values, you should get a result very close to the actual voltage of the battery.

You can try to change the number of samples 'n' (for the average) between 5 and 30. That will probably not make a big difference for the stability.

There is a super tiny glitch with the calculation.
This line:float averageValue = float( total) / float( n);could be improved like this:float averageValue = (float( total) + (float(n) / 2)) / float( n);
The theory behind it, is the way the ADC works. The ADC goes to the next step when the voltage reaches that level. The average voltage is halfway between the steps.