MPU6050 Arduino Tutorial: I2C Wiring, Raw Data, Calibration and Code
The MPU6050 is a 6-axis motion sensor that combines a 3-axis accelerometer and a 3-axis gyroscope on one chip, and it connects to an Arduino with only two signal wires. It is inexpensive, widely available and accurate enough for self-balancing robots, gesture controllers and small drone stabilizers. The sensor reports tilt, motion and rotation, so one module covers most basic motion-tracking needs.
This tutorial shows how to wire the MPU6050 to an Arduino Uno, Nano or Mega and how to read the raw accelerometer and gyroscope registers directly with the Wire library. You will convert the raw numbers into g-force and degrees per second, calibrate all six axes and finish with a complete sketch that prints clean, stable values. No external sensor library is needed, and all of the code runs on any ATmega328P board.
The sensor uses the I2C bus, so the Arduino I2C tutorial is a helpful background read, and the Arduino UNO pinout guide and the Arduino Nano pinout guide show where the I2C pins are on each board. To show the readings on a screen, combine this project with the SSD1306 OLED tutorial. Other sensors that use the same bus include the ADXL345 accelerometer, the BMP180 pressure sensor, the BME280 environmental sensor and the SHT21 humidity sensor, and the full list is in the Arduino sensor tutorials.
Table of contents
MPU6050 Overview
The MPU6050 is a motion-tracking chip that contains an accelerometer and a gyroscope in one small package. Together, the two sensors form a 6-axis IMU (inertial measurement unit) that can measure tilt, motion, rotation and orientation in real time. It communicates over I2C, which is why the wiring needs only two data lines.
Accelerometer and Gyroscope Differences
The two sensors measure different quantities, and each one makes up for a weakness of the other.
- The 3-axis accelerometer measures acceleration along the X, Y and Z axes. Gravity acts on it all the time, so it can also report the tilt angle of the board when the board is not moving.
- The 3-axis gyroscope measures angular velocity, which is the speed of rotation around each axis. It tracks fast rotation accurately, but it cannot report the absolute tilt, and its angle estimate drifts over time.
A project that needs a stable angle usually combines both sensors, for example with a complementary filter. The accelerometer corrects the slow drift of the gyroscope, and the gyroscope smooths out the vibration noise of the accelerometer.
Why the MPU6050 Is Popular with Arduino
- It provides 6-axis motion sensing in one compact module.
- It needs only two I2C wires, so it leaves most Arduino pins free.
- It is inexpensive and available in almost every electronics store.
- It has selectable measurement ranges for both sensors.
- It contains a Digital Motion Processor (DMP) for onboard sensor fusion, although this tutorial reads the raw registers and does not use the DMP.
The chip itself runs from about 3.3 V. Common breakout boards such as the GY-521 add a voltage regulator, so the module accepts 3.3 V to 5 V on its VCC pin. A bare chip without that regulator must not be powered from 5 V.
MPU6050 Pinout
The breakout module exposes eight pins, and the diagram and table below describe each one.
| Pin | Function | Description |
|---|---|---|
| VCC | Power input | Powers the module, 3.3 V to 5 V on most breakout boards |
| GND | Ground | Connects to the Arduino ground |
| SCL | I2C clock | Connects to A5 on the Uno and the Nano |
| SDA | I2C data | Connects to A4 on the Uno and the Nano |
| INT | Interrupt output | Signals motion or data-ready events, optional |
| XDA | Auxiliary I2C data | Connects extra I2C sensors to the MPU6050, rarely used |
| XCL | Auxiliary I2C clock | Auxiliary clock line, rarely used |
| ADO | Address select | LOW gives the address 0x68, HIGH gives 0x69 |
Only VCC, GND, SCL and SDA are needed for the examples in this tutorial.
MPU6050 Arduino Wiring
The circuit needs an Arduino Uno, Nano or Mega, an MPU6050 module, four jumper wires and a USB cable. A breadboard is optional.
| MPU6050 pin | Arduino Uno and Nano pin | Description |
|---|---|---|
| VCC | 5V or 3.3V | Power supply |
| GND | GND | Ground |
| SCL | A5 | I2C clock |
| SDA | A4 | I2C data |
| INT | Not connected | Optional interrupt output |
| ADO | GND or not connected | Selects the address 0x68 |
On the Arduino Mega, connect SDA to pin 20 and SCL to pin 21. The Leonardo has dedicated SDA and SCL pins that are labeled on the board. Most GY-521 modules include pull-up resistors on SDA and SCL, so no external resistors are needed. Because A4 and A5 carry the I2C bus, they cannot be used as analog inputs while the sensor is connected. The Arduino UNO pinout guide shows all pin functions of the board.
MPU6050 I2C Address
The MPU6050 uses a 7-bit I2C address, and the ADO pin selects one of two values.
- 0x68 is the default address, used when ADO is LOW or left at its onboard pull-down.
- 0x69 is the alternate address, used when ADO is pulled HIGH.
Most modules tie ADO to GND, so the Arduino talks to the sensor at 0x68. A project with two MPU6050 sensors can use both addresses on the same bus, with ADO pulled HIGH on the second module. If the sensor does not respond, run the I2C scanner from the Arduino I2C tutorial to confirm the address.
MPU6050 Register Map
The MPU6050 stores its measurements in registers, and each axis occupies two bytes, a high byte followed by a low byte. The table lists the registers that this tutorial uses.
| Data | Register addresses | Description |
|---|---|---|
| Accelerometer X | 0x3B and 0x3C | High byte, then low byte |
| Accelerometer Y | 0x3D and 0x3E | High byte, then low byte |
| Accelerometer Z | 0x3F and 0x40 | High byte, then low byte |
| Temperature | 0x41 and 0x42 | Raw temperature value |
| Gyroscope X | 0x43 and 0x44 | High byte, then low byte |
| Gyroscope Y | 0x45 and 0x46 | High byte, then low byte |
| Gyroscope Z | 0x47 and 0x48 | High byte, then low byte |
The following configuration registers are also used.
| Register | Address | Purpose |
|---|---|---|
| SMPLRT_DIV | 0x19 | Sample rate divider |
| CONFIG | 0x1A | Digital low-pass filter setting |
| GYRO_CONFIG | 0x1B | Gyroscope full-scale range |
| ACCEL_CONFIG | 0x1C | Accelerometer full-scale range |
| PWR_MGMT_1 | 0x6B | Power management and sleep control |
| WHO_AM_I | 0x75 | Device identity register, reads 0x68 on a genuine MPU6050 |
The sensor starts in sleep mode after power-up, so the first step in every sketch is to write to PWR_MGMT_1 to wake it. Writing 0x01 clears the sleep bit and selects the X-axis gyroscope as the clock source, which the manufacturer recommends because it is more stable than the internal oscillator.
MPU6050 Arduino Raw Data
The first goal is to read the raw measurements, which are signed 16-bit numbers taken straight from the sensor registers. The sensor stores all measurements in consecutive registers, so one request that starts at register 0x3B can read the accelerometer, the temperature and the gyroscope in a single transfer of 14 bytes.
Read Raw Accelerometer and Gyroscope Data
The sketch below wakes the sensor and prints the raw values of all six axes every 200 ms. The helper function readWord() reads the high byte first and the low byte second, and it stores them in separate variables. This is more reliable than the expression (Wire.read() << 8) | Wire.read(), because the C++ standard does not define the order in which the two calls in that expression are evaluated.
#include <Wire.h>
#define MPU_ADDR 0x68 // Default I2C address (ADO = LOW)
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
void writeRegister(byte reg, byte value) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission(true);
}
// Reads two bytes (high byte first) and combines them into a signed value
int16_t readWord() {
int16_t high = Wire.read();
int16_t low = Wire.read();
return (high << 8) | low;
}
void setup() {
Serial.begin(9600);
Wire.begin();
// Wake up the sensor and use the X-axis gyro as the clock source
writeRegister(PWR_MGMT_1, 0x01);
Serial.println("MPU6050 initialized");
}
void loop() {
// Start reading at the first accelerometer register
Wire.beginTransmission(MPU_ADDR);
Wire.write(ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14, true);
int16_t accX = readWord();
int16_t accY = readWord();
int16_t accZ = readWord();
readWord(); // Temperature, skipped here
int16_t gyroX = readWord();
int16_t gyroY = readWord();
int16_t gyroZ = readWord();
Serial.print("Acc: ");
Serial.print(accX); Serial.print(", ");
Serial.print(accY); Serial.print(", ");
Serial.print(accZ);
Serial.print(" | Gyro: ");
Serial.print(gyroX); Serial.print(", ");
Serial.print(gyroY); Serial.print(", ");
Serial.println(gyroZ);
delay(200);
}The sketch works in three steps.
- The setup function writes 0x01 to PWR_MGMT_1, which wakes the sensor and selects a stable clock.
- The loop requests 14 bytes starting at register 0x3B, which covers the accelerometer, the temperature and the gyroscope.
- The helper function combines each pair of bytes into a signed 16-bit value, and the loop prints the six values.
Open the Serial Monitor at 9600 baud to see the raw values. The Arduino UART tutorial explains how the Serial Monitor works if you want more background.
With the board lying flat, the Z accelerometer value should be close to 16384, and the other axes should be near zero. The gyroscope values should be small, but they will not be exactly zero, because every sensor has a small offset. The calibration section below removes these offsets.
MPU6050 Unit Conversion (g and °/s)
Raw values are not useful on their own, because they depend on the measurement range that is selected. Dividing a raw value by the sensitivity of the selected range converts it to a physical unit. The accelerometer result is in g, where 1 g is the acceleration of gravity, and the gyroscope result is in degrees per second.
Sensitivity and Range Settings
The sensor starts with the smallest ranges, which are ±2 g and ±250 °/s. These ranges give the highest resolution, but fast motion above the limit clips the reading. The ranges are set through the ACCEL_CONFIG and GYRO_CONFIG registers.
| Sensor | Range | Register value | Sensitivity |
|---|---|---|---|
| Accelerometer | ±2 g | 0x00 | 16384 LSB/g |
| Accelerometer | ±4 g | 0x08 | 8192 LSB/g |
| Accelerometer | ±8 g | 0x10 | 4096 LSB/g |
| Accelerometer | ±16 g | 0x18 | 2048 LSB/g |
| Gyroscope | ±250 °/s | 0x00 | 131 LSB/(°/s) |
| Gyroscope | ±500 °/s | 0x08 | 65.5 LSB/(°/s) |
| Gyroscope | ±1000 °/s | 0x10 | 32.8 LSB/(°/s) |
| Gyroscope | ±2000 °/s | 0x18 | 16.4 LSB/(°/s) |
The conversion formulas for the default ranges are the following.
acceleration (g) = raw value / 16384.0
rotation rate (°/s) = raw value / 131.0For example, a raw accelerometer value of 8192 equals 0.5 g, and a raw gyroscope value of 262 equals 2 °/s. The raw temperature value converts to degrees Celsius with the formula raw / 340.0 + 36.53.
Print Acceleration, Rotation and Temperature
The following sketch sets the ranges explicitly, enables the digital low-pass filter and prints the converted values. The filter at 44 Hz reduces vibration noise, and with the filter enabled, the sample rate is 1 kHz divided by one more than the divider value, so a divider of 7 gives 125 Hz.
#include <Wire.h>
#define MPU_ADDR 0x68
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
void writeRegister(byte reg, byte value) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission(true);
}
int16_t readWord() {
int16_t high = Wire.read();
int16_t low = Wire.read();
return (high << 8) | low;
}
void setup() {
Serial.begin(9600);
Wire.begin();
writeRegister(PWR_MGMT_1, 0x01); // Wake up, X-axis gyro clock
writeRegister(SMPLRT_DIV, 0x07); // 1 kHz / (7 + 1) = 125 Hz
writeRegister(CONFIG, 0x03); // Low-pass filter at 44 Hz
writeRegister(GYRO_CONFIG, 0x00); // +/-250 deg/s
writeRegister(ACCEL_CONFIG, 0x00); // +/-2 g
Serial.println("MPU6050 initialized");
}
void loop() {
Wire.beginTransmission(MPU_ADDR);
Wire.write(ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14, true);
int16_t rawAccX = readWord();
int16_t rawAccY = readWord();
int16_t rawAccZ = readWord();
int16_t rawTemp = readWord();
int16_t rawGyroX = readWord();
int16_t rawGyroY = readWord();
int16_t rawGyroZ = readWord();
float ax = rawAccX / 16384.0;
float ay = rawAccY / 16384.0;
float az = rawAccZ / 16384.0;
float gx = rawGyroX / 131.0;
float gy = rawGyroY / 131.0;
float gz = rawGyroZ / 131.0;
float tempC = rawTemp / 340.0 + 36.53;
Serial.print("Accel (g): ");
Serial.print(ax); Serial.print(", ");
Serial.print(ay); Serial.print(", ");
Serial.println(az);
Serial.print("Gyro (deg/s): ");
Serial.print(gx); Serial.print(", ");
Serial.print(gy); Serial.print(", ");
Serial.println(gz);
Serial.print("Temperature (C): ");
Serial.println(tempC);
Serial.println("----------------------------");
delay(200);
}These values were taken with the sensor lying flat on a table. The Z acceleration is close to 1 g, but the other readings show small offsets, and the gyroscope does not read exactly zero. The next section removes these errors. The temperature value is the temperature of the chip, which is usually a few degrees higher than the room temperature.
MPU6050 Calibration
Every MPU6050 has small factory offsets, so the readings are slightly wrong even when the sensor is still. These offsets cause drift in angle calculations and unstable behavior in balancing projects. Calibration measures the offsets once and subtracts them from every later reading.
The method has four steps.
- Place the sensor flat on a table with the Z axis pointing up, and keep it perfectly still.
- Read several hundred samples from the accelerometer and the gyroscope.
- Average the samples for each axis to get the offsets.
- Subtract the offsets from every later raw reading.
Repeat the calibration when you mount the sensor in a new position, and let the sensor run for a minute first, because the offsets change slightly while the chip warms up.
Calibrate the Accelerometer and Gyroscope
The sketch below averages 500 samples and prints the six offsets. The gyroscope should read zero when it is still, so its average is the offset directly. The accelerometer Z axis should read 1 g, which is 16384 counts in the ±2 g range, so that value is subtracted from the Z average before it is stored. If your sensor is mounted with the Z axis pointing down, use -16384 instead.
#include <Wire.h>
#define MPU_ADDR 0x68
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
#define SAMPLES 500
void writeRegister(byte reg, byte value) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission(true);
}
int16_t readWord() {
int16_t high = Wire.read();
int16_t low = Wire.read();
return (high << 8) | low;
}
void setup() {
Serial.begin(9600);
Wire.begin();
writeRegister(PWR_MGMT_1, 0x01);
delay(1000);
long sumAccX = 0, sumAccY = 0, sumAccZ = 0;
long sumGyroX = 0, sumGyroY = 0, sumGyroZ = 0;
Serial.println("Calibrating... keep the sensor still and flat.");
for (int i = 0; i < SAMPLES; i++) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14, true);
sumAccX += readWord();
sumAccY += readWord();
sumAccZ += readWord();
readWord(); // Temperature, skipped
sumGyroX += readWord();
sumGyroY += readWord();
sumGyroZ += readWord();
delay(3);
}
Serial.println("Calibration done. Offsets:");
Serial.print("accX_offset = "); Serial.println(sumAccX / SAMPLES);
Serial.print("accY_offset = "); Serial.println(sumAccY / SAMPLES);
Serial.print("accZ_offset = "); Serial.println(sumAccZ / SAMPLES - 16384);
Serial.print("gyroX_offset = "); Serial.println(sumGyroX / SAMPLES);
Serial.print("gyroY_offset = "); Serial.println(sumGyroY / SAMPLES);
Serial.print("gyroZ_offset = "); Serial.println(sumGyroZ / SAMPLES);
}
void loop() {
}The values that the sketch prints are the offsets for your own sensor. You can copy them into a final sketch as constants, or you can run the calibration routine at every start-up, as the complete sketch in the next section does.
Apply the Offsets
After the calibration, subtract the offsets from the raw values before the conversion.
float ax = (rawAccX - accX_offset) / 16384.0;
float gx = (rawGyroX - gyroX_offset) / 131.0;The expression in brackets is calculated as a long integer, which avoids an overflow of the 16-bit raw value when a large offset is subtracted.
MPU6050 Complete Arduino Code
The sketch below combines everything from the previous sections. It checks that the sensor responds, calibrates both sensors at start-up, subtracts the offsets, converts the values to g and °/s, and prints the results together with the tilt angles that the accelerometer provides. Keep the sensor flat and still for the first two seconds after you reset the board.
#include <Wire.h>
#define MPU_ADDR 0x68
#define WHO_AM_I 0x75
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
#define SAMPLES 500
const float ACC_SCALE = 16384.0; // +/-2 g
const float GYRO_SCALE = 131.0; // +/-250 deg/s
long accX_offset = 0, accY_offset = 0, accZ_offset = 0;
long gyroX_offset = 0, gyroY_offset = 0, gyroZ_offset = 0;
void writeRegister(byte reg, byte value) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission(true);
}
int16_t readWord() {
int16_t high = Wire.read();
int16_t low = Wire.read();
return (high << 8) | low;
}
// Reads all 14 data bytes starting at the accelerometer registers
void readSensor(int16_t &ax, int16_t &ay, int16_t &az,
int16_t &gx, int16_t &gy, int16_t &gz) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14, true);
ax = readWord();
ay = readWord();
az = readWord();
readWord(); // Temperature, skipped
gx = readWord();
gy = readWord();
gz = readWord();
}
void calibrate() {
long sAx = 0, sAy = 0, sAz = 0, sGx = 0, sGy = 0, sGz = 0;
int16_t ax, ay, az, gx, gy, gz;
Serial.println("Calibrating... keep the sensor still and flat.");
for (int i = 0; i < SAMPLES; i++) {
readSensor(ax, ay, az, gx, gy, gz);
sAx += ax; sAy += ay; sAz += az;
sGx += gx; sGy += gy; sGz += gz;
delay(3);
}
accX_offset = sAx / SAMPLES;
accY_offset = sAy / SAMPLES;
accZ_offset = sAz / SAMPLES - 16384; // Remove the 1 g of gravity
gyroX_offset = sGx / SAMPLES;
gyroY_offset = sGy / SAMPLES;
gyroZ_offset = sGz / SAMPLES;
Serial.println("Calibration done.");
}
void setup() {
Serial.begin(9600);
Wire.begin();
// Check that the sensor answers at the expected address
Wire.beginTransmission(MPU_ADDR);
if (Wire.endTransmission() != 0) {
Serial.println("MPU6050 not found. Check the wiring and the address.");
while (true);
}
writeRegister(PWR_MGMT_1, 0x01); // Wake up, X-axis gyro clock
writeRegister(SMPLRT_DIV, 0x07); // 125 Hz sample rate
writeRegister(CONFIG, 0x03); // Low-pass filter at 44 Hz
writeRegister(GYRO_CONFIG, 0x00); // +/-250 deg/s
writeRegister(ACCEL_CONFIG, 0x00); // +/-2 g
delay(1000);
calibrate();
}
void loop() {
int16_t rawAx, rawAy, rawAz, rawGx, rawGy, rawGz;
readSensor(rawAx, rawAy, rawAz, rawGx, rawGy, rawGz);
// Subtract the offsets, then convert to g and deg/s
float ax = (rawAx - accX_offset) / ACC_SCALE;
float ay = (rawAy - accY_offset) / ACC_SCALE;
float az = (rawAz - accZ_offset) / ACC_SCALE;
float gx = (rawGx - gyroX_offset) / GYRO_SCALE;
float gy = (rawGy - gyroY_offset) / GYRO_SCALE;
float gz = (rawGz - gyroZ_offset) / GYRO_SCALE;
// Tilt angles from the accelerometer, valid while the board is not accelerating
float roll = atan2(ay, az) * 180.0 / PI;
float pitch = atan2(-ax, sqrt(ay * ay + az * az)) * 180.0 / PI;
Serial.print("Acc (g): ");
Serial.print(ax); Serial.print(", ");
Serial.print(ay); Serial.print(", ");
Serial.println(az);
Serial.print("Gyro (deg/s): ");
Serial.print(gx); Serial.print(", ");
Serial.print(gy); Serial.print(", ");
Serial.println(gz);
Serial.print("Roll: "); Serial.print(roll);
Serial.print(" Pitch: "); Serial.println(pitch);
Serial.println("---------------------");
delay(200);
}Check the Output
After the calibration, the accelerometer should read close to 0, 0 and 1 g on a flat table, and the gyroscope should read close to 0 °/s on all axes. Small variations of a few hundredths are normal because of sensor noise.
Two quick tests show that the sensor works correctly.
- Tilt the board forward and sideways. The matching accelerometer axes and the roll and pitch values should change smoothly, without sudden jumps.
- Rotate the board and stop. The gyroscope values should rise during the rotation and return to nearly zero when the board is still.
MPU6050 Troubleshooting
Most problems with the MPU6050 come from the wiring, the address or a sensor that is still in sleep mode.
The Sensor Is Not Found
The sketch above stops with an error message when the sensor does not answer. Check that SDA is on A4 and SCL is on A5, that the module has power, and that the connections are firm. Run the I2C scanner from the Arduino I2C tutorial to find the address, and change MPU_ADDR to 0x69 if the scanner reports that value.
All Values Are Zero or -1
If every raw value is -1, the Arduino received no data, because Wire.read() returns -1 when nothing is available. If all values are zero or never change, the sensor is probably still in sleep mode, so make sure the sketch writes to the PWR_MGMT_1 register before it reads any data.
The Module Is Not a Genuine MPU6050
Some modules that are sold as an MPU6050 contain a different chip, such as an MPU6500. You can read the WHO_AM_I register at 0x75 to check the chip. A genuine MPU6050 returns 0x68, and a different value means that the chip is a different part. The register map is similar, so the examples often still work, but the offsets and the noise may differ.
Large Offsets or Drifting Angles
Large offsets usually mean that the board was moving or tilted during the calibration. Calibrate again on a flat, steady surface. A slow drift of an angle that is calculated from the gyroscope is normal, because integrating a small constant error grows over time. Projects that need a stable angle combine the gyroscope and the accelerometer with a complementary filter.
MPU6050 Arduino Projects
The MPU6050 gives both acceleration and rotation data in one module, so it suits many motion-based projects.
Self-Balancing Robots
Two-wheeled balancing robots use the sensor to measure the pitch angle and the rotation speed. The controller drives the motors to keep the robot upright, and the gyroscope reacts quickly to sudden changes while the accelerometer keeps the long-term angle correct.
Gesture Control
Tilt and rotation readings can control a robot, a cursor or a game with hand movements. A small wearable board that sends the angles over a wireless link is a common build.
Motion Tracking and Stabilization
DIY head trackers, game controllers, wearable motion loggers and small gimbal or drone stabilizers use the same 6-axis data to follow movement and orientation over time.
Tilt Alarm and Display Projects
A tilt alarm can play a sound on a passive buzzer when the angle passes a limit, and the SSD1306 OLED can show the angles on a small screen. Other I2C devices, such as the DS3231 real-time clock, can share the same two wires, so a motion logger with time stamps needs no extra pins.
Conclusion
This tutorial showed how to wire the MPU6050 to an Arduino, read its raw accelerometer and gyroscope registers, convert the values to g and °/s, and calibrate all six axes. The important points are the sleep bit that must be cleared before the first read, the sensitivity values for the selected range and the need to calibrate on a flat, still surface. The complete sketch gives you a clean starting point for tilt, balance and motion projects.
Browse More Arduino Sensor Tutorials
SHT21 Arduino Tutorial: I2C Wiring, SHT2x Library, Serial & LCD Output
AHT20 Arduino Tutorial: I2C Wiring, Adafruit AHTX0 & OLED Display
SHT30 / SHT31 / SHT35 Arduino Tutorial: I2C Wiring, Code & LCD1602 Display
DS18B20 Arduino Tutorial: Single & Multiple Sensors, Serial & OLED Output
BMP180 Arduino Tutorial: I2C Wiring, Temperature, Pressure, Altitude & LCD1602
Arun is an embedded systems engineer with 10+ years of experience in STM32, ESP32, and AVR microcontrollers. He created ControllersTech to share practical tutorials on embedded software, HAL drivers, RTOS, and hardware design — grounded in real industrial automation experience.
Recommended Tools
Essential dev tools
Categories
Browse by platform







