I'm trying to read data from an ADS1115 ADC attached to the I²C link of my Raspi 3B using pigpiod, but get nothing. In order to pin down the error, I took a deep look at the config I write beforehand into the ADC's config register 0x01 to find out that the data, which are effectively stored in the register, are apparently not the ones I had sent to the device. This happens during the init of the device:

int Initialize (int handle)
  int      ret            = 0;
  uint16_t adc_config     = 0;
  int      pigpiod_handle = handle;

  cout << "Initializing display and dimmer ADC...";

  i2c_handle = i2c_open (pigpiod_handle, 1, 0x48, 0);

  if (i2c_handle < (unsigned int) 0) {
    ret = errno;

    cout << "failed! Error: " << strerror (errno) << endl;
  else {
    uint16_t reg = 0x0000;

    SetADCConfigBitfield (&adc_config, 0x8000, 0x8000); // Conversion: single.
    SetADCConfigBitfield (&adc_config, 0x5000, 0x7000); // Input: AIN1.
    SetADCConfigBitfield (&adc_config, 0x0200, 0x0E00); // Gain: ± 4096 mV.
    SetADCConfigBitfield (&adc_config, 0x0000, 0x0100); // Mode: continuous.
    SetADCConfigBitfield (&adc_config, 0x0000, 0x00E0); // Data rate: 8 samples / second.
    SetADCConfigBitfield (&adc_config, 0x0000, 0x0010); // Comparator mode: traditional.
    SetADCConfigBitfield (&adc_config, 0x0000, 0x0008); // Comparator polarity: active low.
    SetADCConfigBitfield (&adc_config, 0x0000, 0x0004); // Comparator latching: off.
    SetADCConfigBitfield (&adc_config, 0x0003, 0x0003); // Comparator queue: off.

    cout << "¤¤ " << std::bitset<16> (adc_config) << " ¤¤" << endl;
    ret += i2c_write_byte_data (vfd::pigpiod_handle, vfd::i2c_handle, 0x01, adc_config >> 8);
    ret += i2c_write_byte_data (vfd::pigpiod_handle, vfd::i2c_handle, 0x01, adc_config  & 0xFF);

    reg  = i2c_read_byte_data (vfd::pigpiod_handle, vfd::i2c_handle, 0x01) << 8;
    reg |= i2c_read_byte_data (vfd::pigpiod_handle, vfd::i2c_handle, 0x01);
    cout << "±± " << std::bitset<16> (reg) << " ±±" << endl;

    if (ret == 0) {
      cout << "done. Handle: " << vfd::spi_handle << "." << endl;
    else cout << "ADC initialization failed! Error: " << ret << "." << endl;

  return ret;

void SetADCConfigBitfield (uint16_t *config, uint16_t value, uint16_t mask)
  *config &= ~mask;
  *config |= (value & mask);

I took SetADCConfigBitfield () and the various parameter values directly from the driver written by Elektor Labs (which I can't use because of incompatibilities). Here is the relevant part of my app's output:

Initializing display and dimmer ADC...
¤¤ 1101001000000011 ¤¤
±± 1000010110000101 ±±
done. Handle: 1.

As you can see, when I read the register immediately after writing, I get completely different data. What is the reason for this? Am I doing anything wrong?

UPDATE Here is the link to the datasheet. See pp. 28–29: ADS111x datasheet

  • 1
    refer to the datasheet ... never expect the read data to be the same as the written data, unless the datasheet says so ... writing a control register and reading a status register involves two different registers even if both are at the same address
    – jsotola
    Dec 25 '20 at 2:11
  • I have already read the datasheet, and it only says about bit #15 (the leftmost one) to have different meanings in write and read operations.
    – Neppomuk
    Dec 25 '20 at 21:55

OK, I've found out what had been wrong here: the two i2c_write_byte_data () methods from pigpio. They send the following signals to the corresponding I²C slave:

Start Address WriteBit [A] Register [A] Payload [A] Stop

So, after the first byte has been written, a stop bit is emitted, followed by a start bit for the second byte. The ADC looks for a 2-byte value, not for two 1-byte ones.

In fact, i2c_write_word_data () would have been the right choice, as this method writes both bytes in one operation. But: It writes the lower half-word (bits #7 to #0) first, followed by the upper half-word (bits #15 through #8). And this is not what the ADC wants, because it expects the upper half-word first.

My solution is to swap the upper with the lower half:

reg = (adc_config << 8) | (adc_config >> 8);
ret = i2c_write_word_data (vfd::pigpiod_handle, vfd::i2c_handle, 0x01, reg);

Now everything works.

  • 1
    Thank you for posting this. The reversed order in i2c_write_word_data had completely escaped me, and once I saw this everything worked in a program I was doing.
    – Linwood
    May 16 '21 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.