ADC read inconsistent results... could do with a clue... RESOLVED

I have 3 i2c devices on my bus.

1 at 0x48 is an ADC (ADS 1115)
1 at 0x50 is a FRAM
1 at 0x68 is an RTC (Chronodot)

They all say "Hi" when polled initially. The Chronodot and FRAM are working fine. But the ADS for some reason is reading out garbage values, the same garbage values (modulo a little jitter) every time. They are the kind of values you get if nothing is connected to the port.

However, if I load a simpler sketch which ONLY reads out the ADS, it starts working.

It gets worse. I can read the same port on the ADS twice and get completely different results.

int adcget(uint8_t which) {
  
  int16_t foo, adc0, adc1, adc2, adc3;

  if (which > 3) {return(-999);}   // whatever
  
  adc0 = ads.readADC_SingleEnded(0);
  adc1 = ads.readADC_SingleEnded(1);
  adc2 = ads.readADC_SingleEnded(2);
  adc3 = ads.readADC_SingleEnded(3);
  
  Serial.print("AIN0: "); Serial.println(adc0);  // these readings all look reasonable
  Serial.print("AIN1: "); Serial.println(adc1);  // this one's connected to the pot & val changes with pot 
  Serial.print("AIN2: "); Serial.println(adc2);
  Serial.print("AIN3: "); Serial.println(adc3);
  
  delay(200);
  
  foo = ads.readADC_SingleEnded(1);   // repeat same read of adc1
  
  Serial.print("AIN  "); Serial.print(which); Serial.print(" : "); Serial.println(foo);
  
  return(foo);
  
}

output

AIN0: 3135
AIN1: 10395   // I cranked the pot up
AIN2: 3153
AIN3: 3153
AIN  1 : 3150  // but foo never changed

AIN0: 3136
AIN1: 10394
AIN2: 3152
AIN3: 3152
AIN  1 : 3158

AIN0: 3129
AIN1: 17         // I cranked the pot down
AIN2: 3132
AIN3: 3132
AIN  1 : 3145  // but foo hardly changed

Surely "foo" and "adc1" should really be the same value.
But reliably, adc1 is correct and foo is wrong. foo always looks like a disconnected port.

As you can see, I was in the midst of making the function take an arg and read a selected port.
But when it failed, I brought in the basic test code from the Adafruit example sketch. Imagine my surprise when the 4 reads worked fine, even in the same context where my single read was failing.

Now, it's late and I'm beat and it would not surprise me if I was doing something stupid :slight_smile: I have been staring at these few lines for far too long.

But I cannot see the difference between one ads.readADC_SingleEnded(1) and another.

Is this something about the ADC, or something about my code? Until I put the Adafruit example lines into the function I thought I was looking at an i2c error. Now I'm clueless, which probably won't surprise anyone since that seems to be my default state...

I'm missing the link to the ADC library you're using.

Sounds like a similar setup as in your other thread, might be the same problem (bus misbehavior caused by too high capacitance/wrong pull-up).

Hi pylon, thanks for suggestion. The library and device initialisation:

#include <Adafruit_ADS1015.h>

Adafruit_ADS1115 ads;  /* Use this for the 16-bit version */

Now it is true that if you carelessly init the device as the wrong version (1015 when it should be 1115) you get very similar garbage (damhikt); but as you see from output above I am not getting garbage on line 1 on the first read. I always get good data on first read, garbage on the 2nd read. The garbage looks just like a floating port, but I know the pot connection is solid.

So... I have read some stuff (that was mostly a bit over my head) suggesting that ADCs have an internal timing loop (they poll their ports and convert the analog voltages into digital values) and it's possible for a sufficiently fast mcu to catch them in an unstable state between polls. The adafruit sample code has a 1 sec (!!!) delay between reads of the ADC, but I assumed this was to make the Serial.print output scroll slowly for readability.

So I put a 200ms delay between my first and second reads of port 1 on my ADC in the hope that it might "settle" between reads, but it doesn't seem to help.

There is nothing asynchronous going on here, I am not using ISRs, just a big main loop. So I would not expect any activity on the i2c bus except what I am explicitly commanding from the master...

I did run for a while w/o any pullups -- being unsure what value to use for a 3.3v i2c bus. For 5v I recall 4K7 is recommended. Maybe half of that for 3.3v? but that would be guessing on my part as I don't have the EE savvy to do the math. Everything worked w/o pullups except that darned adc.

I do see that the i2c bus voltage is a bit low (that is, Due doesn't think it sees digital 1 on those pins) but the other devices seem to be talking successfully to the master.

2.7K was a value I heard mentioned for pullups on a 3.3v i2c bus. So I tried pulling up sda and scl via a 2.2k (I didn't have anything else in the 2k range). Due still thinks SDA and SCL are low. interesting.

I got out my trusty dvm and checked the 3.3vdc bus and I see 3.291 which seems acceptable. Next step I guess is the pocket scope :frowning:

I have 3 little mysteries and they all point to i2c bus: 1) phantom device 0x7c found by i2c scanner, 2) Due thinks that SDA and SCL are low even when bus is idle (even with pullups in place), 3) inconsistent reads from i2c adc device. What's surprising is that the other two i2c devices are working great!

Photo of setup coming soon.

slightly annotated image of breadboard setup

i2c scanner peculiarity:

 if (!(digitalRead(20) && digitalRead(21))) {
   Serial.println("ERROR, SDA or SCL is low");
   Serial.print("SDA:  ");  Serial.print(digitalRead(20));  Serial.print("  SCL:  ");
   Serial.println(digitalRead(21));
 }

[... usual i2c scanning routine ...]

output:

ERROR, SDA or SCL is low
SDA:  0  SCL:  0
Scanning...
I2C device found at address 0x48  !
I2C device found at address 0x50  !
I2C device found at address 0x68  !
Unknown error at address 0x7C
done

Despite the Due's complaint that its SDA and SCL pins are low, it's able to talk to the devices (except for the weirdo ADC which it can only talk to "every other time"). I need to write Yet Another Sketch to call out that specific failure.

I call this sketch "ADCthrash" :slight_smile:

  int16_t adc0, adc1, adc2, adc3;

  int16_t i=0;
  for (i=0; i< 4; i++) {
    adc0 = ads.readADC_SingleEnded(i);
    delay(100);
    adc1 = ads.readADC_SingleEnded(i);
    delay(100);
    adc2 = ads.readADC_SingleEnded(i);
    delay(100);
    adc3 = ads.readADC_SingleEnded(i);
    delay(100);
    Serial.print(i); Serial.print("read0: "); Serial.println(adc0);
    Serial.print(i); Serial.print("read1: "); Serial.println(adc1);
    Serial.print(i); Serial.print("read2: "); Serial.println(adc2);
    Serial.print(i); Serial.print("read3: "); Serial.println(adc3);
    Serial.println(" ");
  
    delay(500);
  }

On pin 1 of this ADC there is a pot wiper, and the pot is powered by 3.3v so the value only reaches about half the ADC's max value of 32K.

Now watch this output!

0read0: 3125
0read1: -1
0read2: -1
0read3: -1

1read0: -1                  // -1?  really?
1read1: 3126
1read2: 3119
1read3: 3127
 
2read0: 3120
2read1: 3131
2read2: 3121
2read3: 3137
 
3read0: 3117
3read1: 3154
3read2: 3125
3read3: 3145
 

0read0: 3121
0read1: 14245
0read2: 15753
0read3: 17361
 
1read0: 17569     // I cranked the pot up.  but look backward and you see pin 0 cranking up too!
1read1: 3148       // now the pot value has disappeared from pin 1
1read2: 3121
1read3: 3128
 
2read0: 3122
2read1: 3145
2read2: 3126
2read3: 3131
 
3read0: 3123
3read1: 3138
3read2: 3133
3read3: 3133
 

0read0: 3129
0read1: -1
0read2: -1
0read3: -1
 
1read0: -1             // pot down to zero again (-1?) and pin 0 seems to be participating.
1read1: 3120
1read2: 3127
1read3: 3118
 
2read0: 3122
2read1: 3121
2read2: 3127
2read3: 3124
 
3read0: 3132
3read1: 3129
3read2: 3144
3read3: 3123
 

0read0: 3146
0read1: 5626
0read2: 5869
0read3: 6240
 
1read0: 6580             // slowly cranking the put up and again pin 0 seems to be dragged along with 1
1read1: 3131             // but after pin 1 is read out, the value disappears!  even after a delay!
1read2: 3123
1read3: 3119
 
2read0: 3131
2read1: 3123
2read2: 3132
2read3: 3121
 
3read0: 3138
3read1: 3127
3read2: 3147
3read3: 3124
 

0read0: 3147
0read1: 10120
0read2: 10319
0read3: 10525
 
1read0: 10752        // once again pin 0 seems to be ramping up towards the pin 1 value
1read1: 3136
1read2: 3127
1read3: 3118
 
2read0: 3124
2read1: 3128
2read2: 3123
2read3: 3130
 
3read0: 3129
3read1: 3133
3read2: 3129
3read3: 3137
 

0read0: 3129
0read1: 17572
0read2: 17572
0read3: 17572
 
1read0: 17572          // pot left at max.  what is the pin 1 value doing on pin 0?
1read1: 3148            // and why does it disappear instantly on next read?
1read2: 3123
1read3: 3124
 
2read0: 3121
2read1: 3134
2read2: 3122
2read3: 3138
 
3read0: 3121
3read1: 3145
3read2: 3121
3read3: 3148
 

0read0: 3122
0read1: 13779
0read2: 13149
0read3: 12494
 
1read0: 11936                    // turning pot down again
1read1: 3141
1read2: 3120
1read3: 3128
 
2read0: 3119
2read1: 3134
2read2: 3119
2read3: 3132
 
3read0: 3122
3read1: 3146
3read2: 3130
3read3: 3136
 

0read0: 3127
0read1: -1
0read2: -1
0read3: -1
 
1read0: -1                     // pot at zero value
1read1: 3118
1read2: 3127
1read3: 3118
 
2read0: 3126
2read1: 3124
2read2: 3132
2read3: 3123
 
3read0: 3132
3read1: 3128
3read2: 3144
3read3: 3125

The "precognition" of pin 0 is really bizarre.

Do I just have a completely bogus ADS1115? I do have spares, I can try another instance of the breakout board.

I am now utterly confused. When I first started fooling with this ADC breakout I used the Adafruit diagnostic/test sketch which reads all the pins just once, once per second. I didn't see my pot value changing on the pin I thought it was attached to. I saw it changing on pin 1 if it was plugged into pin 0. So I did a bit of experimenting and concluded that the silk screen was wrong.

But maybe the silk screen was not wrong. Maybe the first read from any of these registers always fails? Maybe Pin 0 really is Pin 0. But you have to read it twice to get a real value. And if you go on reading it you get updated values. But when you read Pin 1 for the first time, you get garbage left behind by the last read from Pin 0, and... (maybe there's a way out of this labyrinth!) ... stay tuned...

void loop() 
{
 int16_t adc0, adc1, adc2, adc3;

 int16_t i=0;
 for (i=0; i< 4; i++) {
 adc0 = ads.readADC_SingleEnded(i);
 delay(10);
 adc1 = ads.readADC_SingleEnded(i);
 delay(10);
 Serial.print(i); Serial.print("...read0 (bogus): "); Serial.println(adc0);
 Serial.print(" ...read1 (real ): "); Serial.println(adc1);
 }
 Serial.println(" ");

 delay(1000);
}

This new version of the thrash loop reads each port twice.

Watch this output:

0...read0 (bogus): 3165      // my pot is really on A0 set to min
 ...read1 (real ): -1             // second read gets it, why -1 I have no idea.  s/b 0
1...read0 (bogus): -1          // notice 1st read of pin 1 is same as 2nd read of pin 0
 ...read1 (real ): 3121
2...read0 (bogus): 3160
 ...read1 (real ): 3142
3...read0 (bogus): 3165
 ...read1 (real ): 3155
 
0...read0 (bogus): 3175
 ...read1 (real ): -1
1...read0 (bogus): -1
 ...read1 (real ): 3132
2...read0 (bogus): 3162
 ...read1 (real ): 3142
3...read0 (bogus): 3158
 ...read1 (real ): 3141
 
0...read0 (bogus): 3161   // OK now I am cranking up the pot
 ...read1 (real ): 3800      // 2nd read catches the real value
1...read0 (bogus): 3857   // and 1st read of pin 1 also seems to sample pin 0
 ...read1 (real ): 3134
2...read0 (bogus): 3169
 ...read1 (real ): 3152
3...read0 (bogus): 3172
 ...read1 (real ): 3158
 
0...read0 (bogus): 3169
 ...read1 (real ): 6089      // I keep cranking the pot up
1...read0 (bogus): 6124   // pin 1 is tracking pin 0 on the first read
 ...read1 (real ): 3133      // but reading correctly as floating on the 2nd read
2...read0 (bogus): 3163 
 ...read1 (real ): 3133
3...read0 (bogus): 3167
 ...read1 (real ): 3154
 
0...read0 (bogus): 3180
 ...read1 (real ): 8012         // crank crank
1...read0 (bogus): 8047
 ...read1 (real ): 3147
2...read0 (bogus): 3159
 ...read1 (real ): 3144
3...read0 (bogus): 3163
 ...read1 (real ): 3140
 
0...read0 (bogus): 3161
 ...read1 (real ): 9979
1...read0 (bogus): 10022
 ...read1 (real ): 3144
2...read0 (bogus): 3159
 ...read1 (real ): 3141
3...read0 (bogus): 3173
 ...read1 (real ): 3159
 
0...read0 (bogus): 3170
 ...read1 (real ): 13918
1...read0 (bogus): 13946
 ...read1 (real ): 3149
2...read0 (bogus): 3160
 ...read1 (real ): 3138
3...read0 (bogus): 3163
 ...read1 (real ): 3147
 
0...read0 (bogus): 3170
 ...read1 (real ): 16808
1...read0 (bogus): 16808
 ...read1 (real ): 3160
2...read0 (bogus): 3160
 ...read1 (real ): 3148
3...read0 (bogus): 3165
 ...read1 (real ): 3146
 
0...read0 (bogus): 3162
 ...read1 (real ): 17573           // max pot value
1...read0 (bogus): 17573 
 ...read1 (real ): 3145
2...read0 (bogus): 3165
 ...read1 (real ): 3146
3...read0 (bogus): 3170
 ...read1 (real ): 3165
 
0...read0 (bogus): 3178
 ...read1 (real ): 17572
1...read0 (bogus): 17572
 ...read1 (real ): 3158
2...read0 (bogus): 3164
 ...read1 (real ): 3138
3...read0 (bogus): 3161
 ...read1 (real ): 3139
 
0...read0 (bogus): 3169
 ...read1 (real ): 17573
1...read0 (bogus): 17573
 ...read1 (real ): 3155
2...read0 (bogus): 3170
 ...read1 (real ): 3149
3...read0 (bogus): 3163
 ...read1 (real ): 3148
 
0...read0 (bogus): 3162
 ...read1 (real ): 14120       // now I am turning the pot down
1...read0 (bogus): 14050    // and pin 1 is now reading a few counts lower than pin 0
 ...read1 (real ): 3145
2...read0 (bogus): 3159
 ...read1 (real ): 3143
3...read0 (bogus): 3159
 ...read1 (real ): 3155
 
0...read0 (bogus): 3185
 ...read1 (real ): 12574
1...read0 (bogus): 12500
 ...read1 (real ): 3150
2...read0 (bogus): 3159
 ...read1 (real ): 3141
3...read0 (bogus): 3154
 ...read1 (real ): 3142
 
0...read0 (bogus): 3164
 ...read1 (real ): 7680
1...read0 (bogus): 7596
 ...read1 (real ): 3139
2...read0 (bogus): 3165
 ...read1 (real ): 3149
3...read0 (bogus): 3171
 ...read1 (real ): 3157
 
0...read0 (bogus): 3166
 ...read1 (real ): 2669
1...read0 (bogus): 2559
 ...read1 (real ): 3129
2...read0 (bogus): 3162
 ...read1 (real ): 3133
3...read0 (bogus): 3162
 ...read1 (real ): 3150
 
0...read0 (bogus): 3171
 ...read1 (real ): -1             // pot at zero again
1...read0 (bogus): -2
 ...read1 (real ): 3132
2...read0 (bogus): 3164
 ...read1 (real ): 3143
3...read0 (bogus): 3162
 ...read1 (real ): 3140
 
0...read0 (bogus): 3157
 ...read1 (real ): -1
1...read0 (bogus): -1
 ...read1 (real ): 3122
2...read0 (bogus): 3156
 ...read1 (real ): 3145
3...read0 (bogus): 3173
 ...read1 (real ): 3162

This has had me going in circles. When I first tested the thing, I hung the pot on one port and did a loop reading all four ports while twiddling the pot. I connected the pot to A0 but saw A1 changing as I twiddled, so I thought the silk screen on the board was wrong! When I moved the pot to A1 I saw A2 changing. So I thought the silk was really wrong.

Now I see that (for whatever reasons) if I am monitoring a voltage on port 0, when I read port 0 the first time I get garbage; on the 2nd read I get the real value. If I then read port 1, I get the updated value from port 0. If I read port 1 again, I get the real reading from port 1. And so on.

So for my code to work it will have to read any given port on the ADC twice in a row to be sure of getting a sensible value.

Maybe this all makes sense if you know how ADCs really work. All I need to know is that the 2nd read is the one that works.

A strange postscript to all this: I tried reading each port 3x. When I do this, I get:

Port A0, read 1:  bogus data
Port A0, read 2:  good data
Port A0, read 3:  good updated data
Port A1, read 1:  good data?
Port A1, read 1:  good data?
Port A1, read 1:  good data?

So when I do three reads instead of two, I don't get the bizarro effect of the data from port 0 being "copied" to port 1. What is magic about three reads? Search me. If anyone has a good, rational explanation for all this I'd be interested to know more. But for the moment, knowing that you have to "knock twice and ask for Charlie," I can keep moving ahead with my project...

i2c scanner peculiarity:

You cannot read the pins with digitalRead while they are used for I2C.

slightly annotated image of breadboard setup

Which shows 4 boards connected to I2C, is one a level converter? If yes, what do you need it for?

Post links to the schematics of all the boards! I guess some have already pull-ups installed, so you might have a too low pull-up resistor value resulting in a too high current to sink for the devices.

The ADS1115 has a RDY pin that can be configured to go high once the conversion is finished. The 8ms the library waits for the conversion might be too fast. I would first try to increase that value to 10ms and if that doesn't help, connect the RDY pin (pull-up resistor needed) to a free pin and update the library to check that pin before reading the conversion result register.

@pylon thanks for the insights and suggestions!

The logic level converter is for the serial connection to the Nextion. The Nextion is emphatically a 5v device, and a power hog at startup; it needs its own p/s. So to connect it to Serial1 on the Due, I used a logic level converter allegedly rated for i2c, which I figured would be plenty fast enough for Serial at 19200.

You said, You cannot read the pins with digitalRead while they are used for I2C.
This is interesting because I used this code on the Leonardo-based version of my gizmo, and it seemed to work. With the bus idle, I would check for True on the SCL and SDA pins, to make sure my pullups were working and I was getting a solid high level with no traffic. So... I was fooling myself? that reading did not mean what I thought it meant? I just got dumb-lucky?

What if I did that level check before the Wire.begin()? would that properly verify the pullups?

The modules (except the adc) are Adafruit products. The schematics are...

Chronodot
http://docs.macetech.com/doku.php/chronodot_v2.0#datasheet
(no board schematic but text says NO PULLUPS installed)

FRAM
https://learn.adafruit.com/assets/16793
if I am reading it right, it has 10K pullups to vcc

ADC board, who knows, it's a Cheap and Cheerful Chinese device.
I pulled out a spare and I don't see any resistors on the SDA and SCL traces.
They go into a tiny SMD component with "103" label on it.

So it looks like the only pullups would be the internal ones on the Due's SDA and SCL, which I think are automagically enabled when the Wire library is initialised, but I could be remembering that wrong... and the 10Ks on the FRAM if I am reading that schematic right.

I also read last night that the ads 1015 is "faster" than the 1115. Since I surely don't need 16 bits (12 would be more than adequate, all I wanted was to get rid of the outrageous noise on the Due's own ADCs) might I be better off with the 1015?

This is interesting because I used this code on the Leonardo-based version of my gizmo, and it seemed to work. With the bus idle, I would check for True on the SCL and SDA pins, to make sure my pullups were working and I was getting a solid high level with no traffic. So... I was fooling myself? that reading did not mean what I thought it meant? I just got dumb-lucky?

This works on the AVR platform but not on the Due AFAIK.

What if I did that level check before the Wire.begin()? would that properly verify the pullups?

I guess that should work.

They go into a tiny SMD component with "103" label on it.

That's a 10k SMD resistor.

So it looks like the only pullups would be the internal ones on the Due's SDA and SCL, which I think are automagically enabled when the Wire library is initialised, but I could be remembering that wrong.

That's again on the AVR side. The Due has on-board pull-ups of 1k5 size, so together with the two 10k on the other boards you're on the maximum sink current of about 3mA. If you install additional pull-ups you're above the maximum allowed level and it's hard to predict the results of this.

I also read last night that the ads 1015 is "faster" than the 1115. Since I surely don't need 16 bits (12 would be more than adequate, all I wanted was to get rid of the outrageous noise on the Due's own ADCs) might I be better off with the 1015?

Did you try my library adaptions from the last post? That's easier than to change the hardware, you might get even more problems.

Hi Pylon... oops, errr, as you see I am not familiar with the world of SMD parts. Thanks for letting me know what I was looking at (with a magnifying glass).

The ADS1115 has a RDY pin that can be configured to go high once the conversion is finished. The 8ms the library waits for the conversion might be too fast. I would first try to increase that value to 10ms and if that doesn't help, connect the RDY pin (pull-up resistor needed) to a free pin and update the library to check that pin before reading the conversion result register.

I have not yet tried modifying the library. I guess I should time the "2 read" method and see how many ms that is costing me, then figure out how long it would take to poll the RDY line, etc. I don't care if I have to read it twice (whatever works!), just want to read it by the fastest reliable method :slight_smile:

I have more play time this evening and will try a few tweaks. My "ADCthrash" sketch seems to be a fairly useful tool.

UPDATE: I found and edited the conversion delay from 8 to 10 ms and hey, you are quite right, this seems to solve the problem entirely. I now get 3 stable reads in a row including the first read. And no "print-through" of the previous port onto the next one read. Good call! Thanks!!

I can confirm that changing the delay in Adafruit_ADS1015.h works.

On line 38, from
#define ADS1115_CONVERSIONDELAY (9)
to
#define ADS1115_CONVERSIONDELAY (10)