Using Arduino as DAQ? (Help with sampling)

Hi,

First off I would like to thank the arduino community for their indirect help with my project so far! Almost everything I learned about coding on the arduino so far has been from the forums. I am currently a senior Biomedical Engineering major so I am comfortable with topics such as DSP, Nyquist, C programming and such; therefore I consider myself an intermediate user. I know what I want the arduino to do, but don't necessarily know how to exactly code it. So anyways back to the task at hand.

What I'm trying to do is sample an analog signal (microphone with custom pre-amp filter) and output each sample as binary over Bluetooth to a computer or cell phone as it's collected. This pre-amp filter will filter out frequencies over 2000Hz and bias around 0-5V, therefore I need to sample at 4000Hz or greater. This signal once on the computing platform, will be plotted and analyzed using some frequency analysis. The reason I'd like the sampling to be continuous as opposed to being stored in an array is because the computing platform will continuously plot the data as it comes in (similar to an ECG).

The problem that I am having is that I cannot figure out how to control the sampling rate to be consistent. At first I let the while loop run uninhibited, but the sample period would vary a little between samples. Then I thought that implementing an if statement in the loop to only sample when the difference between micros() and the last sample is 236uS (4237Hz), but I get a sampling rate of 4166.66Hz....?

My question is there any other better method to sample at a consistent sampling rate? Or is the problem with how I am calculating the sampling rate, and in actuality I am sampling at the prescribed rate?

I have attached my code so far for reference. To test, I use PUTTY and A0 connected to 3.3V rail. Thanks,

Cody

/*********************************************************************************************
*                                                                                            *
*  This program reads 'n' data points at the A0 port and immediately sends out the value     *
*  over SPP. It calculates the sampling rate of each sample.                                 *
*                                                                                            *
*********************************************************************************************/

const byte analogInPin = 0;  // A0
const byte samplePeriod = 236; // sampling period in uS, MUST BE MULTIPLE OF 4

unsigned int sensorValue = 0;        // value of microphone
unsigned long int i = 0; // number of samples
char test = 's'; // controls when program executed
unsigned long initial = micros(); // time that sample collected
unsigned long scratch1 = micros(); // used to calculate sampling rate
unsigned long scratch2 = micros(); // used to calculate sampling rate
byte scratch = 0; // used to control sampling
float rate = 0;

void setup() {
  Serial.begin(115200);  // initialize serial communications
  pinMode(13, OUTPUT);
  Serial.print("Forced Sampling at ");
  Serial.print(samplePeriod);
  Serial.println("uS");
}

void loop() { 
  test = Serial.read();
  if (test=='r') {
    Serial.println();
    Serial.println("START:");
    initial = micros();
    i = 0;
    scratch1 = micros(); // start of data collection
    while(test != 's'){ // if input 's' into serial from computer exit loop
      scratch = micros() - initial;
      if (scratch == samplePeriod) {
        initial = micros();
        sensorValue = analogRead(analogInPin); // read in value
        Serial.write(highByte(sensorValue)); // output MSB of int in binary
        Serial.write(lowByte(sensorValue)); // output LSB of int in binary
        i++;
        test = Serial.read(); // read for value from serial
        }
      }
    scratch2 = micros(); // end of data collection
    rate = i/(0.000001*(scratch2-scratch1)); // calculate sampling rate
    Serial.println();
    Serial.println("END");
    Serial.print(i);
    Serial.println(" Total Samples");
    Serial.print("Calculated Sampling Rate: ");
    Serial.print(rate);
    Serial.println("Hz");  
  }
else { // flash LED while waiting for input
  digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait
  }
}

Then I thought that implementing an if statement in the loop to only sample when the difference between micros() and the last sample is 236uS (4237Hz), but I get a sampling rate of 4166.66Hz....?

The timing of an Arduino is not super exact, e.g. micros() works in steps of 4 usec. Furthermore additional statements might take time that need to be taken into account.
4166Hz is the value when you tested a difference of 240uS.

so the trick is probably use 232uS to get 4237HZ.

give it a try.

is there any other better method to sample at a consistent sampling rate?

Yes you use one of the timers to generate an interrupt at a regular interval. Then you use the ISR ( interrupt service routine) to take the samples.

Thanks for the responses! I tried using an interrupt service routine and the sampling is much more consistent but for some reason the Bluetooth and keyboard control no longer work. The Bluetooth runs for about 200 samples then starts to stutter then loses connection, so I have been running it straight off of USB. The Arduino seems to be receiving data (RX lights up) but I can't figure out a way to stop transmitting data. Here is my code so far. Thanks for the help!

  • CODY
#include <TimerOne.h>

/*********************************************************************************************
*                                                                                            *
*  This program reads 'n' data points at the A0 port and immediately sends out the value     *
*  over SPP. It calculates the sampling rate of each sample.                                 *
*                                                                                            *
*********************************************************************************************/


const byte analogInPin = 0;  // Analog input pin that the potentiometer is attached to
const byte samplePeriod = 205;

unsigned int sensorValue = 0;

void setup() {
  Serial.begin(115200);  // initialize serial communications
  delay(2000);
  pinMode(13, OUTPUT);
  Serial.write(10);
  Serial.write(10);
  Serial.print("Forced Sampling at ");
  Serial.print(samplePeriod);
  Serial.print("uS");
  Serial.write(10);
  Serial.write(10);
  delay(1000);
  
  timersetup();
}

void sample() {
   sensorValue = analogRead(analogInPin);
   Serial.write(highByte(sensorValue));
   Serial.write(lowByte(sensorValue));
}

void timersetup() {
  Timer1.initialize(samplePeriod); // set a timer of length 
  Timer1.attachInterrupt(sample); // attach the service routine here
}

void loop() { 
  digitalWrite(13, HIGH);   // set the LED on
  delay(100);
  digitalWrite(13, LOW);
  delay(100);
}

Basically the problem is that a protocol like bluetooth does not work in real time. What happens is that data is sent in packets. Somewhere in the chain data is being buffered until there is a certain amount and then that is transmitted as a package. To achieve the illusion of real time these packets are time stamped at one end and unpacked and played back at the right rate at the other.
I don't know of any arduino code that can do this but then I don't know all arduino code that is out there.