Remote Voltage Read

I'm interested in using an Arduino to read the voltage of batteries ( and possibly turn stuff on and off) via IP ( using ethernet connection, web server, etc) . How would I go about using one to read voltages from 6 to 16, so it accurate? I can probably handle the programming part, but the actual voltage reading is a bit beyond me. Any suggestions?

should be simple enough mate, set up a voltage divider to make a voltage in the range of 0-5v then feed that into one of the arduino adc inputs

The "hard part" of this project is the networking.... I'd start with the voltage divider and voltage reading, and do the network part last.

A [u]voltage divider[/u] is simply 2 resistors in series. The voltage gets divided by the ratio of the resistor values.

The Arduino's analog-to-digital converter can handle up to 5V, and there is a built-in 5V reference that you can use. If you were to use a 2k ohm resistor and an 6k ohm resistor, you'd get 1/4th (2/8) of your input voltage (1.5 to 4V). A total resistance of around 10k seems about right. Lower total resistor values will draw more cuurrent from your batteries (which you can calculate with [u]Ohm's Law[/u]). At higher values, the input resistance of the ADC will come into play and change your voltage divider. And you have the potential for more noise pick-up with higher resistance, although a capacitor can smooth-out the noise.

Resistors don't come in every possible value, so you may have to find something close to what you want and adjust your software accordingly.

The ADC is 12 bits. So with 5V applied and a 5V reference, you'll read 1023 (decimal) assuming perfect accuracy... 4 Volts will read ~818.

To measure a voltage, you need a voltage divider.

  • Vin --- R1 ---- analog pin ---- R2 ---- GND
  1. From 6 V to 16 V.
  2. Max voltage into analog pin = 5 V
  3. Get divider ratio : 5 / 17 <-- safety margin = 0.294117647 From : Vout / Vin
    It mean : When a 17 Volt at the divider, at mid point, you will get 5 V.
  4. Calculate the 2 resistors.

4.a Set total resistance of the divider. Let use 100 K
b. Calculate R2 : 100 000 * 0.294117647 = 29 411.76471 ohms
c. Calcualte R1 : 100 000 - 29 411.76471 = 70588.2353 ohms
d. Get nearest commercial values : R1 = 75 K R2 = 27 K <-- Try to get proper value. Those are standard 5 % values
e. Re-calculate new ratio : 1 / ((75 K + 27 K)/27 K) = 0.264705882
f. Re-Calculate the max voltage at 5 V out : Vin = 5 / 0.294705882 = 18.88888..... V at 5 V into analog pin. <-- Very safe.

  1. Get the digital value.
    a. At low voltage : 6 V in * 0.294705882 = 1.588235294 V
    b. At high voltage : 16 V in * 0.294705882 = 4.235294118 V
    c. Each step = 0.004887585 V for 1 digital value <- using analogRead() From 5 V / 1023
    d. At low voltage - 6 V in : 1.588235294 / 0.004887585 = 324 <> 325
    e. At high voltage - 16 V in : 4.235294118 / 0.004887585 = 866 <> 867

And the rest, use map() to manipulate the data optain from analogRead()

That about it... 8)

Does anyone know the impedance of the analog pins?

Does anyone know the impedance of the analog pins?

I was just looking that up to answer another question... I didn't find a direct answer, but I found this from section 26.6.1 of the ATmega datasheet:

...The ADC is optimized for analog signals with an output impedance of approximately 10k? or less....

Table 28-8, Rain Analog input resistance 100 M?.

DVDdoug:

Does anyone know the impedance of the analog pins?

I was just looking that up to answer another question... I didn't find a direct answer, but I found this from section 26.6.1 of the ATmega datasheet:

...The ADC is optimized for analog signals with an output impedance of approximately 10k? or less....

I only found that, as well, but the threads where I found that info indicated that such a high impedance source will not work well if you make rapid succession reads of different pins.

Other data I found was very confusing, saying you can't actually make fast sequential reads of different pins, no matter what. I have a project already that is about to use sequential reading of different pins. Not exactly thrilled with this. It could be that lower impedance sources will work ok. Hope so.

You really want the source impedance to be significantly less than 10k if you want any accuracy on the readings.
Higher values will "work", but the AVR ADC pins have significant leakage into or out of the pin (direction unspecified) so pick your divider resistors so that the bottom resistor is around 4.7k or lower.

Some applications can't handle that much load on what you're measuring, and there you'll need an Opamp or some other circuit to buffer the input and provide the lower impedance that the AVR needs to give accurate readings.

I've been using the AVR for a long time now, in ASM and C, and I'm used to seeing ADC readings accurate within 1-2 counts with properly designed hardware driving them.

But, if you don't need much accuracy then the above is overkill. YMMV and all that.

but the AVR ADC pins have significant leakage into or out of the pin (direction unspecified) so pick your divider resistors so that the bottom resistor is around 4.7k or lower.

In that case, in my example, use 7.5 K for R1 ( top) and 2.7 K for R2 ( bottom ). Same ratio

Some test code that may have some parts of interest.

// arduino IDE 1.0
// for W5100 ethernet shield
// the IP address will be dependent on your local network/router
// port 80 is default for HTTP, but can be changed as needed
// use IP address like http://192.168.1.102:84 in your brouser
// or http://zoomkat.no-ip.com:84 with dynamic IP service
// use the \ slash to escape the " in the html
// meta refresh set for 2 seconds

#include <SPI.h>
#include <Ethernet.h>

int x=0; //set refresh counter to 0
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,102); // ip in lan
EthernetServer server(84); //server is using port 84

void setup()
{
  // start the server
  Ethernet.begin(mac, ip);
  server.begin();
}

void loop()
{
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
     while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        // see if HTTP request has ended with blank line
        if (c == '\n') {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          //meta-refresh page every 2 seconds
          x=x+1;  //page upload counter
          client.println("<HTML>");
          client.print("<HEAD>");
          client.print("<meta http-equiv=\"refresh\" content=\"2\">"); 
          client.print("<TITLE />Zoomkat's meta-refresh test</title>");
          client.print("</head>");
          client.println("<BODY>");
          client.print("Zoomkat's meta-refresh test IDE 1.0");
          client.println("
");
                    
          client.print("page refresh number ");
          client.println(x); //current refresh count
          client.println("
");
          client.println("
");
          
          client.print("Zoomkat's arduino analog input values:");
          client.println("
");
          client.println("
");
          
          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(analogRead(analogChannel));
            client.println("
");
            }
           break;
          client.println("</BODY>");
          client.println("</HTML>");
         }
        }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}