Yesterday I spent around 3 hours putting together the pH and EC circuits on a breadboard.
The pieces of the prototype circuit consist of some sections of the circuit that I milled on the Othermill, a few components on SMT-> DIP boards, and Adafruit’s ADS1015 BoB. All…gulp…wired together….if I don’t breathe…the circuit works.
I then set up a DMM, a scope and an I2C logic analyzer to test.
I hard code the EC probe’s resistance value by using a 200Ω resistor. To be more exact, the resistor actually measures 198Ω. This way I’ll know if my tests can get reasonably close to the resistor value. Recall from previous EC tests that to calculate the EC:
- measure VIN and VOUT in order to calculate the Gain from the EC probe’s resistance. I.e.: Gain = 1+ VOUT/VIN
- calculate the resistance and conductance read from the EC probe: The layout specifies a 1K feedback resistor. So R(measured) = 1K/Gain-1 and the EC value in Siemens is 1/R(measured)
to test, I put together an Eclipse project. The project is located at this GitHub location (the ADS1015_Test.Zip). I decided zipping up Eclipse project at different working states is an easy way to start testing since all the include files, code, makefile, debugging options… changes are there.
Test 1: Measure VIN and VOUT using ADS1015_Test Project
Recall VIN is the DC peak value determined from the Wien Bridge Oscillator. VOUT is the DC peak value determined from the gain loop in the EC circuit. The VOUT is amplified by how much the EC Probe (in this case the 200Ω resistor) amplifies the VIN signal.
For this test, I will “hard code” the MUX.
When the MUX pin = GND, the signal to the ADS1015 is the VIN. When the MUX pin = power source (in this case I am testing with a 3.3V power source), the signal sent to the ADS1015 is VIN.
I start with the MUX pin connected to GND.
I use the debugger and open an Jlinkrttclient session within terminal. As shown above, the value I got from the ADS1015 for VIN = 2012. I had set the ADS1015’s resolution (also called perhaps confusingly the gan) to GAIN_FOUR, which means each LSB of the ADC = .5 mV. So VIN = 2012*.5 = 1006mV. Hmmm…I was expecting a value of around 200mV. Something is not right….
Moving the MUX pin to VDD, I rerun the test program and get VOUT = 2028.
The value of 2028*.5 = 1.014V is pretty much what I was expecting for VOUT.
I should be seeing VIN close to 200mV and VOUT close to 1000mV. What’s going on with VIN? The first debug test I’ll do is to look at the values going into the ADS1015 AIN1 pin. This is the pin for VIN and VOUT after rectification (I discuss all this in detail in previous posts. It takes me a bit longer than I want at this point to back link to these older posts…). So the question I am answering is: What values does the ADS1015 receive as input prior to sending them over to the nRF51822 via I2C?
- VGND = 1.8
- VIN = 1.92 – 1.8 = .12V
- VOUT = 2.8 – 1.8 = 1V
- VIN = 318*.5 = 159mV
- VOUT = 318*.5 = 159mV
Looking at the ADS1015 data sheet (p 8 on the data sheet I’m looking at)
- byte 1 to Address 0x90 -> ADS1015 address (see this link for how to figure out the ADS1015 address from 0x90). Data = 0x01-> write to the config register
- byte 2= 0xA7 = b1010 0111->MSB of what to write to the config register
- byte 3 -> 0x83 = b1000 0011-> LSB of what to write to the config register
p. 15 of the data sheet has the map to what the config write bytes mean:
Looking at the explanation of the MSB config bytes (p. 16)
MSB = b1010 0111
- Single shot reading (bit 15)
- AINp = AIN1 and AINn = AIN3 (bits 14-12)
- programmable gain = +/1 1.024V (GAIN_FOUR) (bits 11-9)
- Power-down single-shot device operating mode (bit 8)
so far so good. Now onto the LSB = b1000 0011
- 1600SPS datarate – the default. (bits 7-5)
- Default comparator mode (bit 4)
- Default comparator polarity (bit 3)
- Default latching comparator (bit 2)
- Disable comparator (bits 1-0) – which I assume means bits 4-2 are ignored.
- byte 4 -> write to the ADS1015, data = 0x00 which means write to the register that contains results (the “Conversion register”)
- byte 5 -> 0x5F – the MSB byte of the ADC reading
- byte 6 -> 0xE0 – the LSB byte
// Read the conversion results
uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
if (m_bitShift == 0)
// Shift 12-bit results right 4 bits for the ADS1015,
// making sure we keep the sign bit intact
if (res > 0x07FF)
// negative number – extend the sign to 16th bit
res |= 0xF000;
- use a uint16_t to store the value that comes back from the ADS1015
- check if the 12th bit is a 1 (i.e.: 0x7FF = b0111 1111 1111…anything above 0x7FF – say 0x800 – has a 1 in the most significant bit, making is a negative value. So extend negative throughout the 16 bits and return an int16_t
After working through a few more results, I’m thinking the challenge is actually the rectifying part of the circuit. In the prototype I take a shortcut and make a rectifier with just an opamp, diode, and cap. I wanted to try this over the more complicated FET solution I was using. However, I’m thinking because the MUX causes such a fluctuation between signal switching, the FET solution is what I should use. The results I am seeing is most likely an artifact of the rectifier since the signals are switching correctly.
Well – that was a good test session. I got a much better feel for the meaning between the I2C packets. I also validated the API is returning the same results as what I2C is returning and the configuration settings of the ADS1015 have been set correctly.
Now I’ll rebuild the rectifier circuit to go back to the one on the Ladybug Shield.
Thanks for reading this far. Please find many things to smile about.