How to programatically determine if my Arduino is running from USB power or VIN?

Please forgive me if I'm asking a Frequently Asked Question here. I've been googling and forum-searching for a while and I can't find the answer. If this is in a FAQ somewhere, I'd be grateful if someone could give me a link with the answer.

Background:
I am writing code for the Arduino Uno. It is driving a daughterboard of my own design, and the daughterboard contains some relays which require an external power voltage, so I must power the system using a 12v wall-wart with enough juice to drive the relays (the arduino itself is not strong enough to drive them). I am also using the USB connector on the arduino as a serial communication command/control line, connected to host pc at the same time. So the interesting thing about this project is that it will be connected to two different power sources at the same time to operate correctly. It is connected to the wall wart, and it is also connected to the host PC via USB.

The problem:
The end-user might plug in the USB cable without plugging in the external wall wart power supply. If they do that, the Arduino code will run fine, the LEDs will light up, but the relays will not switch. In other words, it will look like the device is only partially working.

My question:
Using the Arduino programming language running on the Arduino itself, how can I make my code behave differently depending on whether the arduino is getting its power from the USB cable, or getting its power from the power jack?

I want the code to do something like this:

if (ExternalPower == TRUE)
   {
    Serial.println("Arduino is correctly powered by an external power source.");
    TurnOnTheGreenLED();
    RunMainProgramCode();
   }
else
    {
      While (1)
          {
             Serial.println("ERROR: Arduino is not plugged into an external power source.");
             TurnOnTheRedLED();
             DoNothing();
          }
   }

If this can be done entirely in code without any special circuitry, then what is the technique?

If this requires some sort of special trick, such as connecting the VIN pin back to an analog input pin, and then reading that pin, then that would be an acceptable solution. However, I don't know just how to do that without blowing the Arduino. The external power supply is 12v 500ma, and I don't want to overload the Arduino. What would be the correct circuit to accomplish this? And what would the resulting code look like?

You can measure the current chip supply voltage using the internal ADC and 1.1v voltage reference.

I have found that when connected to the USB of my laptop I get 5.12v. When running on the wall wart through the internal voltage regulator I get 5.04v

The actual USB voltage may vary from computer to computer.

You can get the Vcc monitoring code here: Google Code Archive - Long-term storage for Google Code Project Hosting.

tfabris:
The external power supply is 12v 500ma, and I don't want to overload the Arduino. What would be the correct circuit to accomplish this? And what would the resulting code look like?

Run the Vin through two resistors as a voltage divider, that can bring the voltage down to an acceptable level. Then measure that through an analog pin.

eg.

That circuit would cut 12V down to 4V. Thus an analogRead would return (around) 819 (being 4/5 * 1024). But with only USB input it would be much lower.

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

void loop ()
{
  if (analogRead (A2) > 400)
    Serial.println ("12V connected");
  else
    Serial.println ("USB only");
    
   delay (1000);
}

(Actually Vin would be a bit less than 12V because of the protection diode but you would be able to easily tell the difference between the two).

      While (1)

Change that to "while (test if power not connected)" so they can plug in the wall-wart and have it recover without having to reboot.

That would require hardware changes - the OP wants software only. The on-board regulator will give the same reading whatever the input voltage. Compare it to the 1.1v Vref, and if it is the voltage that the voltage regulator gives you then you are running on wall wart. If it's anything else, then you're on USB.

Thanks for the replies, Majenko and Nick! They are enlightening.

That would require hardware changes - the OP wants software only.

Actually, I'm fine with hardware changes, if there isn't a software-only method, or if the software-only method isn't 100 percent reliable.

The on-board regulator will give the same reading whatever the input voltage. Compare it to the 1.1v Vref, and if it is the voltage that the voltage regulator gives you then you are running on wall wart. If it's anything else, then you're on USB.

This is the technique you linked to higher up on the thread, correct? That's software-only, right? Is that technique reliable? For example, it will work no matter what the voltage of the adapter I have plugged in?

Change that to "while (test if power not connected)" so they can plug in the wall-wart and have it recover without having to reboot.

Ah yes, of course. :slight_smile:

I'm going to experiment with Majenko's software-only method first, since it requires the least amount of work. However, the voltage divider diagram you provided, Nick, is extremely useful and looks like it would be very likely to be of high reliability! So if I run into trouble with the software method, I'll try that instead. Thanks so much!

it will work no matter what the voltage of the adapter I have plugged in?

The on-board voltage regulator will always regulate to the same voltage - that is the point of it.

There is a chance the USB could give the same reading as the on-board regulator, so it's not 100% reliable, but close.

I think the resistors will be more reliable, the other method relies on the USB and the voltage regulator having different voltages.

I have found that when connected to the USB of my laptop I get 5.12v. When running on the wall wart through the internal voltage regulator I get 5.04v

Considering the analogRead maxes out at 5V you may have a problem with that. Those are both over 5V. It may work, but it might be marginal.

The on-board voltage regulator will always regulate to the same voltage - that is the point of it.

What about different Arduino units? I intend to build multiple copies of this thing once I get it debugged. Would the results be different from one Arduino board to the next?

Would the results be different from one Arduino board to the next?

Possibly - it's hard to tell. We could do with running a survey - get people to test their board's regulator voltage (using a sketch) and post the results - see how they vary.

One one Uno:

USB:

5096
5096
5120
5120
5096
5096
5096
5096

9V in:

5073
5073
5073
5073
5073
5073
5073
5073

On another one:

USB:

5028
5028
5028
5028
5028
5028
5028
5028

9V in:

5006
4984
4984
5006
4984
4984
4984
4984
4984

I don't think you will be able to get the "magic number" using this method. Not for a series of boards.

Pants - Ah well, worth a try.

Looks like you'll have to have a voltage divider from Vin to an analogue input pin.

Awesome! Voltage divider it is! Thanks for all your help!

Run a flywire from pin 1 of the LMV358 dual op amp to any unused ATmega pin. If HIGH you are running on Vin. If LOW then Vcc is provided by USB or another source.

Just another option.

Jim

PS There is already a voltage divider on the Uno that splits Vin in half. Look on the Uno schematic at the inputs to LMV358-1.

PS There is already a voltage divider on the Uno that splits Vin in half. Look on the Uno schematic at the inputs to LMV358-1.

However half of 12V is still too high for an analog pin.

Agreed.

Jim

Nick, I have implemented the voltage divider cirucuit, and the code, as shown in your example. It seems to work like a charm. I have a couple of worries though:

  • I didn't have a 20k resistor so I used a 22k resistor instead. So the circuit is made with a 10k resistor and a 22k resistor. Seems to work though! Would you expect that would be OK?
  • The value read from A2 when powered via USB is incredibly consistent: 286, 287, 288, and nothing else.
  • The value read from A2 when powered via a 12v power supply is widely variable: Anywhere from 613 to 939, and everywhere in between. They are all still above 400, though, so far. Is this expected, and does the variability mean that I might not be able to depend upon that 400 cutoff every time?

Thank you so much for all your help!

I didn't have a 20k resistor so I used a 22k resistor instead. So the circuit is made with a 10k resistor and a 22k resistor. Seems to work though! Would you expect that would be OK?

Yes. The voltage will be slightly lower at the common junction, which is better than too high.

The value read from A2 when powered via a 12v power supply is widely variable: Anywhere from 613 to 939, and everywhere in between.

This is not good. If you have a constant 12V to the voltage divider, you should get a constant value at the junction, which is what should be connected to the analog pin that you are reading, so you should get a relatively constant reading.

Okay, it could have just been a crap power supply, combined with the fact that the whole thing is plugged into the prototype breadboard with all the relays in it. The one I had on-hand for the first quick test was only 12v 400ma, and it was scrounged from a particularly crappy old flatbed scanner, so who knows if it was any good.

Instead, I grabbed a 12v 1a power supply from a different device, and now the the A2 values are steady as a rock at 725-726.

When I get back to the office tomorrow, I'll try the official "nice" 500ma power supply that I was intending to source in bulk, and see if that provides a steady output. If not, I'll just source a 1a power supply and call it good.

Thanks again for such great help and advice, all!

tfabris:
Okay, it could have just been a crap power supply ...

Very likely. Is it a "regulated" supply? If not, the voltage will probably vary widely (depending on the load). Make sure that it doesn't exceed 15V or you may be in trouble with your input pins. I assumed that you would not exceed that much. If it does exceed 15V unloaded increase the 22K resistor even higher and compensate in the code if necessary.