In this blog, we will explore how to interface the MPU6050 accelerometer and gyroscope sensor with the Raspberry Pi Pico W using C++. If you are new to setting up the Raspberry Pi Pico W with the C++ SDK, you can refer to my previous blog How to Write Your First C++ Script on the Raspberry Pi Pico W for detailed instructions.
Introduction
The MPU6050 is a versatile sensor that combines a 3-axis gyroscope and a 3-axis accelerometer. In this tutorial, we’ll go through the process of setting up the sensor, configuring it, and reading the data from it. We’ll also cover the necessary configuration of the Raspberry Pi Pico W to communicate with the MPU6050 over the I2C protocol.
— — -
Before we delve into the topic, we invite you to support our ongoing efforts and explore our various platforms dedicated to enhancing your IoT projects:
- Subscribe to our YouTube Channel: Stay updated with our latest tutorials and project insights by subscribing to our channel at YouTube — Shilleh.
- Support Us: Your support is invaluable. Consider buying me a coffee at Buy Me A Coffee to help us continue creating quality content.
- Hire Expert IoT Services: For personalized assistance with your IoT projects, hire me on UpWork.
ShillehTek Website (Exclusive Discounts):
https://shillehtek.com/collections/all
ShillehTekAmazon Store:
ShillehTek Amazon Store — Canada
ShillehTek Amazon Store — Japan
Prerequisites
- Raspberry Pi Pico W
- MPU6050 sensor
- Connecting wires
- Breadboard
- CMake and C++ development environment set up for Raspberry Pi Pico W (refer to the previous blog for setup details)
Wiring Information
Connecting the MPU6050 to the Raspberry Pi Pico W requires four connections: VCC (3.3V), GND, SDA, and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and GPIO 5 (SCL) in software. Power is supplied from the 3.3V pin.
Note: There are many different manufacturers who sell boards with the MPU6050. While they may appear slightly different, they all have at least the same four pins required to power and communicate with the sensor. When wiring up a board that looks different from the one in the diagram, ensure you connect the pins as described.
Steps for Connecting the MPU6050
- Connect VCC: Connect the VCC pin of the MPU6050 to the 3.3V pin on the Raspberry Pi Pico.
- Connect GND: Connect the GND pin of the MPU6050 to a ground (GND) pin on the Raspberry Pi Pico.
- Connect SDA: Connect the SDA pin of the MPU6050 to GPIO 4 (SDA) on the Raspberry Pi Pico.
- Connect SCL: Connect the SCL pin of the MPU6050 to GPIO 5 (SCL) on the Raspberry Pi Pico.
CMakeLists.txt File
First, let’s look at the CMakeLists.txt
file, which is crucial for building the project.
cmake_minimum_required(VERSION 3.13)
# Include the Pico SDK using the environment variable
include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
project(mpu6050_i2c_example)
# Initialize the Raspberry Pi Pico SDK
pico_sdk_init()
# Create an executable for the project
add_executable(mpu6050_i2c_example
main.cpp
)
# Link the necessary libraries
target_link_libraries(mpu6050_i2c_example pico_stdlib hardware_i2c)
# Enable USB output
pico_enable_stdio_usb(mpu6050_i2c_example 1)
# Disable UART output
pico_enable_stdio_uart(mpu6050_i2c_example 0)
# Create map/bin/hex/uf2 files
pico_add_extra_outputs(mpu6050_i2c_example)
Explanation of CMakeLists.txt
- pico_sdk_init.cmake: This line includes the Pico SDK, allowing us to use its functionalities.
- pico_sdk_init(): This initializes the Pico SDK for the project.
-
add_executable: This line specifies the
main.cpp
file as the source file for the executable. -
target_link_libraries: This links the necessary libraries (
pico_stdlib
for standard Pico functions andhardware_i2c
for I2C communication). - pico_enable_stdio_usb: This enables USB output, allowing us to use USB for serial communication.
- pico_add_extra_outputs: This generates additional output files such as .uf2, .bin, and .hex files which are useful for flashing the firmware.
main.cpp File
Now, let’s dive into the main.cpp
file, which contains the actual code to interface with the MPU6050.
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include <stdio.h>
// I2C defines
#define I2C_PORT i2c0
#define MPU6050_ADDR 0x68
// MPU6050 register addresses
#define REG_PWR_MGMT_1 0x6B
#define REG_ACCEL_XOUT_H 0x3B
#define REG_GYRO_CONFIG 0x1B
#define REG_ACCEL_CONFIG 0x1C
#define REG_SMPLRT_DIV 0x19
#define WHO_AM_I_REG 0x75
// Sensitivity scale factors for different ranges
#define ACCEL_SCALE_FACTOR_2G 16384.0 // for ±2g
#define ACCEL_SCALE_FACTOR_4G 8192.0 // for ±4g
#define ACCEL_SCALE_FACTOR_8G 4096.0 // for ±8g
#define ACCEL_SCALE_FACTOR_16G 2048.0 // for ±16g
#define GYRO_SCALE_FACTOR_250DPS 131.0 // for ±250 degrees per second
#define GYRO_SCALE_FACTOR_500DPS 65.5 // for ±500 degrees per second
#define GYRO_SCALE_FACTOR_1000DPS 32.8 // for ±1000 degrees per second
#define GYRO_SCALE_FACTOR_2000DPS 16.4 // for ±2000 degrees per second
// Select the desired scale factor
#define ACCEL_SCALE_FACTOR ACCEL_SCALE_FACTOR_4G // Change this to the desired accelerometer range
#define GYRO_SCALE_FACTOR GYRO_SCALE_FACTOR_250DPS // Change this to the desired gyroscope range
// Corresponding configuration values
#define ACCEL_CONFIG_VALUE 0x08 // for ±4g
#define GYRO_CONFIG_VALUE 0x00 // for ±250 degrees per second
#define SAMPLE_RATE_DIV 1 // Sample rate = 1kHz / (1 + 1) = 500Hz
void mpu6050_reset() {
uint8_t reset[] = {REG_PWR_MGMT_1, 0x80};
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, reset, 2, false);
sleep_ms(200);
uint8_t wake[] = {REG_PWR_MGMT_1, 0x00};
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, wake, 2, false);
sleep_ms(200);
}
void mpu6050_configure() {
// Set accelerometer range
uint8_t accel_config[] = {REG_ACCEL_CONFIG, ACCEL_CONFIG_VALUE};
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, accel_config, 2, false);
// Set gyroscope range
uint8_t gyro_config[] = {REG_GYRO_CONFIG, GYRO_CONFIG_VALUE};
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, gyro_config, 2, false);
// Set sample rate
uint8_t sample_rate[] = {REG_SMPLRT_DIV, SAMPLE_RATE_DIV};
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, sample_rate, 2, false);
}
void mpu6050_read_raw(int16_t accel[3], int16_t gyro[3], int16_t *temp) {
uint8_t buffer[14];
uint8_t reg = REG_ACCEL_XOUT_H;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, ®, 1, true);
i2c_read_blocking(I2C_PORT, MPU6050_ADDR, buffer, 14, false);
accel[0] = (buffer[0] << 8) | buffer[1];
accel[1] = (buffer[2] << 8) | buffer[3];
accel[2] = (buffer[4] << 8) | buffer[5];
*temp = (buffer[6] << 8) | buffer[7];
gyro[0] = (buffer[8] << 8) | buffer[9];
gyro[1] = (buffer[10] << 8) | buffer[11];
gyro[2] = (buffer[12] << 8) | buffer[13];
}
int main() {
// Initialize chosen serial port
stdio_init_all();
// Initialize I2C
i2c_init(I2C_PORT, 400 * 1000);
gpio_set_function(4, GPIO_FUNC_I2C);
gpio_set_function(5, GPIO_FUNC_I2C);
gpio_pull_up(4);
gpio_pull_up(5);
// Reset and configure MPU6050
mpu6050_reset();
mpu6050_configure();
uint8_t who_am_i = 0;
uint8_t reg = WHO_AM_I_REG;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, ®, 1, true);
i2c_read_blocking(I2C_PORT, MPU6050_ADDR, &who_am_i, 1, false);
printf("MPU6050 WHO_AM_I: 0x%02X\n", who_am_i);
if (who_am_i != 0x68) {
printf("MPU6050 not found!\n");
while (1);
}
int16_t accel[3], gyro[3], temp;
while (1) {
mpu6050_read_raw(accel, gyro, &temp);
// Convert raw accelerometer values to g
float accel_g[3];
accel_g[0] = accel[0] / ACCEL_SCALE_FACTOR;
accel_g[1] = accel[1] / ACCEL_SCALE_FACTOR;
accel_g[2] = accel[2] / ACCEL_SCALE_FACTOR;
// Convert raw gyroscope values to degrees per second
float gyro_dps[3];
gyro_dps[0] = gyro[0] / GYRO_SCALE_FACTOR;
gyro_dps[1] = gyro[1] / GYRO_SCALE_FACTOR;
gyro_dps[2] = gyro[2] / GYRO_SCALE_FACTOR;
// Print converted values
printf("aX = %.2f g | aY = %.2f g | aZ = %.2f g | gX = %.2f dps | gY = %.2f dps | gZ = %.2f dps | temp = %.2f°C\n",
accel_g[0], accel_g[1], accel_g[2], gyro_dps[0], gyro_dps[1], gyro_dps[2], temp / 340.00 + 36.53);
sleep_ms(500);
}
return 0;
}
Explanation of main.cpp
Includes and Definitions:
- We include necessary headers for standard library functions and I2C communication.
- Define I2C port and MPU6050 I2C address.
- Define MPU6050 register addresses and scale factors for different ranges.
Sensitivity Scale Factors:
- Accelerometer:
- ±2g: 16384 LSB/g
- ±4g: 8192 LSB/g
- ±8g: 4096 LSB/g
- ±16g: 2048 LSB/g
- Gyroscope:
- ±250 dps: 131 LSB/dps
- ±500 dps: 65.5 LSB/dps
- ±1000 dps: 32.8 LSB/dps
- ±2000 dps: 16.4 LSB/dps
These scale factors are used to convert the raw sensor readings to physical units. For example, with an accelerometer range of ±4g, a raw reading of 8192 corresponds to 1g.
mpu6050_reset() Function:
- This function resets the MPU6050 by writing to the power management register. After resetting, it wakes up the device by clearing the sleep bit.
mpu6050_configure() Function:
- This function configures the accelerometer and gyroscope ranges and sets the sample rate divider. The sample rate is set by writing to the SMPLRT_DIV register. You can change the
ACCEL_CONFIG_VALUE
andGYRO_CONFIG_VALUE
to set different ranges for the accelerometer and gyroscope.
mpu6050_read_raw() Function:
- This function reads raw accelerometer, gyroscope, and temperature data from the MPU6050. It writes the starting register address and then reads the data from the sensor.
main() Function:
- Initializes the standard I/O and I2C communication.
- Calls the reset and configure functions to prepare the MPU6050.
- Checks the WHO_AM_I register to verify communication with the MPU6050.
- Continuously reads raw data from the MPU6050, converts it to physical units (g for acceleration and dps for gyroscope), and prints the results.
Conclusion
By following this guide, you have successfully interfaced the MPU6050 sensor with the Raspberry Pi Pico W using C++. You can now read and process accelerometer and gyroscope data for various applications. Adjust the scale factors and sample rates as needed for your specific use case.
For more detailed steps on setting up the C++ SDK for the Raspberry Pi Pico W, refer to my previous blog How to Write Your First C++ Script on the Raspberry Pi Pico W.