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) exampleapplications/iot-solution/demo_mpu6050; the code can be found directly in the local SDK.
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 gives0x69. 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.
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:
cdis the “change directory” command — entering the MPU6050 example project directory; all subsequentmakebuild andmake flashflash 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 |
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 in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (the program file) the board can run;-j8builds 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 — runcd toolchain/riscv/Linux && . chmod755.shfirst, then rebuild.
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 flashis the “flash” command, writing the compiled firmware into the board’s chip. Afterp=comes the serial device (change it to your computer’s actual one — check withls /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.
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 about0; all three gyroscope values near0(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, address0x68, this tutorial's default) /MPU6050_ADDRESS_AD0_HIGH(AD0 high, address0x69). 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, requireddps: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 asprintf, required...: variadic args matching thefmtplaceholders; 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 viapdMS_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
#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.

