27  Motion and Environmental Sensor Labs

In 60 Seconds

The MPU6050 is a 6-axis IMU combining an accelerometer and gyroscope on one chip, communicating via I2C. When stationary, the accelerometer reads [0, 0, 1]g (gravity). The BMP280 barometric pressure sensor measures altitude using the relationship between pressure and height (about 12 Pa per meter). Both sensors require initial calibration for accurate readings.

Key Concepts
  • MPU-6050 IMU: A 6-axis inertial measurement unit combining a 3-axis accelerometer and 3-axis gyroscope on a single I2C chip (address 0x68 or 0x69); widely used for motion detection and orientation sensing in IoT
  • Accelerometer Output: Measures acceleration in g units along three axes; at rest, reads approximately 0 g on horizontal axes and +1 g on the vertical axis due to gravity
  • Gyroscope Output: Measures angular velocity in degrees per second; integrating over time gives angle, but integration accumulates drift error, making pure gyro integration unsuitable for long-term angle tracking
  • Complementary Filter: Sensor fusion combining accelerometer (accurate long-term, noisy short-term) and gyroscope (accurate short-term, drifts long-term): angle = alpha x (angle + gyro x dt) + (1-alpha) x accel_angle
  • BME280 Environmental Sensor: Combined temperature, humidity, and barometric pressure sensor with I2C/SPI; temperature +-1 C, humidity +-3% RH, pressure +-1 hPa; can compute altitude from pressure
  • Interrupt-Driven Motion Detection: Configuring the MPU-6050 motion threshold interrupt to wake a sleeping microcontroller only when motion exceeds a threshold, dramatically reducing power consumption versus continuous polling
  • Gyroscope Drift: The gradual accumulation of error when integrating gyroscope readings; even 0.01 deg/s offset causes 0.6 deg/minute of drift, making long-term orientation tracking unreliable without correction
  • Sensor Fusion: Combining data from multiple sensors to produce estimates more accurate than any single sensor alone; accelerometer + gyroscope + magnetometer enables robust 3D orientation tracking

27.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Interface the MPU6050 IMU: Configure and read 6-axis accelerometer and gyroscope data via I2C
  • Calculate orientation: Derive pitch and roll angles from accelerometer readings
  • Implement motion detection: Create algorithms to detect movement using total acceleration
  • Measure environmental parameters with BMP280: Read barometric pressure, temperature, and calculate altitude
  • Apply sensor calibration: Perform initial calibration to compensate for sensor offsets

Motion sensors detect movement and orientation, just like the sensor in your phone that knows when you tilt or shake it. Environmental sensors measure things around us like air pressure and temperature. In this lab, you will work with an IMU (a chip that senses acceleration and rotation) and a barometric pressure sensor (which can even estimate your altitude based on air pressure changes).

27.2 Prerequisites

Required Knowledge:

Hardware Requirements:

  • ESP32 development board
  • MPU6050 6-axis IMU module
  • BMP280 barometric pressure sensor
  • Breadboard and jumper wires
  • 4.7kOhm pull-up resistors (for I2C)

Software Requirements:

  • Arduino IDE with ESP32 board support
  • Libraries: Wire, MPU6050, Adafruit_BMP280

27.3 Motion & Orientation Sensors

Time: ~20 min | Level: Intermediate | Code: P06.C10.U02

27.3.1 MPU6050 (6-Axis IMU)

The MPU6050 combines a 3-axis accelerometer and 3-axis gyroscope in a single chip, making it ideal for motion detection, tilt sensing, and orientation tracking.

Specifications:

  • Gyroscope: +/-250, +/-500, +/-1000, +/-2000 deg/s
  • Accelerometer: +/-2g, +/-4g, +/-8g, +/-16g
  • Interface: I2C (400kHz)
  • Built-in DMP (Digital Motion Processor)
  • 16-bit ADC for each channel

Inside the MPU6050, a tiny MEMS (Micro-Electro-Mechanical System) structure converts physical acceleration into an electrical signal. A proof mass suspended by microscopic springs deflects under acceleration, and capacitive plates detect the displacement (Figure 27.1).

Block diagram of MEMS accelerometer showing proof mass, spring suspension, capacitive sensing plates, charge amplifier, and ADC for converting mechanical acceleration into digital output
Figure 27.1: Block diagram of MEMS accelerometer

The proof mass and spring form a second-order mechanical system whose response depends on damping. MEMS accelerometers are designed to be critically or slightly overdamped to avoid oscillation and settle quickly (Figure 27.2).

Graph showing 2nd order mechanical system response with mass-spring-damper dynamics, illustrating underdamped, critically damped, and overdamped response curves over time
Figure 27.2: 2nd order mechanical system response

The underlying physics follows the harmonic oscillator model: the restoring force of the spring is proportional to displacement, balanced by damping and the applied acceleration force (Figure 27.3).

Diagram of mechanical harmonic oscillator with mass, spring constant k, damping coefficient b, showing restoring force and energy equations for simple harmonic motion
Figure 27.3: Mechanical harmonic oscillator model

ESP32 Implementation:

#include <Wire.h>
#include <MPU6050.h>

MPU6050 mpu;

// Raw sensor readings
int16_t ax, ay, az;
int16_t gx, gy, gz;

void setup() {
  Serial.begin(115200);
  Wire.begin();

  Serial.println("Initializing MPU6050...");

  mpu.initialize();

  // Verify connection
  if (mpu.testConnection()) {
    Serial.println("MPU6050 connection successful");
  } else {
    Serial.println("MPU6050 connection failed");
    while(1);
  }

  // Set sensitivity
  mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);  // +/-2g
  mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);  // +/-250deg/s

  // Calibrate (keep sensor still during this)
  calibrateMPU();
}

void loop() {
  // Read raw accel/gyro measurements
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  // Convert to real-world units
  float accelX = ax / 16384.0;  // For +/-2g range
  float accelY = ay / 16384.0;
  float accelZ = az / 16384.0;

  float gyroX = gx / 131.0;     // For +/-250deg/s range
  float gyroY = gy / 131.0;
  float gyroZ = gz / 131.0;

  // Calculate pitch and roll
  float pitch = atan2(-accelX, sqrt(accelY*accelY + accelZ*accelZ)) * 180/PI;
  float roll = atan2(accelY, accelZ) * 180/PI;

  // Detect motion
  float totalAccel = sqrt(accelX*accelX + accelY*accelY + accelZ*accelZ);
  bool isMoving = fabs(totalAccel - 1.0) > 0.1;  // Threshold for motion

  Serial.print("Accel(g): ");
  Serial.print(accelX, 2); Serial.print(", ");
  Serial.print(accelY, 2); Serial.print(", ");
  Serial.print(accelZ, 2);
  Serial.print(" | Gyro(deg/s): ");
  Serial.print(gyroX, 2); Serial.print(", ");
  Serial.print(gyroY, 2); Serial.print(", ");
  Serial.print(gyroZ, 2);
  Serial.print(" | Pitch: ");
  Serial.print(pitch, 1);
  Serial.print(" Roll: ");
  Serial.print(roll, 1);
  Serial.print(" | Moving: ");
  Serial.println(isMoving ? "YES" : "NO");

  delay(100);
}

void calibrateMPU() {
  Serial.println("Calibrating MPU6050... Keep sensor still!");

  long axSum = 0, aySum = 0, azSum = 0;
  long gxSum = 0, gySum = 0, gzSum = 0;
  int samples = 100;

  for(int i = 0; i < samples; i++) {
    mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    axSum += ax; aySum += ay; azSum += az;
    gxSum += gx; gySum += gy; gzSum += gz;
    delay(10);
  }

  // Set offsets (simplified approach for educational purposes;
  // production code should account for offset register scaling)
  mpu.setXAccelOffset(-(axSum / samples));
  mpu.setYAccelOffset(-(aySum / samples));
  mpu.setZAccelOffset(16384 - (azSum / samples));  // 1g offset for Z
  mpu.setXGyroOffset(-(gxSum / samples));
  mpu.setYGyroOffset(-(gySum / samples));
  mpu.setZGyroOffset(-(gzSum / samples));

  Serial.println("Calibration complete!");
}
Learning Points: MPU6050

Understanding the Readings:

When the sensor is stationary on a level surface, you should see: - Accelerometer: [0, 0, 1] g (gravity pulling down on Z-axis) - Gyroscope: [0, 0, 0] deg/s (no rotation)

Accelerometer Sensitivity:

Range Sensitivity Use Case
+/-2g 16384 LSB/g Tilt sensing, gentle motion
+/-4g 8192 LSB/g General purpose
+/-8g 4096 LSB/g Sports, vehicles
+/-16g 2048 LSB/g High-impact detection

Gyroscope Sensitivity:

Range Sensitivity Use Case
+/-250 deg/s 131 LSB/(deg/s) Slow rotation, stabilization
+/-500 deg/s 65.5 LSB/(deg/s) General purpose
+/-1000 deg/s 32.8 LSB/(deg/s) Fast rotation
+/-2000 deg/s 16.4 LSB/(deg/s) Rapid spinning

Common Applications:

  • Step counters (detect walking vibrations)
  • Fall detection (sudden acceleration spike followed by stationary)
  • Tilt sensors (measure gravity direction)
  • Gesture recognition (wave patterns)
  • Vehicle tracking (acceleration, braking, turning)

27.4 Environmental Sensors

Time: ~20 min | Level: Intermediate | Code: P06.C10.U03

27.4.1 BMP280 (Pressure & Temperature)

The BMP280 is a precision barometric pressure sensor that also measures temperature. It is commonly used for weather monitoring and altitude estimation.

Specifications:

  • Pressure Range: 300-1100 hPa (+/-1 hPa absolute, +/-0.12 hPa relative)
  • Temperature Range: -40C to 85C (+/-0.5C typical, +/-1C max)
  • Interface: I2C or SPI
  • Altitude calculation from pressure
  • Power: 2.7uA @ 1Hz (weather monitoring mode)
Overview of environmental sensing showing barometric pressure, temperature, and humidity measurement using integrated sensor modules like BMP280 for IoT weather monitoring and altitude estimation
Figure 27.4: Environmental sensing overview

ESP32 Implementation:

#include <Wire.h>
#include <Adafruit_BMP280.h>

Adafruit_BMP280 bmp;  // I2C interface

// Sea level pressure for altitude calculation
#define SEALEVELPRESSURE_HPA (1013.25)

void setup() {
  Serial.begin(115200);

  if (!bmp.begin(0x76)) {  // or 0x77
    Serial.println("Could not find BMP280 sensor!");
    while (1);
  }

  // Default settings from datasheet
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     // Operating Mode
                  Adafruit_BMP280::SAMPLING_X2,     // Temp oversampling
                  Adafruit_BMP280::SAMPLING_X16,    // Pressure oversampling
                  Adafruit_BMP280::FILTER_X16,      // Filtering
                  Adafruit_BMP280::STANDBY_MS_500); // Standby time
}

void loop() {
  float temperature = bmp.readTemperature();      // C
  float pressure = bmp.readPressure() / 100.0;    // hPa
  float altitude = bmp.readAltitude(SEALEVELPRESSURE_HPA);  // meters

  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.print("C | Pressure: ");
  Serial.print(pressure);
  Serial.print(" hPa | Altitude: ");
  Serial.print(altitude);
  Serial.println(" m");

  // Simplified weather trend demo (real applications should
  // compare readings over 1-3 hours, not 10-second intervals)
  static float lastPressure = pressure;
  float pressureChange = pressure - lastPressure;

  if (pressureChange > 0.5) {
    Serial.println("Weather: Improving (pressure rising)");
  } else if (pressureChange < -0.5) {
    Serial.println("Weather: Worsening (pressure falling)");
  } else {
    Serial.println("Weather: Stable");
  }

  lastPressure = pressure;

  delay(10000);  // 10 seconds
}
Learning Points: BMP280

Altitude from Pressure:

The barometric formula relates pressure to altitude:

\[ h = 44330 \times \left(1 - \left(\frac{P}{P_0}\right)^{0.1903}\right) \]

where \(h\) is altitude in meters, \(P\) is measured pressure in hPa, and \(P_0\) is sea level pressure (1013.25 hPa).

Pressure Varies with:

  • Altitude: ~12 Pa per meter (1.2 hPa per 100m)
  • Weather: +/-30 hPa due to weather systems
  • Temperature: Gas expansion affects pressure readings

Floor Detection Application:

For indoor floor detection, use relative accuracy (+/-0.12 hPa) rather than absolute. Each floor (~3m) produces a 0.36 hPa change – 3x the sensor’s noise floor, making single-floor detection reliable. See the worked example below for a full calculation.

Oversampling Trade-offs:

Setting Resolution Noise Time
x1 16-bit Higher 8ms
x2 17-bit Lower 14ms
x16 20-bit Lowest 62ms

The barometric formula converts atmospheric pressure to altitude above sea level using an exponential relationship.

Worked example: Your BMP280 measures 900 hPa on a mountain. Calculate the altitude:

\[ h = 44330 \times \left(1 - \left(\frac{900}{1013.25}\right)^{0.1903}\right) \]

\[ h = 44330 \times \left(1 - (0.8882)^{0.1903}\right) = 44330 \times (1 - 0.9777) = 44330 \times 0.0223 \approx 989 \text{ m} \]

Cross-check with rule of thumb: pressure drops ~12 Pa/meter, so \((1013.25 - 900) \text{ hPa} \times 100 \text{ Pa/hPa} \div 12 \text{ Pa/m} \approx 944 \text{ m}\). The difference arises because 12 Pa/m is only accurate near sea level; at higher altitudes the air is thinner and each meter of elevation produces a smaller pressure drop, so the barometric formula gives a higher (more accurate) result.

27.4.2 Altitude Calculator

Use this interactive calculator to explore how measured pressure translates to altitude using the barometric formula. Adjust the sea-level reference pressure to see how weather conditions affect altitude estimates.

27.5 Knowledge Check

Key Takeaway

Both the MPU6050 and BMP280 communicate via I2C and require startup calibration. The critical design principle is choosing the right accuracy metric: for the accelerometer, calibrate against the known 1g gravity reference; for the barometric sensor, use relative accuracy (not absolute) when measuring altitude differences.

Sammy the Sensor got a new friend – an accelerometer named “Accel Amy”! Amy can feel which way is down because gravity is always pulling on her tiny proof mass (like a tiny ball on a spring inside a chip).

“When I sit still on a table,” Amy explained, “I feel gravity pulling down on my Z-axis, so I report [0, 0, 1]g. But tip me sideways, and the numbers change – that is how I know which way is up!”

Max the Microcontroller had another friend too – “Baro Bob” the pressure sensor. “I can tell you how high up you are!” Bob said proudly. “The higher you go, the less air presses down on me. Each floor of a building changes my reading by about 0.36 units.”

Lila the LED blinked excitedly: “So if Amy detects someone walking (bouncy acceleration) and Bob detects they went up a floor (pressure dropped), we can track people going upstairs – without any cameras!”

Bella the Battery nodded: “And Bob only uses about 3 microamps – that is so tiny I could power him for years!”

Scenario: You are building a smart building elevator tracker using BMP280 sensors to detect which floor occupants are on.

Given Data:

  • Ground floor pressure: 1013.25 hPa (sea level)
  • Floor 1 pressure: 1012.89 hPa
  • Floor 2 pressure: 1012.53 hPa
  • Floor 3 pressure: 1012.17 hPa
  • BMP280 relative accuracy: +/- 0.12 hPa

Step 1: Calculate pressure change per floor

Floor 0 → Floor 1: 1013.25 - 1012.89 = 0.36 hPa
Floor 1 → Floor 2: 1012.89 - 1012.53 = 0.36 hPa
Floor 2 → Floor 3: 1012.53 - 1012.17 = 0.36 hPa

Average: 0.36 hPa per floor (3 meters vertical)

Step 2: Can BMP280 reliably detect floor changes?

Pressure change: 0.36 hPa per floor
Sensor accuracy: +/- 0.12 hPa
Signal-to-noise ratio: 0.36 / 0.12 = 3:1

Result: YES! The 0.36 hPa change per floor is 3x larger than the sensor’s noise floor (0.12 hPa), making floor detection reliable. You can confidently detect single-floor transitions.

Step 3: Calculate altitude from pressure Using the barometric formula:

float seaLevelPressure = 1013.25; // hPa at ground floor
float currentPressure = bmp.readPressure() / 100.0; // Convert Pa to hPa

// Calculate altitude in meters
float altitude = 44330.0 * (1.0 - pow(currentPressure / seaLevelPressure, 0.1903));

// Estimate floor number (assuming 3m per floor)
int floor = round(altitude / 3.0);

Real-World Application:

  • Smart elevators: Track user movement patterns
  • Emergency response: Locate firefighters in multi-story buildings
  • Fitness tracking: Count stair climbing (calories burned)
  • Indoor navigation: Guide users to specific floors in large buildings

Key Insight: Use relative accuracy (+/- 0.12 hPa) for altitude differences, not absolute accuracy (+/- 1 hPa). Absolute pressure varies +/- 30 hPa daily due to weather, but relative measurements within a few hours are highly stable.

How It Works: MPU6050 IMU Calibration

When you power on the MPU6050, it has factory-set offsets that may not match your specific chip or mounting orientation. Here is the step-by-step calibration process:

  1. Place sensor perfectly level - Use a flat, stable surface
  2. Collect 100 samples - Average out noise (each sample takes ~10ms)
  3. Calculate offsets - Expected values are: accel [0, 0, 1g], gyro [0, 0, 0]
  4. Write offsets to registers - MPU6050 has built-in offset registers
  5. Verify calibration - Check that stationary readings match expectations

Key insight: The Z-axis accelerometer should read exactly 1g (16384 raw units at +/-2g setting) when level because Earth’s gravity pulls on the proof mass. The calibration compensates for the chip being slightly tilted in its package or mounting variations.

Exercise: Implement a fall detection system using the MPU6050.

Challenge: Detect when the device experiences free-fall (all axes near 0g) followed by sudden impact (high acceleration spike).

Hint:

  1. Calculate total acceleration: sqrt(ax^2 + ay^2 + az^2)
  2. Free-fall: total accel < 0.3g for >100ms
  3. Impact: total accel > 3g within 500ms of free-fall
  4. Trigger alert only if both conditions met

Solution skeleton:

bool freeFallDetected = false;
unsigned long freeFallStart = 0;

// In loop():
float totalAccel = sqrt(ax*ax + ay*ay + az*az);

if (totalAccel < 0.3 && !freeFallDetected) {
    freeFallStart = millis();
    freeFallDetected = true;
}
if (totalAccel > 3.0 && freeFallDetected
    && (millis() - freeFallStart < 500)) {
    triggerFallAlert();
    freeFallDetected = false;  // Reset for next detection
}
// Timeout: reset if no impact within 500ms
if (freeFallDetected && (millis() - freeFallStart >= 500)) {
    freeFallDetected = false;
}

27.6 Concept Relationships

Core Concept Related Concepts Why It Matters
Accelerometer Gravity Sensing, Tilt Angle, Motion Detection Measures static orientation and dynamic acceleration
Gyroscope Angular Velocity, Drift, Integration Tracks rotation rate but accumulates error over time
Barometric Pressure Altitude, Weather Prediction, Floor Detection Converts pressure to height using barometric formula
Sensor Fusion Complementary Filter, Kalman Filter Combines accel + gyro for drift-free orientation

27.7 Summary

This chapter covered motion and environmental sensor implementation:

  • MPU6050 IMU combines accelerometer and gyroscope for 6-axis motion sensing
  • Accelerometer calibration compensates for factory offsets by measuring at rest
  • Pitch and roll calculations derive orientation from gravity vector direction
  • Motion detection uses total acceleration magnitude deviation from 1g
  • BMP280 provides pressure-based altitude estimation and weather monitoring
  • Relative vs absolute accuracy determines suitability for different applications

27.8 See Also

Common Pitfalls

The MPU-6050 axis orientation is fixed to the chip package. If the chip is mounted at 45 degrees, all axis readings must be rotated mathematically to match the device frame. Always document and compensate for the physical mounting orientation in firmware.

The MPU-6050 uses address 0x68 or 0x69; the BME280 uses 0x76 or 0x77. Both can coexist on the same bus, but confirm no other sensors in your design share these addresses before ordering PCBs.

Integrating raw gyroscope readings without applying the correct sensitivity conversion for the configured full-scale range (+-250, +-500, +-1000, or +-2000 deg/s) produces angles scaled by factors of 2, 4, or 8. Always read the full-scale register and apply the correct LSB-to-deg/s conversion factor.

The BME280 humidity sensor requires 1-2 seconds after power-on for the humidity cell to stabilize. Reading humidity immediately after power-on produces low readings that do not reflect actual ambient humidity.

27.9 What’s Next?

Chapter Focus Area
Light & Proximity Sensors Photodiode, LDR, IR proximity, and touch sensing labs
Sensor Calibration Lab Hands-on calibration techniques with Wokwi simulation
Best Practices & Labs Production-quality sensor implementation guidelines
Sensor Data Processing Filtering, smoothing, and fusion of sensor readings
Sensor Power Management Low-power techniques for battery-operated sensor nodes