Auto-Ranging Ohmmeter does not read 150K ohm? SOLVED

I recently built an auto-ranging ohmmeter from simple-circuit.com, which I modified to work with an Oled display. It works accurately enough to sort resistors from 10 ohms to 1Meg ohms, but for some reason does not measure 150K ohms?? 100k and 220k read fine. I double checked all the calibration resistor values and wiring and found no faults. I was wondering if there was something in the code which is causing this problem. Can someone check the attached code for me?

// SKETCH: Ohmmeter_Autoranging_SimpleCircuitcom.ino
// Autoranging Ohmmeter with 128x64 Oled display with Arduino Pro-Mini
// SH1106 Oled display driven with I2C via A4=SDA & A5=SCK.
// Adaped from: https://simple-circuit.com/arduino-auto-ranging-ohmmeter-lcd/

#include <Arduino.h>
#include <U8g2lib.h>  // Oled library

// Define digital pins to enable pnp transistors through 4k7 ohm base resistor
#define CH0  12       // Q1 - 100 ohm range
#define CH1  11       // Q2 - 1k ohm range
#define CH2  10       // Q3 - 10k ohm range
#define CH3  9        // Q4 - 100k ohm range
#define CH4  8        // Q5 - 2 Meg ohm range

byte ch_number;
uint32_t res;
const uint32_t res_table[5] = {100, 1000, 10000, 100000, 2000000};  // resistor table array
char _buffer[11];

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup(void) {
  //Serial.begin(9600);                         // initialize the Serial display
  u8g2.begin();                               // initialize the I2C Oled display
  u8g2.clearDisplay();
  u8g2.setFont(u8g2_font_lubB14_tf);          // Set Lucida 14 font
  u8g2.drawStr(10, 20, " < OHM >");           // write to the internal memory
  u8g2.drawStr(10, 40, "< METER >");          // write to the internal memory
  u8g2.setFont(u8g2_font_luRS10_tf);          // Set Lucida 10 font
  u8g2.drawStr(0, 60, " by: f.w.mclennan");   // write to the internal memory
  u8g2.sendBuffer();                          // transfer internal memory to Oled
  // NOTE: Millis can only be used once per sketch like this:
  while (millis() < 4000) {                   // Hold Oled display 4 seconds
  }
  u8g2.clearDisplay();                        // clear the display
  u8g2.clearBuffer();                         // clear the internal memory

  // Set digital pins as outputs
  pinMode(CH0, OUTPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);
  pinMode(CH3, OUTPUT);
  pinMode(CH4, OUTPUT);

  // pinMode(A1, INPUT);  // Set analog (adc) as input - Not needed?

  ch_number = 4;
  ch_select(ch_number);
}

void loop() {
  // Read ADC voltage across unknown resistor (digital value between 0 and 1023
  uint16_t volt_image = analogRead(A1) + 1;

  if (volt_image >= 550 && ch_number < 4) {
    ch_number++;
    ch_select(ch_number);
    delay(50);
    return;
  }

  if (volt_image <= 90 && ch_number > 0) {
    ch_number--;
    ch_select(ch_number);
    delay(50);
    return;
  }

  if (volt_image < 900) {
    // This equation computes the value of the unknown resistor
    float value = (float)volt_image * res / (1023 - volt_image); // value is unknown resistor in ohms
    if (value < 1000.0)
      sprintf(_buffer, "%03u.%1u Ohm ", (uint16_t)value, (uint16_t)(value * 10) % 10);
    else if (value < 10000.0)
      sprintf(_buffer, "%1u.%03u Kohm", (uint16_t)(value / 1000), (uint16_t)value % 1000);
    else if (value < 100000.0)
      sprintf(_buffer, "%02u.%02u Kohm", (uint16_t)(value / 1000), (uint16_t)(value / 10) % 100);
    else if (value < 1000000.0)
      sprintf(_buffer, "%03u.%1u Kohm", (uint16_t)(value / 1000), (uint16_t)(value / 100) % 10);
    else
      sprintf(_buffer, "%1u.%03u Mohm", (uint16_t)(value / 1000000), (uint16_t)(value / 1000) % 1000);
  }

  else
    sprintf(_buffer, "<INFINITY>");     // Measuring probes open circuit

  u8g2.setFont(u8g2_font_lubB14_tf);    // Set Lucida 14 font
  u8g2.clearBuffer();
  u8g2.setCursor(5, 30);
  u8g2.print(_buffer);
  u8g2.sendBuffer();

  // Serial.println(_buffer);
  // Serial.println();
  delay(1000);   // update readings once per second
}

void ch_select(byte n) {
  switch (n) {
    case 0:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 1:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 2:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 3:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, LOW);
      digitalWrite(CH4, HIGH);
      break;
    case 4:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, LOW);
  }
  res = res_table[n];
}  // end of code.



Why " + 1" ?

Yes, and why 900?

1 Like

Not sure, why they added a 1, maybe to make it 1024 from 1023?
Anyway, the meter reads the same without it, excepting the 150k ohm issue. If two 150k resistors are put in parallel, it reads bang on 75k, but stays in Infinity mode with either one individually??

I'm just a beginner programmer, but also wondered why they only compute the float value in the < 900 if statement in line 74. Looks like all the high readings from "A1" were filtered out.

Can you mesure resistors in the range of 120-190k (you can compose from two or three in series or parallel if you have no such).
If you get the same result as for 150k I can say you what is the reson.

If "volt_image" is supposed to be in the range "1..1024", then the part of the equation that reads "(1023 - volt_image)" is a bug. I would remove the "+ 1" from the analogRead line (yes, it makes a dfference). Try to put 2x75k in series and check if the issue persists.

Yes, this is a bug, but it will not create problems, because only values up to 900 are processed

  if (volt_image <900) {
    ...
  }

Nobody knows why analogRead (A1) + 1.

const uint32_t res_table[5] = {100, 1000, 10000, 100000, 2000000};  // resistor table array

I'd make sure the constants that are > 16bits are declared long:

const uint32_t res_table[5] = {100, 1000, 10000, 100000ul, 2000000ul};  // resistor table array

I don't think this is necessary here, but its a habit that will prevent you from being bitten in the future for 16 bit int boards...

You are failing to re-read the analog pin after changing the range, so its out-of-step with the value of res.

Yes, same result!
I'm all ears.... :sunglasses:

I removed +1 and changed 1023 to 1024, but still does not read between 120k & 190 as Boffin mentioned. :sunglasses:

The whole problem is in the algorithm for automatic switching between the third and fourth ranges (channels).
Third range: R1 = 100kΩ, fourth range: R1 = 2MΩ.
If the measured resistance R2 = 150kOhm, then
the Arduino DAC outputs 614 on the third range, 71 on the fourth.
The switching algorithm is as follows

  if(volt_image >= 550 && ch_number < 4) {
    ch_number++;
    ch_select(ch_number);
    delay(50);
    return;
  }
 
  if(volt_image <= 90 && ch_number > 0) {
    ch_number--;
    ch_select(ch_number);
    delay(50);
    return;
  }

The program loops and constantly switches from the 3rd range to the 4th and vice versa.
There will be the same from about 117k to 190k
Auto-range Ohmmeter

The simplest solution is to change the 2MΩ resistor to 1MΩ and the corresponding number in the program. In this case, the maximum measured resistance will decrease from 14.5 MΩ to 7.25 MΩ. I think this is quite enough.

Ok, I added the ul's for to start my good habits. Thanks, it did not make a difference as you said.

Where should I put the analog read function? In multiple places?
uint16_t volt_image = analogRead(A1);

Ok, I totally understand the problem as you outlined it in such great detail. Great!
I'm glad I used sockets for the cal resistors. Changed the 2M for 1M and the constant table from 2000000ul to 1000000ul and PROBLEM SOLVED!
Many Thanks BOFFIN!!

Now I can order the 1% Cal resistors!

I'm glad that I was able to help you solve the problem, although it took a little thought for this. :slight_smile:
Please tell us later what results you achieved after calibration.
Don't forget to click Solved. :slight_smile:

Yes, I did click Solution button, and added SOLVED to title.
It may be months before I get the 1% resistors, but will certainly post again after their installed.
Many thanks. :sunglasses:

There may be a minor issue with accuracy as I could not use the AREF as the Arduino Pro-Mini does not have one. It may be better to use an UNO or NANO board. Here is my final working code. Have fun!

// SKETCH: Ohmmeter_Autoranging_SimpleCircuitcom.ino
// Autoranging Ohmmeter with 128x64 Oled display with Arduino Pro-Mini
// SH1106 Oled display driven with I2C via A4=SDA & A5=SCK.
// Adaped from: https://simple-circuit.com/arduino-auto-ranging-ohmmeter-lcd/

#include <Arduino.h>
#include <U8g2lib.h>  // Oled library

// Define digital pins to enable pnp transistors through 4k7 ohm base resistor
#define CH0  12       // Q1 - 100 ohm range
#define CH1  11       // Q2 - 1k ohm range
#define CH2  10       // Q3 - 10k ohm range
#define CH3  9        // Q4 - 100k ohm range
#define CH4  8        // Q5 - 1 Meg ohm range

byte ch_number;
uint32_t res;
const uint32_t res_table[5] = {100, 1000, 10000, 100000ul, 1000000ul};  // resistor table array
char _buffer[11];

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup(void) {
  // Serial.begin(9600);                         // initialize the Serial display
  
  // analogReference(EXTERNAL);  // I did not use AREF as Pro-Mini does not have it
  // ***Add this after wiring AREF to 5 volt, and before analogRead(A1)

  u8g2.begin();                               // initialize the I2C Oled display
  u8g2.clearDisplay();
  u8g2.setFont(u8g2_font_lubB14_tf);          // Set Lucida 14 font
  u8g2.drawStr(10, 20, " < OHM >");           // write to the internal memory
  u8g2.drawStr(10, 40, "< METER >");          // write to the internal memory
  u8g2.setFont(u8g2_font_luRS10_tf);          // Set Lucida 10 font
  u8g2.drawStr(0, 60, " by: f.w.mclennan");   // write to the internal memory
  u8g2.sendBuffer();                          // transfer internal memory to Oled
  // NOTE: Millis can only be used once per sketch like this:
  while (millis() < 4000) {                   // Hold Oled display 4 seconds
  }
  u8g2.clearDisplay();                        // clear the display
  u8g2.clearBuffer();                         // clear the internal memory

  // Set digital pins as outputs
  pinMode(CH0, OUTPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);
  pinMode(CH3, OUTPUT);
  pinMode(CH4, OUTPUT);

  ch_number = 4;
  ch_select(ch_number);
}

void loop() {
  // Read ADC voltage across unknown resistor (digital value between 0 and 1023
  // uint16_t volt_image = analogRead(A1) + 1;
  uint16_t volt_image = analogRead(A1);  // Seems to work OK without the +1 (above)

  if (volt_image >= 550 && ch_number < 4) {
    ch_number++;
    ch_select(ch_number);  // Execute void ch_select (below)
    delay(50);
    return;
  }

  if (volt_image <= 90 && ch_number > 0) {
    ch_number--;
    ch_select(ch_number);  // Execute void ch_select (below)
    delay(50);
    return;
  }

  if (volt_image < 900) {
    // uint16_t volt_image = analogRead(A1);
    // This equation computes the value of the unknown resistor (changed 1023 to 1024)
    float value = (float)volt_image * res / (1024 - volt_image); // value is unknown resistor in ohms

    if (value < 1000.0)
      sprintf(_buffer, "%03u.%1u Ohm ", (uint16_t)value, (uint16_t)(value * 10) % 10);
    else if (value < 10000.0)
      sprintf(_buffer, "%1u.%03u Kohm", (uint16_t)(value / 1000), (uint16_t)value % 1000);
    else if (value < 100000.0)
      sprintf(_buffer, "%02u.%02u Kohm", (uint16_t)(value / 1000), (uint16_t)(value / 10) % 100);
    else if (value < 1000000.0)
      sprintf(_buffer, "%03u.%1u Kohm", (uint16_t)(value / 1000), (uint16_t)(value / 100) % 10);
    else
      sprintf(_buffer, "%1u.%03u Mohm", (uint16_t)(value / 1000000), (uint16_t)(value / 1000) % 1000);
  }

  else
    sprintf(_buffer, "<INFINITY>");     // Measuring probes open circuit

  u8g2.setFont(u8g2_font_lubB14_tf);    // Set Lucida 14 font
  u8g2.clearBuffer();
  u8g2.setCursor(5, 30);
  u8g2.print(_buffer);
  u8g2.sendBuffer();

  // Serial.println(_buffer);
  // Serial.println();
  delay(1000);   // update readings once per second. Default was 500
}

void ch_select(byte n) {
  switch (n) {
    case 0:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 1:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 2:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, HIGH);
      break;
    case 3:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, LOW);
      digitalWrite(CH4, HIGH);
      break;
    case 4:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, HIGH);
      digitalWrite(CH4, LOW);
  }
  res = res_table[n];
}  // end of code.

If you are concerned about accuracy, take a look at transistors Q1-Q5. These are bipolar transistors, their collector-emitter saturation voltage can be significant (0.4-0.5V), especially in the low-resistance range.
If this will be a problem, you can try replacing them with a p-MOSFET with a low gate threshold voltage.

Yes, good idea. I only have 2N7000 "N" Fets here, so would need to order P-types. For me, the accuracy is good enough, but I may experiment with fets down the road. :sunglasses:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.