I am trying to use micros() to create a delay between calls of IMU.readAcceleration()
According to IMU.accelerationSampleRate() the board has a sample rate of 99.84Hz so I was trying to create a micros() delay period of 10016 including the time it takes to run IMU.readAcceleration() and store the values in an array.
The problem is I am timing the time it takes for the routine to run where the IMU.readAcceleration() function is called and the saving of the values to the array and it is closer to 3 seconds when it should be about 1.5
The delay code I am using is:
for (int t = 0; t < 150; t++) {
time_now = micros();
IMU.readAcceleration(x, y, z);
accelerometerData[t][0] = x;
accelerometerData[t][1] = y;
accelerometerData[t][2] = z;
while(micros() < time_now + 10016){
//wait approx. [period] ms
}
}
Any idea where I am going wrong?
Very likely, in the part of the code that you did not post. For example choosing the wrong data type for time_now. Or in the math.
it is closer to 3 seconds when it should be about 1.5
One approach is to reduce the delay until you get the desired sample rate.
To avoid problems with integer overflow, replace this
while(micros() < time_now + 10016){
with this:
while(micros() - time_now <= 10016){
2 Likes
The data type I chose for time_now is "unsigned long".
I will make the change to the code you suggested.
Here's the whole sketch in case anything stands out:
#include "Arduino_BMI270_BMM150.h"
#include <SD.h>
/*
* Takeoff!
*/
int switchPin = 2; // switch is connected to pin 2
int val = 0; // variable for reading the pin status
int val2;
int buttonState; // variable to hold the button state
const int numberTrials = 21; // 10 presses to go!
int trialNumber = 1;
const int numberSamples = 150;
float x, y, z;
const int delayPeriod = 10016;
unsigned long time_now = 0;
float accelerometerData[numberSamples][3];
String filename;
File myFile;
void setup() {
pinMode(switchPin, INPUT); // Set the switch pin as input
Serial.begin(9600); // Set up serial communication at 9600bps
buttonState = digitalRead(switchPin); // read the initial state
// put your setup code here, to run once:
while (!Serial);
Serial.print("Initializing SD card...");
pinMode(10, OUTPUT);
if (!SD.begin(10)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
Serial.print("Initializing IMU...");
if (!IMU.begin()) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println(" Hz");
}
void loop() {
val = digitalRead(switchPin); // read input value and store it in val
delay(10); // 10 milliseconds is a good amount of time
val2 = digitalRead(switchPin); // read the input again to check for bounces
if (val == val2) {
if (val != buttonState) { // the button state has changed!
if (val == LOW) { // check if the button is now pressed
if (trialNumber == numberTrials) {
Serial.println("Trials Complete!");
}
if (trialNumber < numberTrials) {
delay(100);
for (int t = 0; t < numberSamples; t++) {
time_now = micros();
IMU.readAcceleration(x, y, z);
accelerometerData[t][0] = x;
accelerometerData[t][1] = y;
accelerometerData[t][2] = z;
while(micros() - time_now <= delayPeriod){
}
}
Serial.println("Trial Complete");
writeDataToSD();
trialNumber++;
}
}
buttonState = val; // save the new state in our variable
}
}
}
void writeDataToSD() {
Serial.print("Opening file...");
filename = String(trialNumber) + ".txt";
myFile = SD.open(filename, FILE_WRITE);
if (!myFile) {
Serial.println("opening failed!");
return;
}
Serial.println("file opened.");
for (int i = 0; i < numberSamples; i++) {
myFile.print(accelerometerData[i][0], 6);
myFile.print("\t");
myFile.print(accelerometerData[i][1], 6);
myFile.print("\t");
myFile.println(accelerometerData[i][2], 6);
}
myFile.close();
Serial.print("Accelerometer Data Text File ");
Serial.print(trialNumber);
Serial.println(".txt has been written.");
}
If, not I guess I will play with the delay amount while shaking the Nano board until I don't see repeat measurements.
That probably doesn't take into account how long it takes to read out the data from the IMU. Use the micros() function to measure that.
Why do you think you need any delay? You haven't explained where the number 10016 came from.
That's a great idea! It never occurred to me.
The "IMU.readAcceleration(x, y, z)" and storing the data in the array takes about 4250 microseconds at max.
I got 10016 by dividing 1 second by 99.84, the sample rate, to get 0.010016 second per sample and then converted seconds to microseconds.
I think my timing of the routine was off. I used your while loop modification and took a time stamp before the for loop ran, ran the loop for 1 cycle, took the time after the loop and it came out to slightly over 10016 microseconds.
Do you need a particular sample rate for some reason? If so, it is not easy to take into account the I/O and store timing, unless the sensor can be configured to take collect data on command.
Check whether the sensor has a "data ready" output. You can use that not only to trigger the get&store, but also to calculate the delay, rather than arbitrarily tuning it.
I just wanted to maxmize the rate samples are taken by the device but not have duplicate values.
I just tested the following code to see the total time it will take in one of my trials.
time_start = millis();
for (int t = 0; t < numberSamples; t++) {
time_now = micros();
IMU.readAcceleration(x, y, z);
accelerometerData[t][0] = x;
accelerometerData[t][1] = y;
accelerometerData[t][2] = z;
while(micros() - time_now <= delayPeriod){
}
}
time_end = millis();
total_time = time_end - time_start;
Serial.print(total_time);
Serial.println(" milliseconds");
It printed out slightly over 1500milliseconds which is right on target since I am taking 150 samples per test drop.
Thank you so much for your suggestion to use micros() for time recording and printing it out to see for certain the time that is passing.
Most people use a library function that informs the Arduino when new data are ready, e.g. as in this example from the library you are using:
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);
That is much simpler, and in the meantime, the Arduino can do something other than delay.
Except for what was pointed out by @jremington, you could just as well have used delayMicroseconds().
If you need an accurate timing between readings, I would use something like below
/*
read IMU
in:
index in array to store readings
returns:
true if a reading was done, else false
*/
bool readIMU(uint8_t idx)
{
static uint32_t lastUpdateTime;
if(micros() - lastUpdateTime >= somePeriod)
{
lastUpdateTime = micros();
IMU.readAcceleration(x, y, z);
accelerometerData[idx][0] = x;
accelerometerData[idx][1] = y;
accelerometerData[idx][2] = z;
return true;
}
return false;
}
And in loop(), you can use it like
void loop()
{
// array index counter
static uint8_t cnt;
// read imu
if (readIMU(cnt) == true)
{
// we got a new reading, increment counter
cnt++;
}
// if array filled
if(cnt == 150)
{
// reset counter
cnt = 0;
// array filled do something with it
...
...
}
}
Use this as a guideline; not tested nor compiled.