ADS1115 bit resolution resolution on 16x gain setting

I am sampling a Kipp and Zonen net radiometer sensor that outputs a +/- 25 mV signal.

I am operating the ADS1115 16-bit A/D converter in 16x gain (+/- 256 mV full scale range), differential mode across channels AN0-AN1.

In this mode, the sensor datasheet specifies 0.0078125 mV/count.
My expectation is that I would get a this resolution across the narrower full scale range.
However, what I get is different, the resolution is 16 counts. For example, when varying the input with a trimmer, the minimum change in output is 16 counts. Is this the expected behavior (resolution is simply gain * LSB, so 16 counts for 16x gain mode? I was expecting the full 16-bit range across the +/-256 mV at 0.0078125 mV/count resolution, but in practice the resolution is 16x higher than this, at 0.125mV /count.

Schematics attached are the radiometer measurement circuit (esp32_ads1115_nrlite2_sch.png) and the trimmer measurement circuit with 0.500V voltage reference on a 20:1 voltage divider (esp32_ads1115_vref_trimmer_sch.png)

#include "Arduino.h"
#include "Wire.h"
#define I2Caddress 0x48

// Running average
//unsigned int readings[20] = {0};
unsigned char readCnt = 0;

void setup()
{
    Serial.begin(115200);

    // Join the I2C bus as a master (call this only once)
    Wire.begin();

    Serial.println("Setup completed.");
}

void loop()
{
    // Step 1: Point to Config register - set to continuous conversion
    Wire.beginTransmission(I2Caddress);

    // Point to Config Register
    Wire.write(0b00000001);

    // Write the MSB + LSB of Config Register
    // MSB: Bits 15:8
    // Bit  15 0=No effect, 1=Begin Single Conversion (in power down mode)
    // Bits 14:12 How to configure A0 to A3 (comparator or single ended)
    // 000 : AINP = AIN0 and AINN = AIN1 (default)
    // 001 : AINP = AIN0 and AINN = AIN3
    // 010 : AINP = AIN1 and AINN = AIN3
    // 011 : AINP = AIN2 and AINN = AIN3
    // 100 : AINP = AIN0 and AINN = GND
    // 101 : AINP = AIN1 and AINN = GND
    // 110 : AINP = AIN2 and AINN = GND
    // 111 : AINP = AIN3 and AINN = GND

    // Bits 11:9 Programmable Gain 000=6.144v 001=4.096v 010=2.048v .... 111=0.256v
    // 000 : FSR = ±6.144 V
    // 001 : FSR = ±4.096 V
    // 010 : FSR = ±2.048 V (default)
    // 011 : FSR = ±1.024 V
    // 100 : FSR = ±0.512 V
    // 101 : FSR = ±0.256 V
    // 110 : FSR = ±0.256 V
    // 111 : FSR = ±0.256 V
    // Bits 8 0=Continuous conversion mode, 1=Power down single shot
    Wire.write(0b01001100);

    // LSB: Bits 7:0
    // Bits 7:5 Data Rate (Samples per second) 000=8, 001=16, 010=32, 011=64,
    // 100=128, 101=250, 110=475, 111=860
    // Bit 4 Comparator Mode 0=Traditional, 1=Window
    // Bit 3 Comparator Polarity 0=low, 1=high
    // Bit 2 Latching 0=No, 1=Yes
    // Bits 1:0 Comparator # before Alert pin goes high
    // 00=1, 01=2, 10=4, 11=Disable this feature
    Wire.write(0b00000011);

    // Send the above bytes as an I2C WRITE to the module
    Wire.endTransmission();

    // ====================================

    // Step 2: Set the pointer to the conversion register
    Wire.beginTransmission(I2Caddress);

    //Point to Conversion register (read only , where we get our results from)
    Wire.write(0b00000000);

    // Send the above byte(s) as a WRITE
    Wire.endTransmission();

    // =======================================

    // Step 3: Request the 2 converted bytes (MSB plus LSB)
    Wire.requestFrom(I2Caddress, 2);

    // Read two bytes and convert to full 16-bit int
    uint16_t convertedValue;

    // Read the the first byte (MSB) and shift it 8 places to the left then read
    // the second byte (LSB) into the last byte of this integer
    convertedValue = (Wire.read() << 8 | Wire.read());

    Serial.println(convertedValue);
    //Serial.print("\t"); //Serial.println(totalReadings / 20);

    delay(250);
}

onegneissguy:
I am sampling a sensor that outputs a +/- 25 mV signal.

when varying the input with a trimmer...

Links and schematic diagram please.
Are the inputs of the ADS within it's common mode range?
Leo..

Thank you for the guidance. I’ve updated the post to include links to support documents as well as a couple of diagrams to show my circuit setup.

Why don't you use a ready made ADS1115 library? Makes life a lot easier, and you know that it is not what causes your problems.

Also configuration of the chip is something you should do once in setup(), not every time in loop().

First diagram shows you have powered the ADS1115 from 5volt.
Not a wise thing to do if you interface it with a 3.3volt processor.
Just power the ADS from the 3.3volt pin of the NodeMCU.

Second diagram shows the ADS used single-ended (15-bit resolution).
Because A1 is grounded (not used).

From the manual: "The thermopile consists of a number of thermocouples connected in series, essentially providing a highly sensitive differential temperature sensor."
Makes me think a thermocouple amplifier (MAX31855) could be more suitable for this.
Leo..

wvmarle:
Why don't you use a ready made ADS1115 library? Makes life a lot easier, and you know that it is not what causes your problems.

Also configuration of the chip is something you should do once in setup(), not every time in loop().

I have used the ADS1115 library provided by adafruit and it yields identical results to the code I have posted, the finest resolution output from the chip is 16 counts. I chose to post this code so that the low level communication, such as bit shifting would be obvious for debugging and discussion rather than posting a long winded and encapsulated library of code.

You are correct, all device config should be in setup, edited OP.

Still searching for an answer to the OP though.

Wawa:
First diagram shows you have powered the ADS1115 from 5volt.
Not a wise thing to do if you interface it with a 3.3volt processor.
Just power the ADS from the 3.3volt pin of the NodeMCU.

Second diagram shows the ADS used single-ended (15-bit resolution).
Because A1 is grounded (not used).

From the manual: "The thermopile consists of a number of thermocouples connected in series, essentially providing a highly sensitive differential temperature sensor."
Makes me think a thermocouple amplifier (MAX31855) could be more suitable for this.
Leo..

Agreed on using 3.3V supply in practice.

The trimmer test circuit was put together for varying a low level output for debugging.
Interesting thought on using the thermocouple amplifier. Many have temperature conversion and cold-junction compensation built in so I don't know how I would deal with that using the radiometer sensor.
Still, any guess on why the A/D outputs a minimum resolution of 16 bits?

onegneissguy:
Still, any guess on why the A/D outputs a minimum resolution of 16 bits?

I think you mean a minimum value of 16 counts.
Don't know. Could be offset of the ADS (in differential mode?), could be your build quality (breadboard?).
Where you ground the 'other' A/D input (ground) is equally important.

Seems that sensor can go negative (soil radiation), so you must bias the -input of the ADS to some fixed positive voltage to accommodate the + input being able to read positive and negative voltages.
That fixed voltage can be mid-voltage of the 3.3volt rail (1:1 voltage divider), but should be 'clean' (smoothing cap).
That will probably also fix your 'missing counts' problem.
Leo..