I am using an Arduino Nano 33 BLE Sense Rev 2 Board.
I have changed the example BMI270 - Simple Accelerometer (Library Arduino_BMI270_BMM150) in two points:
baudrate ist changed to 155.200 Bauds;
a timestamp is microseconds resolution is set when data is available:
#include "Arduino_BMI270_BMM150.h"
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("Started");
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println(" Hz");
Serial.println();
Serial.println("Acceleration in G's");
Serial.println("X\tY\tZ");
}
void loop() {
float x, y, z;
if (IMU.accelerationAvailable()) {
Serial.print(micros());
Serial.print(" ");
IMU.readAcceleration(x, y, z);
z = -z;
Serial.print(x,4);
Serial.print(" ");
Serial.print(y,4);
Serial.print(" ");
Serial.println(z,4);
}
}
I analyzed the timestamps and discovered sporadic missing samples. The following plot shows the time difference in microseconds between adjacent samples:
The time difference should be around 10.000 us (sample rate near to 100 Hz). Missing samples occur when the time difference is double (around 20.000 us).
What is the reason for this effect and how can I avoid this?
The Arduino to PC communication is asynchronous, with two steps: filling the serial USB buffer, and transferring the USB buffer to the PC. This process has no simple relation to the sensor sample interval, and there is no reason to assume that all communication happens in the time interval between two measurements.
In addition to skipping a reading, you might also be seeing duplicate samples.
Why send raw data to the PC? You could store successive readings in an array, and do whatever processing is required in the Arduino.
I think OP meant he analyzed the successive timestamps obtained from
which come out once per sample (when IMU.accelerationAvailable() returns true).
I think that on the Nano 33 BLE Sense Rev 2, Serial is over USB CDC, so it ignores the baud rate setting (the baud rate parameter is kept for compatibility with traditional UART code, but the actual data transfer happens over USB, so the speed is determined by the USB connection, not the number you pass to Serial.begin())
Thanks a lot for your support and suggestions! You are right, the change of the baudrate is not relevant due to USB CDC. Serial transfer should only happen every 10 ms when new data is available and it should be fast enough. I tested your sketch:
So, the serial transfer can not be the reason of the missing samples, or?
I would expect that the polling loop with IMU.accelerationAvailable() is fast enough to recognize every new sample in the FIFO.
One possible test could be to reduce the sample rate and check again for missing samples.
... and one additional comment: This is a first test with an example sketch from the library with only small changes from me. This should work as expected, or?
In the next steps I will add samples from the gyro and magnetometer for some offline tests on the PC; This can be done with lower sample rate or with aquiring data first and transmit in a second step. Nevertheless, I would like to get the samples at a defined sample-rate without missing samples.
The final step is a processing locally und transmitting of results at a lower rate ...
However, there is another serial transfer: the data from the sensor to the MCU, which looks to be via I2C (Wire or Wire1 library, the schematics suggest Wire1).
It seems possible that that is not fast enough, so you should try increasing the I2C clock rate, e.g.
Wire1.setClock(400000); //or Wire Check processor docs for fastest supported speed
Edit: I recalled some forum discussions where people have complained about not being able to reach the maximum sample rate, and issues have been posted on the library Github page.
Interestingly, no one seemed to check for or complain about missing samples, so the BLE Sense Rev 2 may be a problematic board from the very start.
Clearly it seems the sampling rate has been creating headaches compared to the rev1 version, there are indeed a few issues raised on this esp. as people wanted faster sampling rates.
I don't understand either, and spent a bit of time looking at the library. It is a tangled mess of cobbled-together code from different sources, and judging from other people's comments on Github, has serious problems.
Frankly, I recommend to avoid the Nano BLE Sense Rev 2 and consider a different sensor. Bosch IMU sensors are simply not state of the art.
Lately, Arduino LLC has released a number of products that are best described as premature, incomplete, inadequately tested and poorly supported.
As always with interrupts, you need to make sure that what needs to be atomic is atomic. Use of volatile flags is also necessary . A crude approach without mutex could be
#include "Arduino_BMI270_BMM150.h"
volatile bool data_rdy_flag = false;
void isr_cb() {
data_rdy_flag = true;
}
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("Started");
IMU.onInterrupt(isr_cb);
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println(" Hz");
Serial.println();
Serial.println("Acceleration in G's");
Serial.println("X\tY\tZ");
}
void loop() {
float x, y, z;
bool ready;
noInterrupts();
ready = data_rdy_flag;
data_rdy_flag = false;
interrupts();
if (ready) {
IMU.readAcceleration(x, y, z);
Serial.print(micros());
Serial.print(" ");
z = -z;
Serial.print(x,4);
Serial.print(" ");
Serial.print(y,4);
Serial.print(" ");
Serial.println(z,4);
}
}
I agree regarding a volatile flag data_rdy_flag. Thank you for this advice.
My test was a quick check without thinking about concurrency after reading the usage of interrupt in issue 25.
I have some experience working with stm32-MCUs. There I can disable a specific interrupt with NVIC_DisableIRQ(). This could be a better way than using noInterrupts() (identical with __disable_irq()), wich disables all interrupts. But what is the necessary argument of type IRQn_Type for NVIC_DisableIRQ()? I have really no knowlewdge of the underlying framework ... and it is not easy to get some ...
Is there a mbed-thread running in the background? What are the tools for concurrency in mbed which are relevant in this case, e.g. methods to block specific interrupts?
Actually now that you say this - if I remember right, the IMU interrupt is handled inside the ArduinoCore-mbed layer (where you do not have direct access to the IRQ number) and the callback you register is executed from the mbed event queue, not directly from the hardware IRQ handler so your callback is not executed in a true ISR context but runs in a threaded callback context ➜ a volatile and very short noInterrupts() / interrupts() section to atomically read-and-clear the flag is fine and portable.
With this information I should read the INT_STATUS_1 register when ready==true to reset acc_drdy_int. The code works without ..
The flag STATUS.drdy_acc is cleared when the data registers are read. This should be o.k.
Coming back to the polling solution: When I do a polling instead of using an interrupt, I would disable the interrupt and poll the STATUS.drdy_acc flag. IMU.accelerationAvailable() checks acc_drdy_int and resets that flag by reading. Interrupts are enabled. May be this solution is responsible for the missing samples (when this flag is cleared by some code else where before the polling)?
I have done a new test with changes to the sketch "BMI270-Simple Accelerometer" polling the status bit STATUS.drdy_acc, disabling the Advanced Power Save Mode (avoiding a 450 us delay in the I2C-reading), disabling the interrupt and setting the I2C-clock to 400 kHz. There are no missing samples. The remaining jitter results most from the duration of the polling loop (about 200 us):