Pages

Monday, 17 April 2017

Resistomatic - an Arduino-based autoranging ohm-meter

Sorting resistors is a pain


If you've been having fun with electronics for a while you've probably got a box
with bits in. I've been playing with electronics for six decades and I have lots of boxes, many of them containing random collections of resistors.

Resistors are cheap, but I hate waste, so from time to time I decide to empty some of the boxes and put the resistors away in their proper homes.

It ought to be easy, but it's a pain. I was never good at reading colour codes, and the problem has got worse with age. What I want is a simple, quick autoranging ohm-meter that I can use to measure resistor values.

So I'm making resistomatic.

Resistomatic requirements

Most of the resistors I use are 1% or 5% tolerance metal film resistors in the range 220 Ohms to 100K Ohms. Almost all the values  are in the E12 range.
(E12 values are multiples of 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2).

I'll be happy if resistomatic can achieve 5% accuracy over the range 220 Ohms to 100K Ohms. That should allow me to identify the resistors well enough, given that I will have samples of the value and its neighbours to compare it with.

Speed of measurement is not a big issue. If it takes a second or so for resistomatic to measure a resistor, that will be fine.

I don't want to spend a lot of time on the project, and I will only be using resistomatic occasionally. I've an unused Arduino Protoshield sitting in my stripboard box, so I will use that and plug it into an Arduino Uno whenever I need to make a resistance measurement.

My desktop computer has the Arduino IDE installed, so I will upload the Arduino sketch when I need it and view text output in the IDE's serial window.

A simple auto-ranging ohm-meter design using Arduino

A few years back I lashed up a simple version of resistomatic using a very simple approach.

One easy way to measure an unknown resistance is to use a voltage divider. You apply a known voltage across two series resistors, one known, one unknown, and measure the voltage at the junction. (As you'll see later, you'll need to use a number of different known resistors, but for now consider the simple case.)

If you ignore the current flowing through the voltmeter, Ohm's law says that I (the current flowing through the circuit) is 5/(R1+R2).

V (the Voltage across R2) is IR2 or R2*5/(R1+R2).

You know R1, you've measured V, and it's not hard to solve the equation for R2.

V =  5*R2/(R1+R2), so
V*R1 + V*R2 = 5*R2 hence
V*R1 = (5-V)*R2

Rearranging, we have
R2 = V*R1/(5-V) or
R2 = R1/(5/V -1)

Now you can calculate the value of the unknown resistance.

In an ideal world that calculation would be exact, but in reality there are several sources of error.

  1. The  value of R1 will not be exactly known.
  2. The applied voltage will not be exactly 5 volts.
  3. The Voltmeter's input impedance will be finite, so some current will flow through it.
  4. The Voltmeter itself will introduce some measurement error.

If you use the Arduino's 5v output the applied voltage should be accurate to within about 1%. You can use a 1% tolerance resistor for R1. The remaining errors relate to the Voltmeter.

Using an Op-Amp to increase accuracy


Boxes. Yay!
You can use a unity gain operational amplifier to buffer the input from the voltage divider. A unity-gain op-amp has an output that closely follows its input, but it has a high input impedance and a low output impedance.

An op-amp like the Microchip MCP6273 will divert very little current from the voltage divider. The MCP6273 available in an 8-pin DIP package, can run off a 5V supply, and costs about £0.60. Its output can vary over the full range of 0v to 5v. It's ideal for this application, and I had some in my OpAmp box. (You may have guessed by now I have lots and lots of boxes).

With an op-amp buffer the divider circuit now looks like this:



What value of R1 should you use? It turns out that you will get the best accuracy if the value of R1 is close to the value of R2, the resistance you are trying to measure.

Inexpensive commercial test-meters use a rotary switch to select the range used to measure an unknown resistance. You can do better by using the Arduino to connect each of a range of resistors, one at a time, and selecting the calculated value that corresponds to a measured voltage that's closest to 2.5 v.

How can you get the Arduino to connect each resistor in turn? You'll find a range of designs on the web, but one simple one works very well.

If you set one of the Arduino's digital pins to be an input, it has a very high impedance. So you connect a range of known resistors to several of the Arduino's digital pins, set all but one to be inputs, and one to be an output, and set that output high.

You've now applied 5 volts to one end of one resistor, and the other resistors are effectively disconnected.

If you connect the other ends of each known resistor together and connect the unknown resistor between  the common connection and ground, you have a voltage divider like the one described above.

Here's a Fritzing diagram.



The resistors on the right of the diagram are the alternatives for R1. After a bit of experimentation I settled on a range of values from 1K to 10M.

The sketch (Arduino code) to drive resistomatic is fairly simple.

/*
  Resistomatic

  Measures an unknown resistance using a voltage divider.
  Known resistors are attached to digital pins d7-d12
  Their other ends are commoned and connected to one end of the resistor under test.
  The other end of the resistor under test is grounded.

  setup opens the serial port and makes each of d7-d11 an input (so high impedence):
  loop:
     for each of d7-d11:
        make the pin an output
        set the output high
        wait 5 ms for the converter to settle
        read, convert and print the analogue reading
        make the pin an input
     wait 1000 ms


 */


const int analogInPin = A4;  // Analog input
const int PIN_COUNT = 8;
const int SAMPLE_COUNT = 10;

const int pin[PIN_COUNT] = {4, 5, 6, 7, 8, 9, 10, 11};
const float r1[PIN_COUNT] = {1000.0, 3300.0, 10000.0, 33000.0, 100000.0, 330000.0, 1000000.0, 10000000.0};


void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  // send all the pins we'll use low, and then configure them as inputs
  for (int i=0; i<PIN_COUNT; i++) {
     pinMode(pin[i], OUTPUT);
     digitalWrite(pin[i],LOW);
     pinMode(pin[i], INPUT);
  }
}

// take SAMPLE_COUNT succesive readings of the voltage and average them
float averageVoltage() {
  float total = 0.0;
  for (int i=0;i<SAMPLE_COUNT;i++) {
    delay(5);
    total += float(analogRead(analogInPin));
  }
  // convert to voltage and average
  return 5.0*total/(SAMPLE_COUNT*1023.0);
}

// see blog post for details of this caluclation
float calculateResistance(float r, float v) {
   return r/(5.0/v-1.0);
}

void loop() {
   float minimum = 2.5;
   float resistance = 100000000.0; // default value

   /* loop through finding the best estimate
      which we'll get when the measured voltage is closest
      to 2.5v  */
   for (int i=0; i<PIN_COUNT; i++) {
     pinMode(pin[i], OUTPUT);
     digitalWrite(pin[i],HIGH);
     float v = averageVoltage();
     digitalWrite(pin[i],LOW);
     pinMode(pin[i], INPUT);
     float difference = abs(v-2.5);
     if (5.0 > v && difference < minimum) {
        minimum = difference;
        resistance = calculateResistance(r1[i], v);
     }
   } 
   Serial.print("resistance = ");
   Serial.println(resistance);
   delay(3000);
} 

Update


I've transferred the breadboarded version to the Arduino shield. It works well, and will be really useful when I sort out my parts boxes. Here's the finished product:


Code and fritzing diagrams are on GitHub.