Setting a constant sampling rate

Hello,

I am trying to measure vibrations on a machine using a Nano + an ADXL345 accelerometer. I will be sending the data to a Raspberry Pi where I will be doing a FFT.

The thing is that I am still having issues programming the Nano to achieve a constant sampling rate. No matter what I have tried, my code is printing a few values (~10-20) under/above the wanted sampling rate (e.g. 600 Hz). Is there something I am missing or that I could improve to reach a constant sampling rate?

#include<Wire.h> // Wire library, used for I2C communication

/*------------CONSTANTS---------------------------*/
const int SENSITIVITY_DIVISOR_2G = 256;
const int SENSITIVITY_DIVISOR_4G = 128;
const int SENSITIVITY_DIVISOR_8G = 64;
const int SENSITIVITY_DIVISOR_16G = 32;
const int DATARATE_25_HZ = 8;
const int DATARATE_50_HZ = 9;
const int DATARATE_100_HZ = 10;
const int DATARATE_200_HZ = 11;
const int DATARATE_400_HZ = 12;
const int DATARATE_800_HZ = 13;

const int ADXL345 = 0x53; // I2C address of the ADXL345
/*------------CONSTANTS---------------------------*/

/*------------PROGRAM VARIABLES-------------------*/

float AccX, AccY, AccZ; // outputs
float gravity = 9.81;
unsigned long previousMicros = 0L;

/*------------PROGRAM VARIABLES-------------------*/

/*------------SETTINGS----------------------------*/

uint8_t sensitivityRange= 2; //Must be equal to 2,4,8 or 16
int sensitivityDivisor = SENSITIVITY_DIVISOR_2G; // change this to change sensitivity_divisor
int dataRate = DATARATE_800_HZ; // 
int arduinoSamplingRate = 600; // MUST BE LESSER THAN dataRate (in Hz)

/*------------SETTINGS----------------------------*/

float samplingIntervalfloat= (1000000/arduinoSamplingRate); //float variable for intermediate calculation
int samplingInterval = (int)samplingIntervalfloat;


void setup() {
  Serial.begin(250000); // Initiate serial communication for printing the results on the Serial monitor
  Serial.println(samplingInterval);
  delay(2000);
  Wire.begin(); // Initialize communication
  Wire.setClock(400000L); // Set Clock Speed
  
  // Set ADXL345 in measuring mode
  Wire.beginTransmission(ADXL345); // Start communication with the ADXL345
  Wire.write(0x2D); // Talk to the register 0x2D (POWER_CTL register)
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable 
  Wire.endTransmission();

  // Set ADXL345 data rate
  Wire.beginTransmission(ADXL345); // Start communication with the ADXL345
  Wire.write(0x2C); // Talk to the register 0x2C (BW_RATE register)
  Wire.write(dataRate); // (13 -> 800 Hz, 12 -> 400 Hz, 11 -> 200 Hz, 10 -> 100 Hz)
  Wire.endTransmission();

  //Set ADXL345 Sensitivity Range
  setRangeSetting(sensitivityRange);
}

void loop() {

  unsigned long currentMicros = micros();
  if (currentMicros - previousMicros >= samplingInterval) {
    // save the last time you blinked the LED
    previousMicros = currentMicros;
    
  // === Read accelerometer data === //
  Wire.beginTransmission(ADXL345);
  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  AccX = ( Wire.read()| Wire.read() << 8); // X-axis value
  AccX = (AccX / sensitivityDivisor) * gravity; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  AccY = ( Wire.read()| Wire.read() << 8); // Y-axis value
  AccY = (AccY / sensitivityDivisor) * gravity;
  AccZ = ( Wire.read()| Wire.read() << 8); // Z-axis value
  AccZ = (AccZ / sensitivityDivisor) * gravity;
  Serial.print(AccX);
  Serial.print(", ");
  Serial.print(AccY);
  Serial.print(", ");
  Serial.println(AccZ);
  } //if
}


/* ---------------------------------HELPER FUNCTIONS---------------------------------------*/
void setRangeSetting(int val) {
  byte _s;
  byte _b;

  switch (val) {
    case 2:
      _s = B00000000;
      break;
    case 4:
      _s = B00000001;
      break;
    case 8:
      _s = B00000010;
      break;
    case 16:
      _s = B00000011;
      break;
    default:
      _s = B00000000;
  }
  readFromI2C(0x31, 1, &_b);
  _s |= (_b & B11101100);
  
  Wire.beginTransmission(ADXL345);
  Wire.write(0x31);
  Wire.write(_s);
  Wire.endTransmission();
}

void readFromI2C(byte address, int num, byte _buff[]) {
  Wire.beginTransmission(ADXL345);
  Wire.write(address);
  Wire.endTransmission(); 

//  Wire.beginTransmission(ADXL345_DEVICE);
// Wire.reqeustFrom contains the beginTransmission and endTransmission in it. 
  Wire.requestFrom(ADXL345, num);  // Request 6 Bytes

  int i = 0;
  while(Wire.available())
  {
    _buff[i] = Wire.read();       // Receive Byte
    i++;
  }
  if(i != num){
    Serial.println("Error");
  }

}
/* ---------------------------------HELPER FUNCTIONS---------------------------------------*/
float samplingIntervalfloat = (1000000 / arduinoSamplingRate); //float variable for intermediate calculation

The result of this calculation will not be a float because calculations are done as integers by default so there is no need to convert it to an int afterwards

As to the timing problem, are you aware that micros() increments in steps of 4, not 1 ? Could this be affecting your calculations ?

Should be:

  unsigned long currentMicros = micros();
  if (currentMicros - previousMicros >= samplingInterval) {
    // schedule next reading correctly
    previousMicros += samplingInterval;
    

You may also be seeing some jitter due to the regular timer interrupts than are used to support
millis() and micros()

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.