Measure voltage when the Photon is connected to 4 AA batteries

I have a Photon connected to four AA batteries. I want to monitor the voltage output so I can set a warning when to replace the batteries. I know the Sparkfun battery shield can do this with a LiPo battery but I want to stay away from Lithium Ion batteries.

If you want a really crude way… You can use a voltage divider so that the maximum voltage going into an analog pin is at most 3.3V. This is not very accurate and drains power from the battery (minimal though).

The accuracy through a divider isn’t bad, but it’s a trade off - the more current through the divider, the more accurate (as the current into the ADC becomes negligible) but the more drain the divider places on the battery.

The best way to solve that is to use a fairly low value divider (for accuracy) and a high side switch, which disconnects the top of the divider when you’re not sampling the voltage. You have to do it this way (vs a low side switch which is easier) as otherwise you’ll end up putting the battery voltage, albeit through a resistor, on the ADC pin, which isn’t good for the processor.

The easiest way to do this is with two FETs, a PFET (to gate the power) and an NFET (to drive the PFET), like this:

(full schematic here https://www.electricimp.com/docs/hardware/resources/reference-designs/lala/ for this exact example, 4xAA to be sampled by a 3.3v ADC).

The high side switch is Q80 & Q81 in the bottom right of the schematic, and the divider scales the battery voltage down by a factor of (2.2/(4.7+2.2))=0.319; even worst-case (4xEnergizer lithiums at 1.7v) you’ll have 2.17v on the ADC pin. D80/R81 are not part of the sampling circuit, but show how you might save IO pins by dual-purposing a status LED with the high side switch enable.

Q81 isn’t labelled on the schematic but the BOM calls it out as an NXP PMV160UP. When the LED_STATUS line is driven high, Q80 - the NFET - turns on, shorting the gate of Q81 (which is normally pulled up to VBAT by R82) to ground. This turns Q81 - the PFET - on, powering the resistor divider formed by R83 & R84. The gate of Q80 is pulled low so that when LED_STATUS isn’t driven Q80 is turned off, minimizing current draw; effectively, this circuit takes no power at all when not in use.

The only problem with FETs is that breadboardable ones with decent thresholds are almost impossible to find. I think these are both SOT23’s, which are fairly small…

4 Likes

Thank you for the ideas. I was hoping for a super simple software solution like what electric imp has:
local voltage = hardware.voltage();
I can not seem to find the same for the Particle platform. Any software ideas?

@hfiennes,
The only problem with FETs is that breadboardable ones with decent thresholds are almost impossible to find. I think these are both SOT23’s, which are fairly small…

How about this, https://www.sparkfun.com/products/10213, and it’s p-channel cousin. They have a low threshold, and can also be used for high power applications, in case you need that. These seem like good general purpose FETs to have around.

The P-channel cousin ( https://www.sparkfun.com/datasheets/Components/General/FQP27P06.pdf ) isn’t so good; the parameter to look at is Vgs(th), ie the threshold where it starts to turn on. This is somewhere between -2.0 and -4.0v; if you got a -4.0v one, and all 4 batteries were low, you’d be right against the threshold. The NXP PMV160UP has a Vgs(th) maximum of -0.95v, so you’ve got margins a mile wide… like I said, through-hole PFETs with decent thresholds are hard to find :frowning:

The imp’s hardware.voltage() doesn’t do what you want either; that reads the supply voltage (well, it reads VCC, and then reads the internal reference voltage on the ADC, and works out what VCC must be for you) - as the CPU itself isn’t directly connected to the battery in this setup, that won’t help. You do need the divider setup I’m afraid, on either platform.

1 Like

If you can manage with 3 AA batteries or if your 4 batteries will never exceed 5v you can use a simpler solution with a fairly low leakage current and no additional hardware. This is based on the premise that most Photon pins are 5v tolerant except analog pins when in analog mode.
Connect your potential divider between the battery, analog in and a data pin (data pin rather than ground). When the Photon is off or asleep the pin is in a high impedance state so the data and analog pins will float up to the battery voltage. This is OK according to the data sheet as long as the pin is not in analog mode.
The key is to make sure that the data pin is pulled down to 0v before enabling the analog pin. This can be achieved by using the following code snippet …

// pull data pin down to ground before enabling analog read
// variable declarations
int refGnd = D0;
int batV   = A0;
double V;
//
void doFirst(){
  pinMode(refGnd, OUTPUT);
  digitalWrite(refGnd, LOW);    // Pull down reference before enabling analog
}

// this calls a function at startup or on wakeup
STARTUP(doFirst());

// This routine runs only once upon reset after WiFi is up
void setup() 
{
    pinMode(battV,INPUT);
    V = 3.3 / 2048 * analogRead(battV);
....
....
  
   

I have this monitoring my batteries and everything seems OK. It has markedly improved my battery life compared with the always grounded divider. When the data pin is in its high impedance state the voltage across the divider is 100mV instead of around 4v so the drain from the batteries is down by a factor of 40.

If I have the wrong end of the stick please let me know before we get some long sunny days and my battery voltage climbs from the present 3.9v to the summer 4.2v

I haven’t looked at how to return the data pin back to a high impedance state other than by putting the Photon to sleep or off.

1 Like

postscript: I’ve just checked with 4 rechargeable AAs (rather than the 3 I normally use) and when on permanent trickle charge (from a solar cell) they rise to 5.2 volts. The simple fix without active components is to drop 0.7v across a diode. That leaves 4.5v on a full charge. In the circuit below I’ve used D1 rather than D0 to pull the potential divider down.

Generally you should be careful about relying on a voltage drop over a diode unless you know you will always have a minimum load. The diode forward voltage can go way below 0.7v as the current drops - if the photon went to a microamp-level sleep mode you might end up with 5.2v on VIN.

That’s not likely a problem in itself here though, as most 5v supplies (probably including the ones on the photon) have absolute max ratings of ~6v, but something to bear in mind when using that technique.

1 Like

Thanks that is a good tip and you are quite right.
Firstly apologies, I drew my circuit wrongly. The diode is dropping the voltage to the potential divider in order to protect the data pins, Vin is happy up to 5.5v. Nevertheless, I’m only getting 0.37v across the diode when the Photon is off.

Secondly, having measured the real world voltages I find that the floating voltage on the analog and data pins never reaches 5v even without the diode.

Analysing the circuit, and assuming my resistors are at their nominal 100K value (a big assumption) I find that the analog pin has a resistance to ground of about 500K and the data pin about 1M. Consequently the maximum voltage that the analog and data pins see is under 5v.

I get:

Vin          5.36
Vanalog      4.85
Vdatapin     4.45

I appreciate that my measured battery voltage will be fairly approximate but it is reproducible and lets me see how the battery is holding up - and it looks as though I won’t cook the Photon.

The other option here is to have the divider in place, but use a zener diode to clamp the voltage at the ADC pin to prevent damage - ie the zener will clamp before the ESD diodes within the STM32 start conducting. For that, you just place the zener (facing “up”, ie cathode to the signal to clamp, anode to ground) directly on the pin. When you have not driven your digital pin low to “enable” the divider, you’re then ONLY passing current if the voltage is above the zener voltage, and it’s only the differential between the input and the zener clamp that’s in play, so current will be significantly reduced.

Note that common or garden zeners are not necessarily terribly accurate. Check the datasheet.

I also believe that pins configured to analog are not 5v tolerant on the STM32, even if they are tolerant when configured as digital… though I’m not sure how the ESD structures on-die are formed in that case! If that is true - this is just a memory from reading the datasheet in the past - then you’d not want to enable the analog input until after the divider was enabled, and re-configure the pin as digital before turning the divider off.

1 Like

Very neat - I like it. I’ll see what zener diodes our local shop stocks when I’m in town tomorrow.
You are correct, the analog pins in analog mode are not 5v tolerant which is why I use the STARTUP macro to pull the data pin low and drop the voltage on the analog pin to around 2.5v quite a while before the analog pin is enabled in the setup() function. I’m relying on the accuracy of the Photon data sheet which states the pins are 5v tolerant when not in analog mode.
I really appreciate the advice - thank you.