[Particle Photon] Reading wrong voltage ADC values from ADS 7828 through I2C

I am reading wrong Voltage ADC values from the ADS 7828 board in the particle PHOTONH through I2C channel.

Even I haven’t given any input to AD0 & COM0 lines, I am reading random voltage values between 3.1 - 3.9. Actually, it should return zero values because I haven’t attached any input. Please refer below pics for more details. Actually, I don’t understand why I am reading wrong values through I2C. Please refer below source code and detail explanation of my input. If you guys have any idea on this issue, please provide your suggestions.

NOTE: we found reference of this source from the github community and modified it based our need.

// ADS7828 I2C address:- A0 = 0x49(73) & A1 = 0x4A(74) 
#define Addr 0x49

int raw_adc = 0;
double Current = 0;

void setup()
{
  // Set variable
  Particle.variable("Current", Current);
  
  // Initialize I2C communication as MASTER
  Wire.begin();

  // Initialize serial communication, set baud rate = 9600
  Serial.begin(9600);
  delay(300);
}

void loop()
{
  byte data[2];
  
  // Start I2C Transmission
  Wire.beginTransmission(Addr);

  // Send command byte
  // Single Ended inputs
  // Channel: 0 (zero) selected
  // Internal Reference ON and A/D Converter ON
  Wire.write(0x9C);

  // Stop I2C transmission
  Wire.endTransmission();
  
  // Request 2 bytes of data
  Wire.requestFrom(Addr, 2);
   //Particle.publish("Digital: ", "read two bytes");
  
  // Read 2 bytes of data
  // raw_adc msb, raw_adc lsb
  if(Wire.available() == 2)
  {
    Particle.publish("Digital: ", "after two bytes read");
    data[0] = Wire.read();
    data[1] = Wire.read();
    
    // Output data to dashboard
    Particle.publish("data D0: ", String(data[0]));
    Particle.publish("data D1: ", String(data[1]));

    // Converting the data to 12 bits
    int raw_adc = ((data[0] & 0x0F) * 256) + data[1];
    double Voltage = raw_adc * 0.0012207;
    Particle.publish("Digital: ", String(Voltage));

    // 1 sec delay
    delay(1000);
  }
  else
    Particle.publish("Digital: ", "Not available");
}

I2C READ (AD0 & COM0) or (ADx & COMx)

Output: 3.2 - 3.8 voltage values

========================================================================
1) CHANNEL SELECTION CONTROL
SD C2 C1 C0 CH0 CH1 CH2 CH3 CH4 CH5 CH6 CH7 COM
-----------------------------------------------
1 0 0 0 +IN - - - - - - - –IN

2) POWER DOWN SELECTION
PD1 PD0 DESCRIPTION
— --- ---------------
0 1 Internal Reference OFF and A/D Converter ON (When Power input from I2C)
1 1 Internal Reference ON and A/D Converter ON (When power src from external 12V pwr supply)

NOTE: I used both options but I am getting wrong values/

3) DEVICE ID SELECTION
A1 A0
-----
0 1 -> device ID ‘01’

This is what’s commonly called a floating input and they will typically produce floating readings :wink:

If you have no signal to measure you are virtually measuring stray charges around your system.

Actually, I have tried to read voltage data both these ways, with 3.3v input from the particle and without any input to AD0 & COM0 but I am getting same floating numbers.

Is my jumper settings are wrong on ADS 7828?

Floating is never a good idea.

To check the readings you should try 3.3V, GND and a known voltage divider to get a set of readings for each of them and then see where these readings fall and what deviation you see between them.

Where is that 0.0012207 constant coming from?

I cannot really see whether the pull-up jumpers are set correctly or rotated 90 degrees (which would be wrong).
However, the pull-ups need to be enabled.

BTW, have you tried running an I2C scanner app to see whether the sensor can be found at all?

As @ScruffR pointed out the ADC inputs are going to float if nothing is connected to them. You can use simple 10K resistors to pull the inputs low which should give you a continuous 0 reading.

If you are not certain you have valid communication between the Photon module and the ADS7828 try flashing this I2C Scan firmware on the photon and monitor the Photon’s output:
https://go.particle.io/shared_apps/5ce853938bb3850010b71ccd
This will tell you if the ADS7828 ICs are on the bus and what their addresses are, then you can validate that against the code you are currently running.

2 Likes

Hi Travis & ScruffR,

Thank you so for your suggestions!!

Actually, I have tried all your suggestions but didn’t work any of them. Let me describe you in detail what I have tried.

  1. I have used the code @Travis has suggested to find out that what device IDs are enabled for communication.
    I found two devices are active for the communication:
    - A0=0x49 (73)
    - A1=0x4A (74)
  2. AD0 & COM0 of 0x49 device, I have used to verify value with 10K resister, but still I am receiving floating values.
    • I have checked values of all ADC channels of both 0x49 & 0x4A pannels. Please refer my below code and floating output.
    • One more stuff, even if I gave input (3.3v | 5.0v) or not (0v) to AD0, I was getting similar floating values.

Output of below source code - Make sure I haven’t given any input to AD0 & COM0 so it should show zero values instead of floating number (voltage)


`
3) I have tried all combinations of PD1 & PD0, but there is no change in values.
PD1 PD0 DESCRIPTION
---------------------------------------
0 0 Power Down Between A/D Converter Conversions
0 1 Internal Reference OFF and A/D Converter ON
1 0 Internal Reference ON and A/D Converter OFF
1 1 Internal Reference ON and A/D Converter ON

  1. I have tried both external power source (12V) and BUS power source (I2C 5+V) using power src jumper but there is not change in values.

  2. About the pull up jumper, as CE (ControlEverthing) guys have suggested that if we are using slave CE device (ADS7828) with master CE device (Screw Terminal Breakout Board for Particle Photon) we do not need to use pull jumper. So pull up jumper is not an issue here.

    • @Travis please correct me I am wrong here then.

I really need your help to get it work. So if you have any other suggestion or I have missed anything then please feel free to let me know.

#include <application.h>
#include <spark_wiring_i2c.h>

int raw_adc = 0;
double Current = 0;
void setup()
{
    // Set variable
    Particle.variable("i2cdevice", "ADS7828");
    Particle.variable("raw_adc", raw_adc);
    Particle.variable("Voltage", Voltage);
  
    // Initialize I2C communication as MASTER
    Wire.begin();

    // Initialize serial communication, set baud rate = 9600
    Serial.begin(9600);
    delay(300);
}

void loop()
{
    byte data[2];
    String newDevices = " ";

    // ADS7828 I2C address is 0x49(73): 0100 1001
    // ADS7828 I2C address is 0x4A(74): 0100 1010
    for(int add=0x49; add<=0x4A; add++)
    {
        newDevices = " ";
    
        for (int i=0; i<8; i++)
        {
            // Start I2C Transmission
            Wire.beginTransmission(add);

            byte tmp = i << 4;
            tmp = 0x84 | tmp;
            Wire.write(tmp);
            
            // Stop I2C transmission
            Wire.endTransmission();

            // Request 2 bytes of data
            Wire.requestFrom(Addr, 2);
          
            // Read 2 bytes of data
            // raw_adc msb, raw_adc lsb
            if(Wire.available() == 2)
            {
                //Particle.publish("Digital: ", "after two bytes read");
                data[0] = Wire.read();
                data[1] = Wire.read();

                // Converting the data to 12 bits
                int raw_adc = ((data[0] & 0x0F) * 256) + data[1];
                double Voltage= raw_adc * 0.0012207;

                char datasct[4];
                snprintf(datasct, sizeof(datasct), "%d: ",i);
                newDevices.concat(String(datasct));
                newDevices.concat(String(Voltage));
                newDevices.concat(", ");
            }
            else
                Particle.publish("Digital: ", "Not available");

            delay(100);
        }

        Particle.publish("Data;;", String(newDevices));
    }

}

The image you posted after this shows 7 very constant numbers that seem pretty close to 3.3V to me. I cannot see any variation/floating whatsoever. :confused:

@ScruffR

It seems constant on all 7 channels but I haven’t connected any input (0v) to all those channels. So it should show us zero voltage value, correct?. One more point I want you clear that, if I have connected 10K resister to AD0 channel input then it should shows us different output value, than the other channels (AD1-AD7), right? But it doesn’t show up that difference.

Let me know if you want more information then. By mistake, I added a image with exact same value but, I will show you an example with different values by EOD.

Also, make sure I removed pull-up resister jumper caps and put them besides. So no one has a confusion for that.

@ScruffR
0.0012207 value, which I am multiplying with raw_adc value to get actual voltage value.
In my case, I am using input voltage from 0v to 5v & ADS7828 has a range from 0-4096 (4k = 2^12 => 12-bits ADC board) to represent readings in digital form. So I am using below equation to convert digital raw ADC data to voltage:

Multiplier = (Vin_max - Vin_min)/(ADC_max - ADC_min)

Multiplier = (5 - 0)/ (4096 - 0) = 5/4096 = 0.0012207.

I see - but it should actually be 0…4095, although that won’t make that much of a difference :wink:

Hi,
could you please flash with this:
https://go.particle.io/shared_apps/5eff67e556a45e0025e0ca2e
and see if is any difference ?
The original code is from here, just modified a little to fit in Photon.
As suggested by @ScruffR could you double check with multi meter if your
Pull-ups jumpers are for sure OK on PR33-6 board ?

in your code above is some glitch i’m not sure but…

  1. you iterate over add in for loop and then Wire.requestFrom(Addr, 2);
    (this shouldn’t even compile as Addr is not defined )

  2. delay(100); looks like is to small during Particle.Publish require ad least 1s between publishes

I hope this helps
Regards

Hi @dreamER,

Thank you for your suggestion and sharing your code with us!!

Actually, I have tried your code (used your Original code) but still it gives us wrong output (value = 0).

I have used below configuration to run your code:

// device 1
// Address: A1=0, A0=1
// Command: SD=1, PD1=1, PD0=1
// Scaling: min=0, max=1000
ADS7828 device1(1, SINGLE_ENDED | REFERENCE_ON | ADC_ON, 0xFF, 0, 1000);

// device 2
// Address: A1=1, A0=0
// Command: SD=1, PD1=1, PD0=1
// Scaling: min=0, max=1000
ADS7828 device2(2, SINGLE_ENDED | REFERENCE_ON | ADC_ON, 0xFF, 0, 1000);

As you can see in below pics, I was giving 3.3V input to AD0 & COM0 channel (refer my Multimeter output) but when I ran your code it was giving me zero ‘0’ value as digital output of the 3.3V input (Refer my computer screen).


It is not the correct output, right?

Also you can see jumper setting s of ADS7828 & Particle base board in below pics:

And answer of your questions:

  1. you iterate over add in for loop and then Wire.requestFrom(Addr, 2);
    (this shouldn’t even compile as Addr is not defined )
    Hari: Sorry, It was just a typo mistake I made while I was copying it.

  2. delay(100); looks like is to small during Particle.Publish require ad least 1s between publishes
    Hari: It publish a msg after every 800 ms but not at every 100ms (except it fails to grab 2 bytes - if(Wire.available() == 2)). It my case it never failed to grab 2 bytes of data from ADS 7828.

I hope this clears your all doubts!!

Still, if you have any other suggestion for me, please feel free to let me know.

I am seriously looking for the solution of my this problem.

Thank you again for your inputs!!

Regards,
Harikrishna

Hi,
I scr…ed up completely sorry for that.
I was talking that Particle.Publish require at least 1s and then I’m calling my ParticlePublish function from “for:see_no_evil:, which not gonna work correct ! Could you move delay(1000) from loop (not elegant at all but just for testing purpose) and then place at the end on my function ParticlePublish like here:

void loop()
{
  uint8_t a, ch;

  // update all registered ADS7828 devices/unmasked channels
  ADS7828::updateAll();

  // iterate through device 1..2 channels 0..7
  for (a = 1; a <= 2; a++)
  {
    for (ch = 0; ch < 8; ch++)
    {
      ParticlePublish(ADS7828::device(a)->channel(ch));
    }
  }
  
}

void ParticlePublish(ADS7828Channel* ch)
{
  // device address (0..3)
  // channel ID (0..7)
  // moving average value (scaled)
  // minimum scale applied to moving average value
  // maximum scale applied to moving average value
  
  char StatTemplate[] = "{\"AD\":%d,\", CH\":%d,\", v\":%d,\", mn\":%d,\", mx\":%d}";
  char msg[sizeof(StatTemplate) + 32];
  
  snprintf(msg, sizeof(msg), StatTemplate, ch->device()->address(), ch->id(), ch->value(), ch->minScale, ch->maxScale);
  Particle.publish("All_data", msg, 60, PRIVATE);
 delay(1000);
  
}

Also I just guessing but your device1 is on your right site, you apply input voltage to device2 channel1 (but maybe i’m wrong)

Hi,
sorry for bothering, maybe you already solved this out but if not, could you please try to change for this .ino file in previous app and then try and see if is working ?

/*
  two_devices.ino - example using i2c_adc_ads7828 library
  Library:: i2c_adc_ads7828
  Author:: Doc Walker <4-20ma@wvfans.net>
  Copyright:: 2009-2016 Doc Walker
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/


#include <i2c_adc_ads7828.h>
#include "Particle.h"



// device 1
// Address: A1=0, A0=1
// Command: SD=1, PD1=1, PD0=1
// Scaling: min=0, max=1000
ADS7828 device1(1, SINGLE_ENDED | REFERENCE_ON | ADC_ON, 0xFF, 0, 1000);

// device 2
// Address: A1=1, A0=0
// Command: SD=1, PD1=1, PD0=1
// Scaling: min=0, max=1000
ADS7828 device2(2, SINGLE_ENDED | REFERENCE_ON | ADC_ON, 0xFF, 0, 1000);

const int CHANNELS = 8;
const int DATA = 48;
const int DATA2 = 256;
const int DATA3 = 320;
const int DEVICES = 2;
    
char values[] = {"\"CH%d\":{\"v\":%d,\"mn\":%d,\"mx\":%d}"};
char valuesTemp[CHANNELS][DATA];
    
char devbuild[DEVICES][DATA2] = {"{\"DEVICE%d\":{\"address\":%d,\"values\":{%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}}",
                                 ",\"DEVICE%d\":{\"address\":%d,\"values\":{%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}}}"};
char devbuildTemp[DEVICES][DATA + DATA2];
    
char StatTemplate[DATA3] = {"{\"data\":%s%s}"}; 
char msg[DATA3 + DATA2 + DATA];



void setup()
{
  // enable serial monitor
  //Serial.begin(19200);

  // enable I2C communication
  ADS7828::begin();
}


void loop()
{
  uint8_t a, ch;

  // update all registered ADS7828 devices/unmasked channels
  ADS7828::updateAll();

  // iterate through device 1..2 channels 0..7
  for (a = 1; a <= 2; a++)
  {
    for (ch = 0; ch < 8; ch++)
    {
       snprintf(valuesTemp[ch], sizeof(valuesTemp), values, ADS7828::device(a)->channel(ch)->id(), ADS7828::device(a)->channel(ch)->value(), ADS7828::device(a)->channel(ch)->minScale, ADS7828::device(a)->channel(ch)->maxScale);
      
    }
    snprintf(devbuildTemp[a-1], sizeof(devbuildTemp), devbuild[a-1], a, ADS7828::device(a)->address(), valuesTemp[0],",",valuesTemp[1],",",valuesTemp[2],",",valuesTemp[3],",",valuesTemp[4],",",valuesTemp[5],",",valuesTemp[6],",",valuesTemp[7]);
    
  }

  snprintf(msg, sizeof(msg), StatTemplate, devbuildTemp[0],devbuildTemp[1] );
  Particle.publish("All_data", msg, 60, PRIVATE);
  delay(1000);
}

/*
void MakkeMsg(ADS7828Channel* ch)
{
  // device address (0..3)
  // channel ID (0..7)
  // moving average value (scaled)
  // minimum scale applied to moving average value
  // maximum scale applied to moving average value
  
  char StatTemplate[] = "{\"AD\":%d,\", CH\":%d,\", v\":%d,\", mn\":%d,\", mx\":%d}";
  char msg[sizeof(StatTemplate) + 32];
  
  snprintf(msg, sizeof(msg), StatTemplate, ch->device()->address(), ch->id(), ch->value(), ch->minScale, ch->maxScale);
  Particle.publish("All_data", msg, 60, PRIVATE);
  delay(1000);

  
}
*/

Full app can be found here.

Please note that I don’t have the ADS 7828 board (I’m gonna get one next week) so I can’t tested this before.
Also I’m not sure if I didn’t mess up around here :slight_smile: I mean I’m not sure if this even gonna work !( it’s compiling fine)
Will be perfect and I will really appreciate if @ScruffR could have look on this and give some suggestions before you even gonna flash this to your Photon !!!.

In theory this code should give you an update of all devices/channels every 1s.

I think there are some issues here.
You are using sizeof() for the entire two dimensional array but fill the data into only one field, so the snprintf() will not prevent you from putting too many characters into that field.
I also don’t see why you have the commas between your valueTemp[] entries as literals to insert in place of %s place holders and not as part of your format template. This makes the format string as well as the instruction rather confusing.

Furthermore, you could make your templates const to save some RAM and also ensure that they won’t be overwritten by any accident.

Finally a suggestion for readability. Try to keep your code lines shorter so that you will not need to scroll right to see the entire code.

e.g. like this

  // instead of
  //   snprintf(valuesTemp[ch], sizeof(valuesTemp), values, ADS7828::device(a)->channel(ch)->id(), ADS7828::device(a)->channel(ch)->value(), ADS7828::device(a)->channel(ch)->minScale, ADS7828::device(a)->channel(ch)->maxScale);
  // try this
       snprintf(valuesTemp[ch], sizeof(valuesTemp[ch])
               , valuesTemplate
               , ADS7828::device(a)->channel(ch)->id()
               , ADS7828::device(a)->channel(ch)->value()
               , ADS7828::device(a)->channel(ch)->minScale
               , ADS7828::device(a)->channel(ch)->maxScale
               );

  // or even shorter
       ADS7828Channel *chx = ADS7828::device(a)->channel(ch);
       snprintf(valuesTemp[ch], sizeof(valuesTemp[ch])
               , valuesTemplate
               , chx->id()
               , chx->value()
               , chx->minScale
               , chx->maxScale
               );

(the leading comma and having the closing ); on its own line makes it easier to add/remove a new entry without having to bother with adding/removing it to the end of the previous line)

2 Likes

Thank you so so much ! as always really appreciate your input. As i mentioned before i still awaiting for ADS7828 board so I can’t tested but I make some “fake devices” in test.cpp and use rand() to fill up my values and is looks like it’s gonna work. My JSON var is valid so I just need the board and then I’ll post some update.

Final code can be found here also I get rid of all Arduino requirements eg: word(); and bitRead();

Here some results from “fake device”: