Setting a BMM150 preset mode breaks the BMM150 and the I2C bus as a whole

I wrote the following subclass of the BoschSensorClass from the Arduino_BMI270_BMM150 library, following this example rather closely:

#include <Arduino_BMI270_BMM150.h>

class MyBoschSensor : public BoschSensorClass {

public:
  MyBoschSensor(TwoWire& wire = Wire)
    : BoschSensorClass(wire){};

protected:
  virtual int8_t configure_sensor(struct bmm150_dev* dev) override {
    /* Status of api are returned to this variable. */
    int8_t rslt;
    struct bmm150_settings settings;

    /* Set powermode as normal mode */
    settings.pwr_mode = BMM150_POWERMODE_NORMAL;
    rslt = bmm150_set_op_mode(&settings, dev);

    if (rslt == BMM150_OK) {
      /* Setting the preset mode as Low power mode
      * i.e. data rate = 10Hz, XY-rep = 1, Z-rep = 2
      */
      settings.preset_mode = BMM150_PRESETMODE_REGULAR;
      rslt = bmm150_set_presetmode(&settings, dev);

      if (rslt == BMM150_OK) {
        /* Map the data interrupt pin */
        settings.int_settings.drdy_pin_en = 0x01;
        //rslt = bmm150_set_sensor_settings(BMM150_SEL_DRDY_PIN_EN, &settings, dev);
      }
    }
    return rslt;
  }
};

MyBoschSensor myIMU(Wire1);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial)
    ;
  Serial.println("Started");

  myIMU.debug(Serial);
  if (!myIMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1)
      ;
  }
}

void loop() {
  float x, y, z;

  if (myIMU.magneticFieldAvailable()) {
    myIMU.readMagneticField(x, y, z);

    Serial.print(x);
    Serial.print('\t');
    Serial.print(y);
    Serial.print('\t');
    Serial.println(z);
  }
}

As you can see, the only overridden method is configure_sensor(struct bmm150_dev* dev), as I am only interested in configuring the magnetometer for now. This subclass's definition only differs from the superclass's definition by exactly one line: I uncommented line 265. I was aiming to set the BMM150 to the high accuracy preset, as we need the best available quality of data for our purposes, but I would soon discover that I wouldn't get that far.

When BMM150_PRESETMODE_LOWPOWER is assigned to settings.preset_mode in the above code, everything works as expected. However, assigning BMM150_PRESETMODE_NORMAL, BMM150_PRESETMODE_ENHANCED, or BMM150_PRESETMODE_HIGHACCURACY to it will break everything.

First, the x-axis will overflow on nearly every sample. I read here that this may be due to magnetic fields being generated by nearby components on the board, but I'm having trouble accepting this as the cause, as samples taken with the low power preset don't show any signs of such interference. The only plausible explanation I can imagine in this regard would have something that has to do with the increased power draw of these modes (the high accuracy preset uses nearly 29x the power of the lower power preset!). Even so, the normal preset only uses around 3x more power, and the enhanced preset isn't much worse.

Second, selecting any preset other than the low power one completely destabilizes the I2C bus. I have tested several variations of this code across four Nano 33 BLE Sense Rev2 boards, all purchased at different times and locations, and the bus is guaranteed to get stuck within 5-10 seconds of startup. This also prevents the BMI270's accelerometer and gyroscope from being polled, which is frustrating. Adding a delay of about 100ms in the main loop seems to help for a little while, but (a) this negates our reason for using the board, and (b) delays of less than 1000ms have still resulted in eventual failure in every test I have performed. Even a 1000ms delay still fails rather frequently.

I ended up writing my own implementation using just the BMM150-Sensor-API library to try and narrow down the cause of the bus failure, and the pattern seems to be as follows:

  1. Everything is working, save for usually the x-axis overflow problem.
  2. This requestFrom call in bmi2_i2c_read returns 0 when it should not.
  3. This endTransmission call returns 2 (an error code) for all subsequent calls to bmi2_i2c_read. Notably, I have yet to observe a failure within bmi2_i2c_write... I really can't explain that.

Now, according to the Wire library documentation, error code 2 means

received NACK on transmit of address.

But I would take this with a grain of salt for two reasons. First, the endTransmission call within bmi2_i2c_write never results in this error code, so I'm suspicious of the idea that bmi2_i2c_read is somehow different. Second, I took a peek at the Mbed OS implementation of endTransmission, and there are several major discrepancies between the documentation and the Mbed OS implementation of Wire.h as a whole. Namely, the Mbed OS implementation of endTransmission makes no attempt to discern between the possible sources of error: it either returns 0 on success or 2 on failure, and I'm not sure why this error code was chosen in particular.

Tangent: Other Discrepancies

I also noticed the buffer is 256 bytes in the Mbed OS implementation, not 32 bytes as stated in the documentation and seen in the AVR implementation. I also noticed that, unlike the AVR implementation and contrary to the documentation, the Mbed OS implementation has no safeguards against buffer overflow. It also defines a memcpy-like write overload that takes an int for the length argument for some reason, and breaks if the provided len is negative, and conflicts with the standard size_t overload imported here when passing a uint32_t for len. All of this is sadly beside the point.

Does anyone have any experience with configuring the integrated BMM150 on this board, and if so, did you encounter any of these issues?

It turns out this was a power issue caused by the Arduino Mbed core firmware, and it was fixed in release 4.1.1 a couple days after I made this post.

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