Overview
In this tutorial, we will be Interfacing the BMI160 Accelerometer & Gyroscope with Arduino Board. You can also use BMI160 with ESP32 microcontroller and Raspberry Pi Pico with BMI160. The BMI160 is a small, low-power, low-noise 16-bit inertial measurement unit that integrates a 3-axis accelerometer and 3-axis gyroscope. It provides precise motion sensing for applications like augmented reality, fitness tracking, and indoor navigation.
This tutorial provides detailed information about the BMI160 Accelerometer Gyroscope Module, including its specifications and technical details. First, we will interface the BMI160 with an Arduino Nano board to read the raw acceleration and gyroscope values. Next, we will write an auto-calibration code to use with the acceleration measurement code to measure acceleration across all three axes. Finally, in the third example, we will use the gyroscope data to measure tilt angles such as pitch and roll.
Bill of Materials
We will need the following components for this tutorial. You can purchase all the components from the given links.
| S.N. | Components Name | Quantity | Purchase Link |
|---|---|---|---|
| 1 | Arduino Nano Board | 1 | Amazon | AliExpress |
| 2 | BMI160 Accelerometer Gyroscope | 1 | Amazon | AliExpress |
| 3 | Connecting Wires | 10 | Amazon | AliExpress |
| 4 | Breadboard | 1 | Amazon | AliExpress |
BMI160 3 Axis Accelerometer & Gyroscope
The BMI160 is a high-performance, small, and ultra-low-power 16-bit Inertial Measurement Unit (IMU) that combines a 3-axis accelerometer and a 3-axis gyroscope in a single package.

It is designed to deliver accurate and reliable motion sensing for a wide range of applications, such as augmented reality, indoor navigation, wearable devices, and gaming. The sensor offers advanced features like motion detection, step counting, and gesture recognition, making it versatile for modern technology needs.
The BMI160 supports both I2C and SPI interfaces for communication, making it compatible with a variety of microcontrollers and processors. It has a compact footprint of just 2.5 x 3.0 x 0.8 mm³, which is smaller than many competing IMUs, such as the MPU6050, and is well-suited for space-constrained devices like wearables. Refer to Bosch BMI160 Datasheet for more information.
Technical Specifications of BMI160
- Operating Voltage: 1.71 – 3.6 V (Breakout Board operates between 3.2V~6V)
- Power Consumption:
- Gyroscope: 900 µA (full operation)
- Gyroscope + Accelerometer: 950 µA (full operation)
- Accelerometer: 180 µA (full operation)
- Suspend Mode: 3 µA
- Motion Detection: 200 µA
- Acceleration Range: ±2g/±4g/±8g/±16g
- Gyroscopes Range: ±125°/s,±250°/s,±500°/s,±1000°/s,±2000°/s
- Acceleration Zero-g Offset: ±40mg
- Gyroscopes Zero-g Offset: ±10°/s
- Interfaces:
- Primary: I2C or SPI
- Secondary: High-speed SPI for optical image stabilization
- Noise Density:
- Accelerometer: 180 µg/√Hz
- Gyroscope: 0.008 °/s/√Hz
- Programmable Frequency: 25/32Hz~1600Hz
- 6D Detection and Location
- 16-bit Data Output
- Shock Resistance: 1000gx 200us
- 2 Independent Programmable Interrupt Generators
- In-built 1024 Byte FIFO
Applications of BMI160
- Augmented Reality: Tracks motion for virtual environments.
- Indoor Navigation: Supports step counting and positioning.
- Mobile Devices: For smartphones and smartwatches.
- Wearables: Powers fitness and health devices.
- Gaming: Tracks motion in controllers.
- Cameras: Stabilizes images.
- Drones & Toys: Enables motion sensing.
Pinout of BMI160

| Pin | Name | Description |
|---|---|---|
| VIN | VIN | Power input pin. Accepts 3.3V to 5V and steps it down for the BMI160 using an onboard regulator. |
| 3.3V | 3.3V | Regulated 3.3V output pin. |
| GND | GND | Ground pin. Connect to the ground of your system. |
| OCS | OCS | Optional Chip Select for secondary SPI interface (used in optical image stabilization). |
| INT1 | INT1 | Interrupt pin 1. Used for motion events or data-ready notifications. |
| INT2 | INT2 | Interrupt pin 2. An additional interrupt pin for more advanced use cases. |
| SCL/SCX | SCL or SCX | I2C clock line when using I2C communication. Acts as SPI clock (SCX) in SPI mode. |
| SDA/SDX | SDA or SDX | I2C data line when using I2C communication. Acts as SPI data (SDX) in SPI mode. |
| CS | CS | Chip Select pin for SPI communication. Used to select the BMI160 during SPI transactions. |
| SA0 | SA0 | I2C address selection pin. Connect to GND for address 0x68 or to 3.3V for 0x69. |
Comparison Between MPU6050 and BMI160
The BMI160 is more advanced and power-efficient compared to the MPU6050. It also includes enhanced features like gesture recognition and step counting, which are absent in the MPU6050.
While the MPU6050 is still useful for basic motion sensing, the BMI160 outperforms it in terms of precision, power efficiency, and advanced capabilities. Here is the detailed comparison table:
| Feature | MPU6050 | BMI160 |
|---|---|---|
| Power Consumption | Higher: ~3.6 mA in full operation | Lower: ~950 µA (gyro + accel) |
| FIFO Buffer Size | 512 bytes | 1024 bytes |
| Accelerometer Resolution | 14-bit | 16-bit |
| Gyroscope Resolution | 16-bit | 16-bit |
| Integrated Features | Basic motion sensing | Advanced features like gesture recognition, step counting, and motion interrupts |
| Noise Characteristics | Higher noise in accelerometer and gyro | Lower noise, better signal clarity |
| Communication Speed | Standard I2C | I2C and High-speed SPI for demanding applications |
| Package Size | Larger (4 x 4 x 1 mm) | Smaller (2.5 x 3.0 x 0.8 mm) |
| Power Modes | Limited low-power options | Multiple ultra-low-power modes (3 µA suspend) |
| Operating Voltage Range | 2.375V – 3.46V | 1.71V – 3.6V |
| Sensor Integration | Separate accelerometer and gyroscope data streams | Seamless integration with synchronization |
| Target Applications | General-purpose motion sensing | Optimized for mobile, wearables, AR, and IoT |
Interfacing BMI160 Accelerometer & Gyroscope with Arduino
Let us interface the BMI160 Gyroscope Accelerometer module with Arduino Nano Board. We can either use the module in SPI Mode or in I2C Mode. For this tutorial, we are using the I2C Mode as it is very easy to configure and requires less wiring as well.
Here is an interfacing circuit diagram for the connection between BMI160 and Arduino Nano Board.

| BMI160 Pin | Arduino Nano Pin | Description |
|---|---|---|
| VIN | 5V | Power input for the BMI160. |
| GND | GND | Ground connection. |
| SCL | A5 | I2C Clock line. |
| SDA | A4 | I2C Data line. |
| SA0 | GND | Sets I2C address to 0x68. |
The BMI160 module is powered via the 5V pin from the Arduino Nano. The onboard voltage regulator steps it down to 3.3V for the sensor. Here we have connected the SA0 Pin to the GND. Connecting SA0 to GND sets the I2C address to 0x68. If connected to 3.3V, the I2C address would change to 0x69.

Use a simple breadboard and connect as shown in the circuit diagram above.
Code: Reading the Raw Acceleration and Gyroscope Data
After hardware connection is done, now lets write an Arduino C++ Code to read the Raw Acceleration and Gyroscope Data from BMI160 Module.
To get the BMI160 readings, a very well-written library is available. Download the BMI160 Library maintained by Hanyazou and add it to Arduino IDE via library manager.
The following code initializes the BMI160 sensor over I2C, reads raw accelerometer and gyroscope data and prints them to the Serial Monitor for real-time monitoring.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <BMI160Gen.h> //https://github.com/hanyazou/BMI160-Arduino #include <Wire.h> // I2C Configuration for ESP32 const int i2c_addr = 0x68; // Default I2C address for BMI160 void setup() { // Initialize Serial communication at 115200 baud rate Serial.begin(9600); while (!Serial); // Wait for Serial Monitor to connect (not required on ESP32) // Initialize I2C with custom SDA and SCL pins Wire.begin(); // Initialize the BMI160 device in I2C mode if (!BMI160.begin(BMI160GenClass::I2C_MODE, i2c_addr)) { Serial.println("BMI160 initialization failed!"); while (1); // Halt if initialization fails } Serial.println("BMI160 initialized successfully in I2C mode!"); } void loop() { int gx, gy, gz; // Raw gyroscope values int ax, ay, az; // Raw accelerometer values // Read raw gyroscope measurements from the BMI160 BMI160.readGyro(gx, gy, gz); // Read raw accelerometer measurements from the BMI160 BMI160.readAccelerometer(ax, ay, az); // Display the gyroscope values (X, Y, Z) on the Serial Monitor Serial.print("Gyroscope Data (X, Y, Z): "); Serial.print(gx); Serial.print(", "); Serial.print(gy); Serial.print(", "); Serial.println(gz); // Display the accelerometer values (X, Y, Z) on the Serial Monitor Serial.print("Accelerometer Data (X, Y, Z): "); Serial.print(ax); Serial.print(", "); Serial.print(ay); Serial.print(", "); Serial.println(az); delay(100); // Delay for readability (500ms) } |
Copy the above code and paste it on your Arduino editor window. From the board manager select Arduino Nano Board and also the COM port. Then upload the code.
After code uploading is done, open the Serial Monitor. The Serial Monitor will show the raw data for acceleration and gyroscope from BMI160 Module.
The above value is shown when BMI160 is placed on a flat table and there is no any movement or vibration.
When the sensor is shaken or moved or tilted, there is massive change in values as shown in the image below.
You may rotate and shake the BMI160 Module to observe the change in the readings.
This is how you can use the BMI160 Accelerometer Gyroscope Module with Arduino to read the raw values.
Code: Acceleration Measurement with BMI160 & Arduino
Using the BMI160 Accelerometer and Gyroscope Module, we can develop an Arduino C++ Code to read the Acceleration values. We have converted the raw readings into acceleration using the mathematical equations.
This code initializes the BMI160 sensor using I2C communication. It sets the accelerometer to normal mode. The code performs auto-calibration to reduce noise and errors. It calculates offsets for the accelerometer during calibration.
After calibration, it reads raw accelerometer data from the X, Y, and Z axes. The raw values are converted to acceleration in m/s² and displayed on the Serial Monitor.
Raw values are converted to 𝑚/𝑠^2 using the formula:
Sensitivity for ±2g is 16384 LSB/g. Adjust if you use a different range.
The Z-axis also accounts for the effect of gravity during calibration. The final corrected acceleration values are displayed on the Serial Monitor.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include <Wire.h> #define BMI160_I2C_ADDRESS 0x68 #define ACCEL_SENSITIVITY 16384.0 // Sensitivity for ±2g in LSB/g (adjust based on your configuration) void setup() { Serial.begin(9600); // Initialize Serial communication Wire.begin(); // Initialize I2C communication // Initialize BMI160 accelerometer Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x7E); // Command register Wire.write(0x11); // Set accelerometer to normal mode Wire.endTransmission(); delay(100); // Perform accelerometer auto-calibration autoCalibrateAccelerometer(); Serial.println("BMI160 Initialized and Calibrated"); } void loop() { int16_t ax, ay, az; // Read accelerometer data Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x12); // Start register for accelerometer data Wire.endTransmission(false); Wire.requestFrom(BMI160_I2C_ADDRESS, 6); if (Wire.available() == 6) { ax = (Wire.read() | (Wire.read() << 8)); ay = (Wire.read() | (Wire.read() << 8)); az = (Wire.read() | (Wire.read() << 8)); } // Convert raw accelerometer values to m/s^2 float ax_mps2 = ax * (9.81 / ACCEL_SENSITIVITY); float ay_mps2 = ay * (9.81 / ACCEL_SENSITIVITY); float az_mps2 = az * (9.81 / ACCEL_SENSITIVITY); // Print accelerometer values in m/s^2 Serial.print("Accel (m/s^2): "); Serial.print(ax_mps2, 2); Serial.print(", "); Serial.print(ay_mps2, 2); Serial.print(", "); Serial.println(az_mps2, 2); delay(100); } void autoCalibrateAccelerometer() { // Configure accelerometer for auto-calibration Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x7E); // Command register Wire.write(0x37); // Start accelerometer offset calibration Wire.endTransmission(); delay(100); // Wait for calibration to complete delay(1000); Serial.println("Accelerometer Auto-Calibration Complete"); } |
After the code uploading is done, the device is ready for testing. Open your serial monitor to see the results. Initially it will calculate the offsets and do the calibration. Then it will start printing the acceleration values in x, y, z axis.
When the device is at rest with no any shaking or trembling movements, the acceleration in x and y axis is almost zero. The acceleration in z axis will be almost 9.8 due to the gravity.
When you rotate, shake, tremble or move the BMI160 Sensor, the acceleration values changes drastically.
The reading changes are very quick and highly sensitive.
This is how you can use the BMI160 Gyroscope Accelerometer module with Arduino to measure the Acceleration.
Code: Tilt Angle Measurement with BMI160 & Arduino
As we measured acceleration above, we can also use the BMI160 Module with Arduino to measure the tilt angles also called as Pitch, Roll, Yaw. But in the code, we are only measuring the Pitch and Roll. The Yaw measurement requires magnetometer.
The following code initializes the BMI160 sensor using I2C communication. It sets the accelerometer to normal mode. The code performs auto-calibration to calculate offsets and reduce noise. This helps improve the accuracy of tilt angle measurements.
After calibration, it reads accelerometer data from the X, Y, and Z axes. It calculates the tilt angles (pitch, roll, and yaw) based on the accelerometer readings.
The angle of rotation around the X-axis is called as Pitch which is calculated using the following formula:
The angle of rotation around the Y-axis is called as Roll which is calculated using the following formula:
Finally, the pitch and roll values are printed to the Serial Monitor in degrees.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <Wire.h> #define BMI160_I2C_ADDRESS 0x68 #define ACCEL_SENSITIVITY 16384.0 // Sensitivity for ±2g in LSB/g (adjust based on your configuration) void setup() { Serial.begin(9600); // Initialize Serial communication Wire.begin(); // Initialize I2C communication // Initialize BMI160 accelerometer Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x7E); // Command register Wire.write(0x11); // Set accelerometer to normal mode Wire.endTransmission(); delay(100); // Perform accelerometer auto-calibration autoCalibrateAccelerometer(); Serial.println("BMI160 Initialized and Calibrated"); } void loop() { int16_t ax, ay, az; // Read accelerometer data Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x12); // Start register for accelerometer data Wire.endTransmission(false); Wire.requestFrom(BMI160_I2C_ADDRESS, 6); if (Wire.available() == 6) { ax = (Wire.read() | (Wire.read() << 8)); ay = (Wire.read() | (Wire.read() << 8)); az = (Wire.read() | (Wire.read() << 8)); } // Convert raw accelerometer values to g float ax_g = ax / ACCEL_SENSITIVITY; float ay_g = ay / ACCEL_SENSITIVITY; float az_g = az / ACCEL_SENSITIVITY; // Calculate tilt angles (pitch and roll) in degrees float pitch = atan2(ay_g, sqrt(ax_g * ax_g + az_g * az_g)) * 180.0 / PI; float roll = atan2(-ax_g, az_g) * 180.0 / PI; // Print tilt angles Serial.print("Pitch: "); Serial.print(pitch, 2); Serial.print("°, Roll: "); Serial.print(roll, 2); Serial.println("°"); delay(100); } void autoCalibrateAccelerometer() { // Configure accelerometer for auto-calibration Wire.beginTransmission(BMI160_I2C_ADDRESS); Wire.write(0x7E); // Command register Wire.write(0x37); // Start accelerometer offset calibration Wire.endTransmission(); delay(100); // Wait for calibration to complete delay(1000); Serial.println("Accelerometer Auto-Calibration Complete"); } |
Upload the above code to the Arduino board and then Open the Serial Monitor.
When the Serial Monitor is opened, the code initializes the BMI160, performs calibration to calculate accelerometer offsets, and then continuously prints the calculated pitch and roll angles in degrees.
When the sensor is not moved or tilted, the pitch and roll angles are almost zero.
Shake or tilt the BMI160 module, you will observe the changes in Pitch and Roll angles.
All the values printed ranges from -180 to +180 in degrees.
This is how you can use the BMI160 Gyroscope Accelerometer module with Arduino to measure the Tilt Angles (Pitch & Roll).

















