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:
- Everything is working, save for usually the x-axis overflow problem.
- This
requestFrom
call inbmi2_i2c_read
returns 0 when it should not. - This
endTransmission
call returns 2 (an error code) for all subsequent calls tobmi2_i2c_read
. Notably, I have yet to observe a failure withinbmi2_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?