Skip to content

Overview

The MPU6050 is the classic six-axis motion sensor; six-axis = 3-axis accelerometer (measures the acceleration the object feels in the X/Y/Z directions, in g — 1g≈9.8m/s², the acceleration of gravity) + 3-axis gyroscope (measures how fast the object rotates around the three axes, in degrees/second). It's the core chip behind a phone's "auto-rotate screen". This tutorial uses the Ai-WB2 development board to read the MPU6050's 6 values over the I2C bus (a two-wire serial communication — one clock SCL, one data SDA), printing them on the serial in real time, and walks through the full flow of wiring → coding → building → flashing (writing the compiled program into the board's chip) → running and verification.

In plain words: inside the MPU6050 there's "a scale" (the accelerometer) and "a spinning top" (the gyroscope). Phone auto-rotate relies on it: lying flat, the "scale" weighs about 1g pulling straight down; stand the phone up, and the direction the "scale" feels the force changes — the system judges the phone's attitude from that. This tutorial reads both data sets out to the serial: with the module lying flat, the accelerometer Z axis should read about 1.0g, the other axes about 0; turn the module, and the gyroscope values follow.

This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version release_bl_iot_sdk_1.6.40) example applications/iot-solution/demo_mpu6050; the code can be found directly in the local SDK.

🎯Page GoalRead the MPU6050 six-axis data (accelerometer g + gyroscope degrees/second) over I2C, print on the serial every second, and understand register-based I2C devices and multi-file driver project structure.
🧰Prerequisites① An Ai-WB2 development board + an MPU6050 six-axis sensor module ② Development environment set up per [SDK Installation](../sdk/sdk_intro), and [GPIO Output (LED)](../basic/gpio_led) done.
🔗RelatedI2C fundamentals: [I2C Protocol](../basic/i2c); other I2C sensors: [SHT30 Temperature and Humidity Sensor](./sht3x) and [BH1750 Light Sensor](./bh1750).

Hardware Wiring

Wire per the official example (see the SDK’s applications/iot-solution/demo_mpu6050/README.md), using dupont wires (jumper wires with pins at both ends):

Ai-WB2 Pin MPU6050 Pin
IO4 SCL
IO5 SDA
3V3 VCC
GND GND

💡 The MPU6050 is an I2C device — VCC to 3.3V (most modules have onboard regulators and pull-ups, more stable power). 💡 The AD0 pin decides the I2C address (door number): AD0 to GND/floating gives address 0x68, AD0 to 3V3 gives 0x69. This tutorial’s code defaults to AD0 low (MPU6050_ADDRESS_AD0_LOW); if your module’s AD0 is tied high, you need to modify the code. 💡 Note this example’s I2C clock is only 25kHz (freq = 25000) — slower than 100kHz; the driver uses this conservative setting to tolerate long wires and reduce interference. No need to change it.

Enter the Example Project

This tutorial directly uses the demo_mpu6050 example project shipped with the official SDK; open a terminal and enter it:

cd ~/Ai-Thinker-WB2/applications/iot-solution/demo_mpu6050

Note: cd is the “change directory” command — entering the MPU6050 example project directory; all subsequent make build and make flash flash commands must run in this directory first.

This is a multi-file project: main program + the official ported MPU6050 driver library (from the open-source LibDriver, reading/writing the chip’s internal registers over I2C):

File Purpose
demo_mpu6050/main.c Main program source, the main file this tutorial looks at
demo_mpu6050/driver_mpu6050_basic.c/h Basic read/write interface (init, read six-axis data)
demo_mpu6050/driver_mpu6050.c/h Register-level driver core (reads/writes the chip’s internal registers)
demo_mpu6050/driver_mpu6050_fifo.c/h Data FIFO (first-in-first-out buffer) feature, not used in this tutorial
demo_mpu6050/driver_mpu6050_dmp.c/h DMP (the chip’s built-in attitude-solving engine), not used in this tutorial
demo_mpu6050/driver_mpu6050_interface_bl602.c Platform adaptation layer: hooks the I2C reads/writes to the WB2’s IO4/IO5
Makefile Build entry, usually no changes needed
Write the Code

Open demo_mpu6050/main.c — the complete code for this step has been moved to the end of this page:

📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (applications/iot-solution/demo_mpu6050/demo_mpu6050/main.c).

Code highlights:

Code Purpose
mpu6050_basic_init(MPU6050_ADDRESS_AD0_LOW) Initializes the chip at the AD0-low address (0xD0 >> 1 = 0x68) and configures ranges (accelerometer ±2g, gyroscope ±2000°/s); address mismatching the wiring = chip not found
mpu6050_basic_read(accel, gyro) Reads the 3-axis accelerometer (g) and 3-axis gyroscope (degrees/second), 3 floats each; a failed read returns 1
blog_info("accel: %.2f %.2f %.2f\tgyro: ...", ...) Prints the 6 values on the serial; no print = no visible result
vTaskDelay(pdMS_TO_TICKS(1000)) Samples every 1 second; faster just occupies the I2C bus and the data barely changes, no point
Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, turning code into firmware (the program file) the board can run; -j8 builds with 8 parallel CPU cores, faster.

On success a firmware build_out/demo_mpu6050.bin is generated.

⚠️ If it reports riscv64-unknown-elf-gcc: command not found, the toolchain permissions aren’t configured — run cd toolchain/riscv/Linux && . chmod755.sh first, then rebuild.

Flash the Firmware

Keep the board connected via USB, confirm the serial device (usually /dev/ttyUSB0 on Linux), and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the board’s chip. After p= comes the serial device (change it to your computer’s actual one — check with ls /dev/ttyUSB*), b= is the flash baud rate (transfer speed).

⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode (some boards enter automatically); wait for the progress bar to complete — that means the flash succeeded. For flashing on Windows, see Windows Quick Start.

Run and Verify

After flashing, the board automatically restarts and runs; open the serial assistant (baud rate 921600 — the baud rate is the serial transfer speed, both ends must be set the same) and six-axis data prints every 1 second:

accel: -0.01 0.02 1.00	gyro: 0.01 -0.02 0.03
accel: -0.01 0.02 1.00	gyro: 0.02 -0.01 0.04
...

What to watch:

  • With the module lying still and flat, the accelerometer Z axis (the 3rd value) should read about 1.00 (gravity 1g), the X/Y axes about 0; all three gyroscope values near 0 (not moving = not rotating).
  • Flip/rotate the module: the accelerometer values change with attitude, the gyroscope values change with rotation speed.

Seeing the serial print the accel/gyro data pairs every second, with values changing as the module moves, means success; if it keeps printing all zeros or prints nothing, it hasn’t succeeded yet — check the FAQ at the end.

💡 Watch the IO4/IO5 waveforms with a logic analyzer to see the I2C register read/write timing (see the official example’s img/logic_analyzer.jpg).


API Summary for This Tutorial

mpu6050_basic_init(addr_pin)

Initializes the MPU6050 at the given I2C address: configures the clock source, sample rate, low-pass filter, accelerometer range (default ±2g) and gyroscope range (default ±2000°/s).

Parameters:

  • addr_pin: I2C device address enum, values: MPU6050_ADDRESS_AD0_LOW (AD0 low, address 0x68, this tutorial's default) / MPU6050_ADDRESS_AD0_HIGH (AD0 high, address 0x69). Init fails if it doesn't match the hardware wiring

Return: 0 on success; 1 on failure (e.g. chip not responding). Driver implementation in demo_mpu6050/driver_mpu6050_basic.c

mpu6050_basic_read(g, dps)

Reads the six-axis raw values back from the chip's data registers and converts to physical quantities: accelerometer in g (gravity acceleration), gyroscope in degrees/second.

Parameters:

  • g: float g[3] output array, receives the X/Y/Z accelerations (g) in order, required
  • dps: float dps[3] output array, receives the X/Y/Z angular velocities (degrees/second) in order, required

Return: 0 on success; 1 on failure (read failure). Driver implementation in demo_mpu6050/driver_mpu6050_basic.c

blog_info(fmt, ...)

Prints an INFO-level log (UART0, subject to level filtering); this tutorial prints the six-axis data with it.

Parameters:

  • fmt: format string, same usage as printf, required
  • ...: variadic args matching the fmt placeholders; can be omitted

Return: none

vTaskDelay(ms)

Suspends the current task for the given milliseconds, yielding the CPU to other tasks.

Parameters:

  • ms: delay in milliseconds, values: any non-negative integer (internally converted to system ticks via pdMS_TO_TICKS)

Return: none


Full Code

Below is the complete demo_mpu6050/main.c source, identical to the official example (applications/iot-solution/demo_mpu6050/demo_mpu6050/main.c). This is a multi-file project; the rest of the driver files (driver_mpu6050*.c/h, driver_mpu6050_interface_bl602.c) live in the same directory, no changes needed:

📜 Click to expand the full demo_mpu6050/main.c code
c
#include <stdio.h>

#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "driver_mpu6050.h"
#include "driver_mpu6050_basic.h"

int main(void)
{
    mpu6050_basic_init(MPU6050_ADDRESS_AD0_LOW);
    
    for (;;) {
        float accel[3];
        float gyro[3];
        mpu6050_basic_read(accel, gyro);
        blog_info("accel: %.2f %.2f %.2f\tgyro: %.2f %.2f %.2f\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    return 0;
}

FAQ & Troubleshooting

⚠️ Keeps printing all zeros (no data)
Cause: wrong I2C wiring, the AD0 pin state doesn't match the code (address is 0x69 when tied high), or a damaged chip
Fix: check IO4/IO5/3V3/GND against the wiring table; confirm the module's AD0 is grounded or floating; if AD0 is tied to 3V3, change mpu6050_basic_init's argument to MPU6050_ADDRESS_AD0_HIGH and rebuild/reflash

⚠️ The serial prints nothing at all
Cause: wrong wiring, insufficient VCC supply, no common ground between the board and module (GND not connected), or poor dupont wire contact
Fix: confirm VCC to 3V3 and GND to GND (common ground = the two devices' grounds must be joined, otherwise voltage has no reference point); re-seat the dupont wires

⚠️ Values jump around, or not 1g at rest
Cause: the module sits in a shaky/vibrating environment, unstable supply, or the module isn't fixed down
Fix: lay the module flat on the desk and watch it still; confirm stable power; small accelerometer fluctuations (±0.05) are normal — if too large, check the supply

⚠️ Serial device not found or no permission
Cause: on Linux /dev/ttyUSB0 doesn't exist or permission denied; on Windows the USB-to-serial driver isn't installed
Fix: on Linux check with ls /dev/ttyUSB*; if permission denied run sudo usermod -aG dialout $USER and log back in; on Windows install the driver in Device Manager and confirm the COM port

⚠️ Flashing keeps waiting, progress bar doesn't move
Cause: download mode wasn't entered, or the cable only charges and can't transfer data
Fix: press and hold EN during flashing to enter download mode as prompted; try a Type-C data-capable cable

⚠️ make reports Makefile not found
Cause: the build command ran in the wrong directory (must be inside the example project directory)
Fix: run cd ~/Ai-Thinker-WB2/applications/iot-solution/demo_mpu6050 first, then make -j8

Self-Check

The serial prints the accel/gyro data pairs every second; flat at rest the accelerometer Z axis reads about 1.00g, and the values change as the module moves — the MPU6050 six-axis measurement is verified.

Released under the MIT License. Build Time 2026-09-11 14:52:23