Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arduino_BHY2: Help on high-speed sampling of accelerometer #72

Open
gmacario opened this issue Nov 11, 2022 · 2 comments
Open

Arduino_BHY2: Help on high-speed sampling of accelerometer #72

gmacario opened this issue Nov 11, 2022 · 2 comments

Comments

@gmacario
Copy link

Hello,

In our project we have a use case which is quite similar to the one described in the issue #48 which is still unsolved.

We want to retrieve the sensor value of the on-board accelerometer of the Nicla Sense ME at the highest possible supported by the hardware, store the values in memory then send them through the serial port and/or BLE to allow further processing (such as performing a FFT or other analysis) by external entities.

According to Table 117 in the BHI260AP datasheet the accelerometer can be sampled up to 1600 Hz, but we have been unable to achieve similar targets using the Arduino_BHY2 library.

Based on the following documentation:

We have developed the following simple sketch to be run on the Nicla Sense ME:

/**
 * Flags for conditional compilation
 */
//#define ENABLE_BHY2_DEBUG 1

/**
 * Include header files
 */
#include <assert.h>
#include <ctype.h>

#include <Arduino_BHY2.h>
#include <Nicla_System.h>

/**
 * Flag to define whether to capture acceleration values from Nicla
 */
auto capture_accel { false };

/**
 * Data structure for holding sensor data
 */
typedef struct __attribute__((__packed__)) Sensor_Values {
  unsigned long timestamp_us; // timestamp in microseconds
  float accel_x;      // acceleration_x component
  float accel_y;      // acceleration_y component
  float accel_z;      // acceleration_z component
};

#define MAX_ENTRIES 16 /* TODO: 2048 */
static Sensor_Values sensor_buffer[MAX_ENTRIES];
static int buffer_count = 0;

/**
 * Acceleration data from Nicla sensor
 */
SensorXYZ nicla_accelerometer(SENSOR_ID_ACC_PASS);

void setup() {
  nicla::begin();
  nicla::leds.begin();

  Serial.begin(115200);    // initialize serial communication
  while (!Serial);

  delay(1000);
  Serial.println("\n\nINFO: " __FILE__ " - Built on " __DATE__ " " __TIME__);

  delay(5000);

#ifdef ENABLE_BHY2_DEBUG
  BHY2.debug(Serial);
#endif
  Serial.println("DEBUG: Call BHY2.begin");
  BHY2.begin(NICLA_STANDALONE);

  /** Initialize accelerometer sensor with a non-default configuration
   * Reference: <https://github.com/arduino/nicla-sense-me-fw/blob/main/Arduino_BHY2/src/sensors/SensorClass.h#L22>
   *
   * TODO: Understand whether sample_rate is used on physical sensors such as SENSOR_ID_ACC_PASS
   */
  //nicla_accelerometer.begin(1, 10);      // sample_rate=1Hz; latency=10ms
  nicla_accelerometer.begin(1600, 10);     // sample_rate=1600Hz; latency=10ms

  /** Verify actual sensor configuration
   */
  SensorConfig cfg = nicla_accelerometer.getConfiguration();
  Serial.println(String("DEBUG: acceleration configuration - rate: ") + cfg.sample_rate + String("Hz - latency: ") + cfg.latency + String("ms - range: ") + cfg.range);

  /** Initialize buffer holding sensor values (timestamp and raw accelerometer XYZ value)
   */
  buffer_count = 0;
  capture_accel = true;

  Serial.println("DEBUG: END setup()");
}

void loop() {

  // Serial.println("DEBUG: Calling BHY2.update()");
  BHY2.update();

  if (capture_accel == true && buffer_count < MAX_ENTRIES) {
    // Serial.println("DEBUG: Reading accelerometer values");

    /**
      * According to <https://github.com/arduino/nicla-sense-me-fw/issues/46#issuecomment-1012338693>:
      * With the current sensor configuration settings, the sensor has a range of +/-8G within 16bits of data. Then 1g = 4096.
      */
    Sensor_Values entry;
    entry.timestamp_us = micros();
    entry.accel_x = nicla_accelerometer.x();
    entry.accel_y = nicla_accelerometer.y();
    entry.accel_z = nicla_accelerometer.z();

#if 0
    // If Serial output buffer is full, Serial.write will block
    Serial.println("DEBUG: Acquire: timestamp_us=" + String(entry.timestamp_us)
      + ", buffer_count=" + buffer_count
      + ", acceleration=(x=" + String(entry.accel_x)
      + String(", y=") + String(entry.accel_y)
      + String(", z=") + String(entry.accel_z) + String(")"));
#else
    // Queue entry to sensor_buffer, then print values in another loop to speed up acquisition
    sensor_buffer[buffer_count] = entry;
    buffer_count++;
    if (buffer_count >= MAX_ENTRIES) {
      Serial.println("DEBUG: buffer full, stopping acquisition");
      capture_accel = false;
    }
#endif
  }

#if 1
  /**
   * Print the data in sensor_buffer if not acquiring sensor
   */
  if (!capture_accel && buffer_count > 0) {
    for (int k = 0; k < buffer_count; k++) {
        Sensor_Values entry = sensor_buffer[k];
        Serial.println("DEBUG: Report: timestamp_us=" + String(entry.timestamp_us)
          + ", k=" + k
          + ", acceleration=(x=" + String(entry.accel_x)
          + String(", y=") + String(entry.accel_y)
          + String(", z=") + String(entry.accel_z) + String(")"));
    }
    buffer_count = 0;
    capture_accel = true;
  }
#endif

  yield();
}

/* EOF */

The sketch invokes BHY2.poll() inside loop(), then reads accelerometer sensor values from Virtual Sensor SENSOR_ID_ACC_PASS and stores the results in a buffer in memory; each entry is supplemented with the timestamp when the Sensor was read.

As soon as the buffer is full, the acquisition is stopped and the results are printed out in a separate loop.

Here are our questions:

  1. Which is the difference between Virtual sensor ID 4 (ID SENSOR_ID_ACC) vs ID 1 (SENSOR_ID_ACC_PASS)?
    Is there any additional information beyond Table 88 in bst-bhi260ap-ds000.pdf?
  2. According to https://docs.arduino.cc/tutorials/nicla-sense-me/cheat-sheet
    we can call nicla_accelerometer.begin() and provide a custom configuration for sample rate and latency.
    As a matter of fact, if we call nicla_accelerometer.getConfiguration() after nicla_accelerometer.begin(1600, 10)
    the function returns a different sample_rate from the one requested, and never higher than 400 Hz.
  3. More importantly, it looks like if we call nicla_accelerometer.x(), nicla_accelerometer.y(), nicla_accelerometer.z() just after a BHY2.update() the values returned by those functions do not seem to change while the timestamp suffers from jitter and is not related to the sample_rate we set in nicla_accelerometer.begin(), therefore the values we are storing in the buffer for further analysis are not meaningful.
    The problem is not evident in the provided examples when the sensor is read every second, but as explained we want to sample the acceleration sensor at the highest frequency supported by the hardware while obtaining meaningful data.
    How can we make make sure that a new value is returned when the function is called?

Any help and/or pointers to sample working code is appreciated, thanks!

@facchinm
Copy link
Member

Hi Gianpaolo,
would you mind taking a look at this PR https://github.com/arduino/nicla-sense-me-fw/pull/68/commits ?
It should fix exactly your use case 🙂
We have to agree with @bstbud a way to document the whole procedure but the combination of fw + reliable usb communication should do the trick.

@dariosortino
Copy link

Hello! I'm having the same kind of issue here.

@gmacario, have you found a suitable solution? I don't fully understand the PR linked above, so I'm wondering how you proceeded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants