Raspberry Pi ADC (measure analog voltages) using ADS1115

The raspberry pi only has digital inputs. With a capacitor and careful timing you can use that to measure analog voltage, but not with any temporal accuracy. Here I'm using an addon board (using an ADS1115 chip) which can take 860 samples a second with up to 15 bits of resolution.

Steps:

(1) set up i2c bus to connect to ADC

https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c

(2) characteristics of ADS1115 discussed here

http://www.bristolwatch.com/rpi/ads1115.html

(3) This link describes the wiring in pretty good detail and suggests that it's better to power the chip using 3.3v not 5v.

https://leanpub.com/rpcanalog/read#leanpub-auto-analog-to-digital-conversion-adc

(4) The actual wiring I used (breadboard):

https://alantechreview.blogspot.com/2020/04/photodiode-wiring-example-and-resources.html

(5) and now on the pi with the ADS1115 hooked up (5v power). black tube contains a photodiode

100k resistor is hidden inside the blue tape under the ADC board.



Here's some C code, stolen and modified. 

#include
#include
#include
#include
#include     // read/write usleep
#include     // exit function
#include   // uint8_t, etc
#include // I2C bus definitions

#include

long long microsec() { // since epoc
    struct timespec spec;
    clock_gettime(CLOCK_REALTIME, &spec);
    return ((long long) spec.tv_sec *1000000LL) + (spec.tv_nsec / 1.0e3); // Convert nanoseconds to microseconds (1.0e6 would give milliseconds)
}

// Setup to use ADC0 single ended

int fd;
int asd_address = 0x48; // Note PCF8591 defaults to 0x48!
int16_t val;
uint8_t writeBuf[3];
uint8_t readBuf[2];
float myfloat;

const float VPS = 4.096 / 32768.0; // volts per step

/* Signal Handler for ^C */
void sigint_handler(int sig_num)
{
    printf("\n User provided signal handler for Ctrl+C \n");
  // power down ASD1115
  writeBuf[0] = 1;    // config register is 1
  writeBuf[1] = 0b11000011; // bit 15-8 0xC3 single shot on
  writeBuf[2] = 0b10000101; // bits 7-0  0x85
  if (write(fd, writeBuf, 3) != 3) {
    perror("Write to register 1");
    exit (1);
  }
    exit(0);
}

/*
The resolution of the ADC in single ended mode 
we have 15 bit rather than 16 bit resolution, 
the 16th bit being the sign of the differential 
reading.
*/

int readval()
 {

  if (read(fd, readBuf, 2) != 2)  // read conversion register
{
  perror("Read conversion");
  exit(-1);
    }
    return readBuf[0] << 8 | readBuf[1]; // could also multiply by 256 then add readBuf[1]
 }

int main() {

 signal(2, sigint_handler); // runs until ^c,  this allows ADC cleanup 

 nice(-20); // as high priority as possible

    // open device on /dev/i2c-1 
  if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {
    printf("Error: Couldn't open device! %d\n", fd);
    exit (1);
  }

  // connect to ADS1115 as i2c slave
  if (ioctl(fd, I2C_SLAVE, asd_address) < 0) {
    printf("Error: Couldn't find device on address!\n");
    exit (1);
  }

  // set config register and start conversion
  // AIN0 and GND, 4.096v, 128s/s
  // Refer to page 19 area of spec sheet
  writeBuf[0] = 1; // config register is 1
//                76543210 
  writeBuf[1] = 0b11000010; //  continuous, ANC0,  amp gain 
  // bit 7 flag bit for single shot not used here
  // Bits 6-4 input selection:
  // 100 ANC0; 101 ANC1; 110 ANC2; 111 ANC3
  // Bits (3-1) Amp gain. Defaults to 010, here we use 001, see P.19
  // Bit 0 Operational mode of the ADS1115.
  // 0 : Continuous conversion mode
  // 1 : Power-down single-shot mode (default)
                //76543210
  writeBuf[2] = 0b11100101; // bits 7-0  0x85
  // Bits 7-5 data rate default to 100 for 128SPS. 0=8, 111=860
  // Bits 4-0  comparator functions see spec sheet.

  // begin conversion
  if (write(fd, writeBuf, 3) != 3) {
    perror("Write to register 1");
    exit (1);
  }

  sleep(1);


  // set pointer to 0
  readBuf[0] = 0;
  if (write(fd, readBuf, 1) != 1) {
    perror("Write register select");
    exit(-1);
  }
  
long long t;
int count = 0;

int buf[100];
float delta[100];
t = microsec();
while (count < 100)
  {
  buf[count] = readval();
  delta[count]= (microsec()-t)/1000.0;
  count++;
  }
for (int i = 0; i < 100; i++)
  {
  printf("Raw %i \t@ %3.3fms \t volts: %3.3f\n",buf[i], delta[i], buf[i]*VPS );
  } // end while loop

  close(fd);

  return 0;
}


Comments

Email me

Name

Email *

Message *

Popular posts from this blog

Panasonic TH-42PX75U Plasma TV review: input lag and upscaling tested using the piLagTesterPRO

piLagTester PRO order page

A $5 TV Input Lag tester using a Raspberry Pi Zero