Reference
MPU6050 Accelerometer + Gyroscope Complete Guide
The MPU6050 (GY-521) six-axis motion sensor: real specifications, pins, I2C addresses, wiring, code with raw registers, calibration, and the traps that give drifting readings.
- 3-axis gyro + 3-axis accel
- I2C 0x68 or 0x69
- 2.375 to 3.46 V chip supply
- 16-bit
The MPU6050 is a six-axis motion sensor from InvenSense (now TDK): a 3-axis gyroscope and a 3-axis accelerometer in one 4 × 4 × 0.9 mm chip, with a Digital Motion Processor (DMP) and a 1024-byte FIFO. It is usually sold on the purple GY-521 breakout. It tells you how a thing is tilted and how fast it turns, which makes it the basis of self-balancing robots, gimbals, drones and gesture projects.
This guide covers the real specifications from the InvenSense datasheet and register map, the pins of the GY-521 board, the I2C addresses, wiring, code with raw registers, calibration, and the traps that give drifting or wrong readings. The chip data comes from the MPU-6000/MPU-6050 product specification and register map (InvenSense). The board pins describe the common GY-521, and other boards may differ.
What is in the chip
| Item | Value |
|---|---|
| Gyroscope | 3 axes, full scale ±250, ±500, ±1000 or ±2000 °/s, 16-bit |
| Accelerometer | 3 axes, full scale ±2, ±4, ±8 or ±16 g, 16-bit |
| Temperature sensor | On-chip, −40 to 85 °C, for compensation |
| Digital Motion Processor | Runs sensor fusion on the chip |
| FIFO | 1024 bytes |
| Interface | I2C up to 400 kHz (MPU-6050). The MPU-6000 also has SPI |
| Auxiliary I2C | Extra I2C port for a magnetometer |
| Package | QFN 4 × 4 × 0.9 mm |
The MPU6050 does not have a magnetometer, so it cannot give an absolute compass heading. For a nine-axis solution look at the BNO055, which does sensor fusion on the chip.
Source: InvenSense MPU-6000/MPU-6050 product specification.
Getting started
Specifications
Sensor chip
| Supply voltage (VDD) | 2.375 to 3.46 V |
| Logic reference (VLOGIC) | 1.71 V up to VDD, 1.8 V ± 5 % or VDD |
| Gyroscope sensitivity | 131, 65.5, 32.8 and 16.4 LSB per °/s for the four ranges |
| Accelerometer sensitivity | 16384, 8192, 4096 and 2048 LSB per g for the four ranges |
| Gyroscope current | 3.6 mA operating |
| Total current | 3.9 mA with all six axes and the DMP on |
| Standby current | 5 µA |
| Temperature sensor | 340 LSB per °C, untrimmed, range −40 to 85 °C |
| Zero-rate offset variation | ±20 °/s over −40 to 85 °C |
| Accelerometer sensitivity change | ±0.02 % per °C |
| I2C address | 0x68 (AD0 low) or 0x69 (AD0 high) |
| Operating temperature | −40 to 85 °C |
GY-521 breakout (typical)
| Supply | Typically 3.3 to 5 V on VCC, through an on-board regulator |
| Pins | VCC, GND, SCL, SDA, XDA, XCL, AD0, INT |
| Size | Not given. Measure your own board |
Source: InvenSense MPU-6000/MPU-6050 product specification. The GY-521 data is typical and differs between sellers.
Dimensions
The chip is a QFN package of 4 × 4 mm and 0.9 mm high. The GY-521 breakout is a small board of roughly 21 × 16 mm in many shops, but sizes vary, so measure yours before you design a mount. Orientation matters: the chip has X, Y and Z axes marked in the datasheet, and the printed arrows on your board tell you which way is which.
Source: package size from the InvenSense datasheet. The board size is not from a datasheet.
Pins of the GY-521
| Pin | Type | Function |
|---|---|---|
| VCC | Power | Supply, 3.3 to 5 V on most boards (regulated on the board) |
| GND | Power | Ground |
| SCL | I2C | Clock |
| SDA | I2C | Data |
| XDA | I2C (aux) | Auxiliary data, to connect a magnetometer |
| XCL | I2C (aux) | Auxiliary clock |
| AD0 | Input | Address select: low 0x68, high 0x69 |
| INT | Output | Interrupt output, for data ready, motion or FIFO events |
The order and the pull-ups differ between boards, so check yours. Most GY-521 boards have pull-up resistors on SDA and SCL.
Source: InvenSense MPU-6000/MPU-6050 product specification, pin functions. The GY-521 layout is typical.
Wiring
| MPU6050 pin | ESP32 | Arduino Uno / Nano | Raspberry Pi |
|---|---|---|---|
| VCC | 3.3 V | 3.3 V | 3.3 V (pin 1) |
| GND | GND | GND | GND (pin 6) |
| SCL | GPIO22 | A5 | GPIO3 (pin 5) |
| SDA | GPIO21 | A4 | GPIO2 (pin 3) |
| INT | Any free GPIO (optional) | D2 (optional) | Any free GPIO (optional) |
| AD0 | GND (0x68) or 3.3 V (0x69) | GND or 5 V | GND or 3.3 V |
The chip runs at 3.46 V at most. The board has a regulator, but its I2C pull-ups may go to VCC, so with 5 V on VCC the SDA and SCL lines also swing to 5 V. For a 3.3 V ESP32, power the board from 3.3 V.
Software setup
| Platform | What to do |
|---|---|
| Arduino IDE | Install Adafruit MPU6050 from the Library Manager (it also installs Adafruit BusIO and Adafruit Unified Sensor) |
| PlatformIO | Add adafruit/Adafruit MPU6050 to lib_deps |
| ESP-IDF | Use the I2C master driver and the registers below |
Read the sensor
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
Adafruit_MPU6050 mpu;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050");
while (1) delay(10);
}
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
Serial.print("Accel (m/s^2): ");
Serial.print(a.acceleration.x); Serial.print(", ");
Serial.print(a.acceleration.y); Serial.print(", ");
Serial.println(a.acceleration.z);
Serial.print("Gyro (rad/s): ");
Serial.print(g.gyro.x); Serial.print(", ");
Serial.print(g.gyro.y); Serial.print(", ");
Serial.println(g.gyro.z);
delay(200);
}
I did not run this sketch on a board, it follows the library example.
Reading the registers yourself
You do not need a library to read the MPU6050. Wake the chip (it starts in sleep mode), then read 14 bytes from 0x3B: accelerometer X, Y, Z, temperature and gyroscope X, Y, Z, each a signed 16-bit value, high byte first.
#include <Wire.h>
#define MPU 0x68
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.beginTransmission(MPU);
Wire.write(0x6B); Wire.write(0x00); // PWR_MGMT_1: clear the sleep bit
Wire.endTransmission();
Wire.beginTransmission(MPU);
Wire.write(0x75); Wire.endTransmission(false);
Wire.requestFrom(MPU, 1);
Serial.printf("WHO_AM_I = 0x%02X (expect 0x68)n", Wire.read());
}
void loop() {
Wire.beginTransmission(MPU);
Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, 14);
int16_t v[7];
for (int i = 0; i < 7; i++) v[i] = (Wire.read() << 8) | Wire.read();
float ax = v[0] / 16384.0f, ay = v[1] / 16384.0f, az = v[2] / 16384.0f; // g, range +-2 g
float tc = v[3] / 340.0f + 36.53f; // degC
float gx = v[4] / 131.0f, gy = v[5] / 131.0f, gz = v[6] / 131.0f; // deg/s, range +-250
Serial.printf("a %.2f %.2f %.2f g g %.1f %.1f %.1f dps %.1f Cn", ax, ay, az, gx, gy, gz, tc);
delay(100);
}
Registers
| Register | Use |
|---|---|
| 0x6B | PWR_MGMT_1: bit 6 is sleep (set after a reset), the device starts asleep |
| 0x75 | WHO_AM_I: reads 0x68 |
| 0x3B to 0x40 | ACCEL_XOUT_H to ACCEL_ZOUT_L |
| 0x41, 0x42 | TEMP_OUT_H and TEMP_OUT_L, in °C = value / 340 + 36.53 |
| 0x43 to 0x48 | GYRO_XOUT_H to GYRO_ZOUT_L |
| 0x1B | GYRO_CONFIG: selects the gyroscope range |
| 0x1C | ACCEL_CONFIG: selects the accelerometer range |
| 0x19 | SMPLRT_DIV: sample rate divider |
| 0x1A | CONFIG: digital low-pass filter |
The sketch has not been run on a board. Use 16384 as the divisor for the ±2 g range, and 131 for the ±250 °/s range. If you change the range with the config registers, use the matching sensitivity from the specification table.
Source: InvenSense MPU-6000/MPU-6050 register map and product specification.
Calibration and angles
Raw readings are not zero at rest, so you have to calibrate:
- Gyro offset. Keep the board still, average a few hundred gyroscope readings, and subtract that offset. The zero-rate offset can be up to ±20 °/s over temperature.
- Accelerometer offset. With the board flat, Z should read 1 g and X and Y 0 g. Correct the difference.
- Tilt from the accelerometer. Roll = atan2(ay, az) and pitch = atan2(−ax, sqrt(ay² + az²)). It is stable over time but noisy when the board moves.
- Angle from the gyroscope. Integrate the rate over time. It is smooth but drifts.
- Combine them. A complementary or Kalman filter blends both: angle = 0.98 × (angle + gyro × dt) + 0.02 × accel angle.
- No yaw from the accelerometer. Without a magnetometer the heading drifts. The DMP and a magnetometer on the auxiliary port can help.
Source: InvenSense datasheet for the offsets. The angle formulas are standard and not from the datasheet.
Getting stable readings
- Mount it rigidly. A loose board picks up vibration as noise.
- Keep it away from motors and magnets. The chip has no magnetometer, but a vibrating motor shakes the accelerometer.
- Choose the lowest range that fits. ±2 g and ±250 °/s give the best resolution. Use larger ranges only when you need them.
- Set the digital low-pass filter in CONFIG to reduce noise at the cost of delay.
- Use the INT pin. Read data when the sensor says it is ready, not on a fixed delay.
- Wait after power-up. Let the sensor warm up for a few seconds before you calibrate.
- Let the temperature settle before a calibration, because the offsets change with temperature.
Real gotchas
- It starts in sleep mode. You must write 0 to PWR_MGMT_1 (0x6B), or every reading is zero.
- The chip is a 3.3 V part. VDD is 2.375 to 3.46 V. Only the regulated GY-521 board takes 5 V on VCC.
- Two addresses. AD0 low is 0x68, high is 0x69. A fake chip (marked MPU6050 but a clone) can answer with a different WHO_AM_I, so check it.
- Gyroscope drift. Integrating gives an angle that drifts. Always combine it with the accelerometer.
- No heading. The MPU6050 has no magnetometer.
- Orientation of the axes. Follow the arrows printed on the board and the datasheet, not your assumption.
- Wrong scale factor. The divisor depends on the range you set. Using 16384 after you set ±8 g gives values four times too large.
- I2C speed. The chip runs at 400 kHz at most. Keep the wires short, because the GY-521 pull-ups are often weak.
- Temperature is for compensation. The built-in temperature value is not an accurate room thermometer.
Compared with other motion sensors
| Sensor | Axes | Good for | Weak point |
|---|---|---|---|
| MPU6050 | 6 axes, gyro and accel, DMP | Cheap, many examples | No magnetometer, old chip |
| BNO055 | 9 axes with fusion | Gives orientation directly | More expensive |
| ADXL345 type accelerometers | 3 axes | Tilt only | No gyro |
The ADXL345 row is general knowledge and not from a datasheet read for this guide.
What to build with it
- Tilt/orientation sensing: self-balancing robots, gimbals.
- Step counting / activity detection: wearable projects.
- Vibration/impact detection: accelerometer-only use cases.
- Drone/RC flight controllers: a common low-cost IMU choice for hobby builds.
- A self-balancing robot with a PID loop on the tilt angle.
- A motion-triggered light that wakes up when the board is picked up, using the INT pin.
Safety notes
Before you wire anything up
- Keep the chip supply below 3.46 V. Power the GY-521 from 3.3 V on a 3.3 V microcontroller.
- Check the pin order on your own board before you apply power.
- Never connect the board to mains voltage. Keep it on a low-voltage supply.
- Disconnect the power before you change the wiring.
- An accelerometer or gyroscope is not a safety device. Do not use it as the only protection in a drone, a balancing robot or a machine.
Conclusion
The MPU6050 is a cheap and well-documented way to add six-axis motion sensing to a project. Wake it from sleep mode, calibrate the offsets, pick the lowest range that fits, and combine the accelerometer and the gyroscope for stable angles. For a compass heading you need a magnetometer, or a chip like the BNO055.
Chip data is from the InvenSense MPU-6000/MPU-6050 product specification and register map. The filter formula and the comparison row are general knowledge.
More guides
Keep building.
From ESP32 HomeKit accessories to Arduino and ATtiny reference guides, there’s more where this came from.
All guides
