RobTillaart / ADS1X15

Arduino library for ADS1015 = I2C 12 bit ADC and ADS1115 = I2C 16 bit ADC
MIT License
155 stars 29 forks source link

How to check if the data rate is correct? #44

Closed yanxiang-wang closed 2 years ago

yanxiang-wang commented 2 years ago

Hi, I just bought an ADS 1015, and I set the data rate to option 1 in the code. However, when I read the serial output with my laptop, the data rate looks lower than the target rate. For instance, it will give me about 160Hz while it is set to 250Hz. So, how do you check if you acquire the correct data rate? Thanks.

#include "ADS1X15.h"

// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);

// ADS1115 ADS(0x48);

void setup()
{
  Serial.begin(115200);
  // Serial.println(__FILE__);
  // Serial.print("ADS1X15_LIB_VERSION: ");
  // Serial.println(ADS1X15_LIB_VERSION);

  ADS.begin();
  ADS.setGain(1); // 6.144 volt
  ADS.setDataRate(1);
  Serial.println("Voltage");
  ADS.requestADC(0);
}

void loop()
{
  if (ADS.isReady())
  {
    int16_t value = ADS.getValue();
    ADS.requestADC(0);
    Serial.println(value);
  }
}
RobTillaart commented 2 years ago

Thanks for the issue You should be aware that your sketch does also use time to print the value and check if the sensor is ready.

To show this effect change the baudrate of serial to 500.000 or 9600.

RobTillaart commented 2 years ago

High speed will be improved by setting the I2C clock speed e.g to 400K or higher. Best performance is in continuous mode see examples. Then read a sample by checking micros()

yanxiang-wang commented 2 years ago

Thanks, RobTillaart. I changed the baud rate to 9600 as you suggested. At the same time, I add a timer to print the data count per second.

#include "ADS1X15.h"
#include <MsTimer2.h>
int count = 0;
void check_fq()
{
  Serial.print("count:");
  Serial.print(count);
  Serial.println("");
  count = 0;
}

// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);

// ADS1115 ADS(0x48);

void setup()
{
  Serial.begin(9600);
  ADS.begin();
  ADS.setGain(1); // 6.144 volt
  ADS.setDataRate(0);
  ADS.readADC(0);
  MsTimer2::set(1000, check_fq);
  MsTimer2::start();
}

void loop()
{
  // Serial.println(ADS.getValue());
  {
    int16_t data = ADS.getValue();
    count += 1;
  }
}

And the serial output is as follows:

count:1783
count:1784
count:1784
count:1785
count:1784
count:1785
count:1784
count:1784
count:1785
count:1784
RobTillaart commented 2 years ago

So you see the time the Serial printing takes affects the performance. At lower baud rates you just have less time to read the ADC. Now give it a try with 500000 baud, what is the count then?

Note: I updated your post by adding cpp to the code block to get syntax highlighting.

yanxiang-wang commented 2 years ago

Yes, the println function will affect the speed. The strange point is that I set the data rate to 250 by ADS.setGain(1);. So, the count should be 250, am I right? Also, I change the baud by Serial.begin(500000);, the result is the same as above:

count:1783
count:1784
count:1784
count:1784
count:1785
count:1784
count:1784
count:1784
count:1785
count:1784
count:1785
count:1784
count:1784
count:1784
count:1785
count:1784
count:1784
count:1784
count:1784
RobTillaart commented 2 years ago

The strange point is that I set the data rate to 250 by ADS.setGain(1);.

The frequency is not set by setGain(), gain is sort of amplification, the voltage range. You should use **setDataRate(1)

#include "ADS1X15.h"
#include <MsTimer2.h>

//  VARIABLES
volatile uint32_t count = 0;
ADS1015 ADS(0x48);
uint32_t lastTime = 0;

/////////////////////////////////////
void check_fq()
{
  Serial.print("count: ");
  Serial.println(count);
  count = 0;
}

void setup()
{
  //  initialize Serial
  Serial.begin(500000);

  // initialize ADS
  ADS.begin();
  ADS.setWireClock(400000);  //  higher depending on what your processor supports.

  ADS.setGain(1); // 6.144 volt
  ADS.setDataRate(2);  //  490, note this is faster than we want to sample
  ADS.setMode(0);      // continuous mode
  ADS.readADC(0);

  //  initialize timer
  MsTimer2::set(1000, check_fq);
  MsTimer2::start();
}

void loop()
{
  if (micros() - lastTime >= 4000)  // every 4 millisecond we want a sample.
  {
    lastTime = micros();
    int16_t data = ADS.getValue();
    count++;
  }

  // other code here
}

Give it a try

yanxiang-wang commented 2 years ago

Wow, it works. Very grateful for the comments. Initially, I thought micros() was not accurate. And I need to use the interrupt-based timer for all the functions that run at a fixed period. But your example proves I am wrong—many thanks. And the result of your code is as follows:

count: 249
count: 250
count: 250
count: 250
count: 250
count: 250
count: 250
count: 250
count: 250
RobTillaart commented 2 years ago

If you do not have too many (short, non blocking) tasks at hand scheduling with micros() or even millis() works very well. Did a project with more than 20 tasks that needed to run at least 10 to 50 times per second and that worked well. Only thing I had adapted was the analogRead() which resulted in => https://github.com/RobTillaart/AsyncAnalog

Note there are two slightly different ways to implement this with different behavior:

if (micros() - lastTime >= threshold)
{
  lastTime = micros();
  ...
}

if (micros() - lastTime >= threshold)
{
  lastTime += threshold;
  ...
}

As said, the difference is subtle but can be significant when "the load" of the system is high. So choose with care.


If this solves the issue, and no further related questions remain you may close the issue.