Autoranging ohmmeter

Note: Before i explain my issue i would like to note that I am a beginner in electronics. Please pay attention to that when providing solutions and explanations. Thank you.

I have succeeded at making an auto ranging Ohmmeter it is still ofcours experimental there for I would like to get some opinions and perhaps solutions to some of the issues I’m facing How it works:

It is based on a simple voltage divider. After experimenting it seems that a voltage drop across the unknown resistor in a certain range (this case [3.135V, 0.99V] with a voltage source equal to 3.3V) gives the most precise results compared to other values.

This Ohmmeter has a very useful feature and that is auto ranging, it changes the range automatically when the voltage drop values are not in the previously mentioned interval ([3.135V, 0.99V]). The accompanied image shows how the device works.

If the value of Rx is too small compared to (R1+R2+R3) the Arduino switches the analog reading from A0 to A1 if the problem still accurse the Arduino will change again to A2 meaning it is now measuring not the value of Rx but (Rx+R1+R2) in series after that the Arduino is programmed to do the subtraction accordingly.

This device presents some noticeable inconvenients that need to be eliminated:

1. It depends on experimental values, so it is not certain if it’ll function properly in different conditions.

2. For accurate (still not exact) measurements the value of Rx has to be greater than R1

3. It provides acceptable measurements compared to the actual value with an error that is not too big (less than 1% relative error) the precision was obtained by the use of some of the arduino functions (such as voltage reference) also I have chosen the voltage source to be 3.3V instead of 5V to reduce noise, now if one wishes to use a different microcontroller other than arduino will it still be possible to obtain the same precision.

I would like to know your professional opinion regarding these inconvenients and if you may solutions for them, I have accompanied the code for better understanding.

Thank you

``````#include <ResponsiveAnalogRead.h>

int raw1= 0,raw2=0,raw3=0;
int Vin= 3.3;
float Vout1= 0,Vout2=0,Vout3=0;

float R1= 10000,R2=100000,R3=1000000;
float Rx= 0;
float buffer= 0;

void setup()
{
analogReference(EXTERNAL);

Serial.begin(9600);
}

void loop()
{
analogPin0.update();
analogPin1.update();
analogPin2.update();
raw1=analogPin0.getValue();
buffer= raw1 * Vin;
Vout1= (buffer)/1024.0;
if(Vout1<=3.135&&Vout1>=0.99) //firstif
{

buffer= (Vin/Vout1) -1;
Rx= (R1+R2+R3) * buffer;
Serial.print("Vout1: ");
Serial.println(Vout1);
Serial.print("Rx: ");
Serial.println(Rx);
delay(1000);
}
else //else1 of first if
{

raw2=analogPin1.getValue();
buffer= raw2 * Vin;
Vout2= (buffer)/1024.0;
if(Vout2<=3.135&&Vout2>=0.99) //second if of first else
{
buffer= (Vin/Vout2) -1;
Rx= (R2+R3) * buffer;
Rx=Rx-R1;
Serial.print("Vout2: ");
Serial.println(Vout2);
Serial.print("Rx: ");
Serial.println(Rx);
delay(1000);
}
else
{
raw3=analogPin2.getValue();
buffer= raw3 * Vin;
Vout3= (buffer)/1024.0;
if(Vout3<=3.135&&Vout3>=0.99)
{
buffer= (Vin/Vout3) -1;
Rx= R3 * buffer;
Rx=Rx-(R1+R2);
Serial.print("Vout3: ");
Serial.println(Vout3);
Serial.print("Rx: ");
Serial.println(Rx);
delay(1000);
}
}
}
}
``````

======================================================

to jot here are the results as i have previously explained the ohmmeter worked pretty well at the beginning but when i turned it off Then back on the value measured changed, the error got much bigger i tried changing the caliber aka R1 from 220 to 100 but the results were the same, the error was still big thank you

• Make the variable `Vin` a float. The range is limited by the number of analog inputs and the resistors are in series. It will be hard to adapt the circuit to measure a resistor of 10 ohm. It is possible to use digital pins to power resistors or not and use a single analog input. – Jot Sep 13 '18 at 23:37

The answer by @EdgarBonet seems to be a very nice solution for the lowered output voltage of a pin, when a low resistor value is used.

A arduino uno pin can output 20mA without problem, even up to 40mA if needed. So a safe value to measure low resistor values is 220 ohm, which could result in a maximum of 23mA.

Analog input A1 is used both as digital output to power the circuit and also as a analog input to measure how much the voltage has dropped.

This is a quick test:

``````// Using the idea in the answer by Edgar Bonet
//
// Allow that the voltage of a digital output is
// lowered by the low resistor value,
// to calculate the unknown resistor.
//
// With two resistors in series.
// A1 - 220 ohm - A0 - 10 ohm - GND
//
// The circuit is a voltage divider.
// A1 powers the voltage divider.
// A0 measures the output.

#define R1 220
#define N_SAMPLES 10

void setup() {
Serial.begin(9600);
pinMode(A1,OUTPUT);
digitalWrite(A1, HIGH);
}

void loop() {

// Pin A1 is already set as a digital output and set HIGH.
// Using analogRead(A1) is to measure the real output
// voltage. There is a voltage drop, because of the current.
Serial.print("Vin=");
Serial.print(Vin);

Serial.print(" Vout=");
Serial.print(Vout);

if(Vin > 0.0 && Vout < Vin) {
float Vt = Vout / Vin;
float Rx = (Vt * R1) / (1 - Vt);
Serial.print(" Rx=");
Serial.println(Rx);
}

delay(500);
}

int total = 0;
for(int i=0; i<N_SAMPLES; i++) {
}
return float(total) / N_SAMPLES;
}
``````

That is very nice. It is no problem when the unknown resistor is 10 ohm.

Because of the low resistor values, I did not add delays. With high resistor values, some delay is needed before measuring the analog values.

The next step is to find the best value by finding the value that is the most in the middle, as Edgar Bonet has explained in his answer.

``````// Autoranging by finding the value that is most in the middle.
// Using the answer by Edgar Bonet

#define N_SAMPLES 10          // the number of samples to average, maximum 30

// Four resistors to power the circuit (one at a time).
// A1 has a resistor of 220 ohm, A2 has 3k3, A3 47k, and A4 750k
// The other leg of the resistors are all connected to A0.
// The unknown resistor Rx is at pin A0 and GND.
const float R[4] = {220.0, 3300.0, 47000.0, 750000.0};
const int pinR[4] = {A1, A2, A3, A4};
const unsigned long wait[4] = {1, 2, 4, 10};  // delay, values guessed.
const int pinMeasure = A0;

void setup() {
Serial.begin(9600);
Serial.println("Autoranging");
}

void loop() {
float Vin[4];    // Vin is the pin that powers the circuit
float Vout[4];   // Vout is the voltage over the unknown resistor

for(int i=0; i<4; i++) {
// Power the circuit with this resistor
pinMode(pinR[i], OUTPUT);
digitalWrite(pinR[i], HIGH);

// Vin is the pin that powers the circuit.
// This is always low impedance, no need to wait.

// Extra delay to wait for the voltage to become steady,
// before reading the analog data.
// Might be needed for the higest resistor values
// Perhaps there is a cable to the unknown resistor
// that has capacitance.
delay(wait[i]);

// The first 'analogread()' sets the multiplexer.
// For a high impedance circuit, the first
// 'analogread()' might be less acurate.
// Read it a few times, and throw it away.

// Everything is settled by now.

pinMode(pinR[i], INPUT);  // Make this pin high impedance
}

// Find the value for Vout that is the most in the middle.
// For an Arduino Uno, the middle ADC value is 512.
float diff = 2000.0; // set to value higher than possible as default
int best = 0;        // index of the best resistor

for(int i=0; i<4; i++) {
float d = fabs(512.0 - Vout[i]); // difference for this resistor
if(d < diff) {                   // closer to the middle?
diff = d;                      // remember new difference
best = i;                      // remember index best resistor
}
}

if(Vin[best] <= 0.0) {
Serial.println("Something wrong with the circuit");
}
else if(Vout[best] >= Vin[best]) {
Serial.println("Rx not connected");
}
else {
float Vt = Vout[best] / Vin[best];     // temporary precalculation
float Rx = (Vt * R[best]) / (1 - Vt);  // calculate unknown resistor

Serial.print(" Rx=");
Serial.print(Rx);
Serial.print(" ohm (with resistor ");
Serial.print(R[best]);
Serial.print(")");
Serial.println();
}

delay(250);
}

int total = 0;
for(int i=0; i<N_SAMPLES; i++) {
}
total += N_SAMPLES / 2;  // see www.gammon.com.au/adc
return(float(total) / float(N_SAMPLES));
}
``````

I would like to add a capacitor from A0 to GND (parallel with the unknown resistor) to reduce noise. This capacitor should be 1nF or less. The delay in the code is large enough to wait for the voltage to settle with 1nF.

The increase of the resistors by 15 times is a large step. That makes it less accurate, but the arduino uno has only 6 analog inputs. By using 5 resistors with an increase of 6 times is better.
4 resistors 15x : 220, 3k3, 47k, 750k
5 resistors 6x : 220, 1k2, 8k2, 47k, 270k

There are a few ways to do more:

• When two resistors result in a output that is close to the middle, it is possible to average the results.
• When the 5 resistors are used, it is for example possible to lower the value of the 8k2 resistor. When all higher values are turned on, the resulting resistor value becomes 6k8 instead of 8k2.
• For high resistor values, the output voltage of a pin is not lowered, and digital outputs can be used as well.

An protection resistor for A0 might be needed. I think that a value of 470 ohm or 1k should be fine.

Many thanks to @EdgarBonet, I used his answer and added the average and the delay.

Update November 2018

When using a breadboard, there can be contacts which are not perfect. That can cause a lot of troubles.

Are you using a circuit as this:

• Wow this is pretty impressive i see what you did right there one small issue that still worries me, EdgarBonet previously mentioned "the pins have an output resistance that is about 25 Ω, which ends up being in series with the corresponding reference resistors. You can account for that by adjusting, in your code, the values of those references." i wonder why you didn't take this detail in consideration when you wrote this code?, thank you – Rekaia Draoui Sep 15 '18 at 16:19
• I did! Not the 25 ohm, but I measure the actual voltage drop. Edgar Bonet wrote "That's why I suggest to use analog instead of digital pins" (to be able to measure the voltage drop of a pin). I measure the voltage of the pin that powers the circuit and the voltage over the unknown resistor. The `analogRead` of pin a1 could be 840 (instead of 1023) and the a0 could be 37. The actual voltage does not matter. With the two resistors, everything is relative. Because it is a voltage divider, the unknown resistor is measured relative to the 220 ohm resistor. The calculation is just two lines. – Jot Sep 15 '18 at 16:29
• OH! true i missread both of your answers well thank you big time! i will improve it and post it here as an answer once im done if anybody was interested, thank you again – Rekaia Draoui Sep 15 '18 at 16:56
• i have another question, i've tried ur circuit in a simulator bcz my pc decided to stop all of a sudden, anyway when i chose a high value reference resistor(say 100k) vin was equal to 1024 so 5v unlike a low value reference resistor(say 220)where vin was about 824 can you please explain to me why is that ? – Rekaia Draoui Sep 16 '18 at 22:40
• Because of the output resistance of 25 ohm, that Edgar Bonet was talking about. The atmega328 can output 20mA (even up to 40mA), but with such high currents the pin is no longer Vcc (5.0v), but lower. With very small current, it has no problem to set a pin to Vcc (5.0v). That is what my sketch is for. To test the accuracy by actually measuring the output pin with a analog pin. Since the arduino uno has a digital pin and a analog pin combined for all analog inputs, it is possible to use that single pin. Did you choose reference resistors? You can set them apart by 15 times: 220, 3k3, 47k, 750k. – Jot Sep 17 '18 at 3:56

As you probably already know, the standard setup for measuring a resistance with an Arduino is to put the unknown resistor in series with a known one between 5V and GND, and measure the voltage at the point in-between them:

``````     5V
│
R₁
A0 ─┤
R
│
GND
``````

where R is the unknown resistor and R1 is the known reference. The resistance is then computed as:

``````const float R1 = ...;  // reference resistance

``````

First, it is worth noting that the reference voltage does not appear in this calculation. You could, of course, do the calculation in terms of voltage, but you should then notice that the reference voltage can be simplified away. This is not surprising if you realize than the ADC of the Arduino does not actually measure a voltage: it rather measures a voltage ratio: the ratio of the input voltage to the reference voltage. This ratio can then be easily related to the resistance ratio R/R1. What all this implies is that you should not expect to get better accuracy by using a 3.3 V reference rather than 5 V. Any inaccuracy in the voltage reference is irrelevant to the measurement of the resistance.

Next, when measuring a physical quantity, you are almost always interested in the relative rather than the absolute accuracy. From the equation above, it can be easily shown that you get the best relative resolution when the reading is near the middle of the ADC range:

``````raw reading    │   64 │  128 │  256 │  512 │  768 │  896 │  960
───────────────┼──────┼──────┼──────┼──────┼──────┼──────┼──────
resolution (%) │ 1.67 │ 0.89 │ 0.52 │ 0.39 │ 0.52 │ 0.89 │ 1.67
``````

This is consistent with your observations, and it means that the reference resistor should ideally have a value that is of the same order as the unknown resistor.

Now, for the autoranging, I would advise you against putting all your reference resistors in series. With this setup, you always have the same voltage across the unknown resistor, irrespective of what range you choose. Reading a different input does not help. You may find out that the voltage between R1 and R2 gives a better reading (closer to mid-range), and you get a better accuracy on the measurement of R+R1. However, when you subtract R1 you are immediately degrade the relative accuracy of the result. In the end you cannot really improve the accuracy this way.

What I suggest is to put the reference resistors in parallel instead of in series, and power each one from an analog pin:

``````     A1  A2  A3
│   │   │
R₁  R₂  R₃
A0 ─┼───┴───┴── ...
R
│
GND
``````

You then use these analog pins in digital mode: all of then in `INPUT` mode, except one which is set to `OUTPUT` `HIGH`. You can try all of them in turn and select whichever gives a result closer to mid-range. Or you can implement a smarter algorithm that finds the optimal one without trying them all.

There is something to be aware of with this setup: the pins have an output resistance that is about 25 Ω, which ends up being in series with the corresponding reference resistors. You can account for that by adjusting, in your code, the values of those references. But then that output resistance is not very well known, and can vary with temperature. This should only be an issue when you are in the low ranges though. You can mitigate this problem by measuring the voltage at the pin that is currently pulling up, and replacing `1024` in the equation for R by that reading. That's why I suggest to use analog instead of digital pins.

Finally, there are a couple of limitations that you should be aware of when measuring resistances with the Arduino ADC. At the low end of the resistance range, you cannot use a reference resistor with a very low value, because you have to make sure you do not pull more than about 20 mA from the pin that pulls up. Your smallest reference may then end up being somewhere around 220 Ω, and you will loose significant accuracy when you try to measure anything below 50 Ω. At the upper end of the range, you should be aware that the ADC is meant to measure voltage sources with an output resistance no larger than 10 kΩ, otherwise you risk cross-talk between the ADC channels. Then your higher reference should probably be 10 kΩ, and any measurement above 50 kΩ will give poor accuracy. Going past these limitations would require extra electronics, like a good-quality op-amp to “condition” the signal into something that the ADC can reliably measure.

• The 50kΩ upper limit is on the safe side. I have no problem with 1M. That requires the average of a number of samples and sometimes an extra delay or a capacitor to gnd (to keep the voltage the same when the adc eats some charge). When for example only 5% accuracy is required for the very low and very high resistor values, then a lot is possible. – Jot Sep 14 '18 at 20:59
• thank you for your answer it has served me alot, as a last question i have felt after reading your answer that my project doesn't seem to be well made and that is of course due to my lack of knowledge in this field now, do you think it is worth fixing or should i jump to another way? it doesn't metter how it works as long as it is eventually an autoranging ohmmeter, thank you @EdgarBonet – Rekaia Draoui Sep 14 '18 at 23:29
• @RekaiaDraoui Your circuit has too many limitations. Use this circuit with turning on a resistor one at a time. Find in the sketch which value is the most in the middle and use that to calculate the unknown resistor. – Jot Sep 15 '18 at 7:10
• I don't quite get your last question. Can you easily change the circuit, or is it something you have soldered? – Edgar Bonet Sep 15 '18 at 12:48
• @EdgarBonet yes i can easily change the circuit – Rekaia Draoui Sep 15 '18 at 16:05