Advice: I2C communication with internal sensors BLE / BLE Sense

I've spent almost two days figuring out how to properly communicate with the internal sensors of the new Nano 33 BLE and Nano 33 BLE Sense boards via I2C. And because it took me so long, i wanted to share, what i've found out.

So, the main reason, why we can't properly communicate with the internal sensors via I2C is that one of the default I2C bus wires is set to control the power mode of some sensors and therefore blocking the default I2C bus for direct communication. Now that we know the problem, we can use the TwoWire class of the Wire.h library to switch to a secondary I2C bus in order to communicate with the sensors.

In order to do so, you simply have to add the following line

extern TwoWire Wire1;

after your #include <Wire.h> statement and afterwards replace every Wire.something() with Wire1.something().

For example here is a sketch showing you how to use the secondary I2C bus with the help of Wire1 to read and write to the accelerometer and gyroscope of the Nano 33 BLE (also works for the Nano 33 BLE Sense).

#include <Wire.h>
extern TwoWire Wire1;                                 //Use TwoWire class to switch to seconds I2C bus!

//this read function works
uint8_t readRegister(uint8_t slaveAddress, uint8_t registerAddress) {
      
      uint8_t registerData;                           //placeholder for register data 
      
      Wire1.beginTransmission(slaveAddress);          //Initiate I2C communication with slave address 
      Wire1.write(registerAddress);                   //Set pointer to register address
      Wire1.endTransmission(false);                   //Send buffer, but keep connection alive
      Wire1.requestFrom(slaveAddress, (uint8_t)1);    //Request one byte from the register the pointer was set to
      registerData = Wire1.read();                    //Read byte and copy its content to data placeholder variable
      Wire1.endTransmission();                        //End I2C communication
      
      return registerData;                            //Return read data
}

//this write functions works (but only if register is writeable)
uint8_t writeRegister(uint8_t slaveAddress, uint8_t registerAddress, uint8_t registerValue) {
  
  Wire1.beginTransmission(slaveAddress);              //Initiate I2C communication with slave address
  Wire1.write(registerAddress);                       //Set pointer to register address
  Wire1.write(registerValue);                         //Overwrite register address the pointer is set to with new value
  Wire1.endTransmission();                            //End I2C communication
}

void setup(){
  Serial.begin(9600);                                 //Start serial communication
  
  Wire1.begin();                                      //Initialize I2C Wire1
  
  while (!Serial);                                    //Wait for the serial monitor
  Serial.println("Serial Started");

  delay(50);
}

void loop(){
  byte registerData = readRegister(0x6b, 0x1e);       //Read CTRL_REG4 register of the LSM9DS1 accelerometer and gyroscope
  Serial.print("Register value = ");                  //default value = 0x38
  Serial.println(registerData, HEX);
  delay(5000);

  writeRegister(0x6b, 0x1e, 0x00);                    //Replace default CTRL_REG4 register value with 0x00
  delay(5000);
}

I hope this was helpful to some of you.

Best regards,
paarn

PS: I have also attached a modified version of the I2C-scanner which works with the new boards to this thread.

i2c_scanner_for_nano_33_ble.ino (2.27 KB)

2 Likes

Why not use the provided libraries?

and Wire1 is in Wire.h/.cpp. an extern doesn't instance an object. Wire1 is instanced in Wire.cpp

Juraj:
Why not use the provided libraries?

Because the provided libraries are often not well suited for specific purposes, e.g. the output data rates or measurement ranges of the sensors could be inappropriate for some specific applications such as measuring quick movements in sports, science or some other "serious" application.

And i'm not exactly sure if i get what you intended to say with your second sentence? Can you please elaborate your thought a bit more in depth?

paarn:
And i'm not exactly sure if i get what you intended to say with your second sentence? Can you please elaborate your thought a bit more in depth?

extern TwoWire Wire1; is in Wire.h. you don't need to add it to sketch. if it wouldn't be in Wire.cpp, only adding extern declaration would generate a linker error, because the object would not be defined.