Sharjah, UAE GST
Home/ Projects/ Lesson 2: Tilt Angle from a Baseline
Lesson 2 of 5 Sensing Beginner ◎ 30 min

Lesson 2: Tilt Angle from a Baseline

Add the motion sensor. It learns which way is "upright", then the OLED shows how many degrees you have tilted it.

ESP32 Fall-Detection Learning Prototype
One of 5 projects in the ESP32 Fall-Detection Learning Prototype — the parts below are from that kit.

Before you start

Time
30 min
Do these first
Adult help needed
You can do this lesson on your own.
  • Only if your GY-521 pins are not soldered: soldering by an adult

Exact parts

PartHow many
Wiring from Lesson 1
GY-521 (MPU-6050) motion sensor1
Male-to-male jumper wires4

Not in the box

Nothing extra - just the kit and your computer.

What you build and learn

  • What an accelerometer measures: the pull of gravity, in g
  • Two I2C devices sharing the same two wires
  • Why "tilted" only means something compared with a starting position
  • Working out an angle between two directions

Sitting still, the accelerometer feels only gravity: 1 g pointing down. Which of its x, y and z axes feel it tells us which way the sensor is facing.

For 3 seconds the sketch averages 150 readings while you keep it still. That direction becomes "upright", however the sensor is mounted.

After that it compares each new reading with "upright" and works out the angle between them (with acos of the dot product). 0° = as at the start, 90° = lying on its side.

The OLED and the GY-521 share SDA and SCL. They do not get mixed up because each has its own address.

New words

Accelerometer
A sensor that measures acceleration, including gravity, on three axes: x, y and z.
g
The pull of gravity. Sitting still, the sensor measures about 1 g in total.
Baseline
The starting position the sensor learns as "upright".
Address
The number an I2C device answers to: the GY-521 is 0x68 (or 0x69), the OLED 0x3C.

What this project uses

Every item is in the kit — nothing extra to buy.

Build this with

ESP32 Fall-Detection Learning Prototype

All 6 parts in one box, with this project and 4 more to build from them.

Price on request See the kit Request this kit
Need only these parts? Order them loose · from AED 133.07
Used in this project tick what you still need
Selected parts AED 133.07

One of each part. Packs (resistor assortment, jumper wires) already hold more than a single build needs. Backorder parts ship once they arrive. Delivery is AED 20, free on orders AED 100+.

Connections

Unplug USB before you change any wire. Pin names are the labels printed on the board.

Lesson 2: Tilt Angle from a Baseline - connection diagram with labelled pins
Connection diagram, drawn from this lesson's pin plan - not a photo of a tested build. Tap to open it full size. The table below says the same thing in words.
Part / pinConnect toNote
GY-521 VCC+ rail (3V3)
GY-521 GND− rail (GND)
GY-521 SDAGPIO21Same wire row as the OLED SDA
GY-521 SCLGPIO22Same wire row as the OLED SCL
GY-521 XDA, XCL, AD0, INTNothingLeave empty
OLED, buttonAs in Lesson 1GPIO21, GPIO22, GPIO26

Step by step

One thing at a time. Tick nothing off until it matches the diagram.

  1. Unplug the USB cable.Watch out: Always unplug the USB cable before you add, move or remove a wire.
  2. Check the GY-521 has its 8 header pins soldered on. If they are loose, stop: an adult must solder them first.Watch out: Soldering irons are very hot. Adults only.
  3. Push the GY-521 into the breadboard. Wire VCC to the + rail (3V3), GND to the − rail, SDA into the same row as the OLED's SDA wire, SCL into the same row as the OLED's SCL wire.Watch out: VCC to 3V3, not 5V.
  4. Leave XDA, XCL, AD0 and INT empty.
  5. Tape the breadboard flat to a book or a piece of card, so the sensor cannot wobble.
  6. Plug in USB, upload the Lesson 2 code and keep everything still for 3 seconds.
  7. Tilt the book slowly. Watch the angle grow and "TILTED" appear past 55°. Press the button to learn a new "upright".

Sketch for this project

Written for the board in the kit. Copy it into the Arduino IDE; change the pin numbers if you wire it differently.

lesson2_tilt_angle.ino arduino
/*
  DIY KIT - ESP32 Fall-Detection Learning Prototype
  Lesson 2 of 5: Tilt Angle from a Baseline

  WIRING (unplug the USB cable before you wire)
    Keep the OLED and button from Lesson 1.
    GY-521 (MPU-6050)  VCC -> 3V3   GND -> GND   SDA -> GPIO21   SCL -> GPIO22
    The OLED and the GY-521 share the same two I2C wires (SDA and SCL).
    Leave XDA, XCL, AD0 and INT on the GY-521 unconnected.

  WHAT SHOULD HAPPEN
    Keep the sensor still for 3 seconds while it learns "upright". Then the
    OLED shows the angle you have tilted it from that position. Past 55
    degrees it says TILTED. Press the button to learn a new "upright".
    Serial Monitor (115200) or Serial Plotter shows angle_deg.
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int SDA_PIN = 21;
const int SCL_PIN = 22;
const int BUTTON_PIN = 26;
const float TILT_DEG = 55.0;          // the "lying down" angle used in Lesson 5
const int CALIBRATION_SAMPLES = 150;  // 150 x 20 ms = 3 seconds
const unsigned long SAMPLE_MS = 20;
const unsigned long REPORT_MS = 250;

Adafruit_SSD1306 display(128, 64, &Wire, -1);
bool displayOk = false;
int mpuAddress = 0;                   // 0x68 or 0x69, found at start
float baseX = 0, baseY = 0, baseZ = 1;  // the "upright" direction of gravity
unsigned long lastSample = 0, lastReport = 0;
bool lastButton = HIGH;

bool answers(int address) {
  Wire.beginTransmission(address);
  return Wire.endTransmission() == 0;
}

// Read the three accelerometer axes, in g. 1 g = the pull of gravity.
bool readAccel(float &x, float &y, float &z) {
  Wire.beginTransmission(mpuAddress);
  Wire.write(0x3B);                   // the first acceleration register
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom(mpuAddress, 6) != 6) return false;
  int16_t rx = (int16_t)((Wire.read() << 8) | Wire.read());
  int16_t ry = (int16_t)((Wire.read() << 8) | Wire.read());
  int16_t rz = (int16_t)((Wire.read() << 8) | Wire.read());
  x = rx / 16384.0;                   // +-2 g range: 16384 counts per g
  y = ry / 16384.0;
  z = rz / 16384.0;
  return true;
}

void show(const char *line1, int angle) {
  if (!displayOk) return;
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("TILT ANGLE LAB");
  display.println(line1);
  if (angle >= 0) {
    display.setTextSize(3);
    display.setCursor(0, 22);
    display.print(angle);
    display.setTextSize(2);
    display.print(" deg");
  }
  display.setTextSize(1);
  display.setCursor(0, 56);
  display.print("limit ");
  display.print((int)TILT_DEG);
  display.print("  btn=reset");      // "limit 55  btn=reset": 21 characters fit on a line
  display.display();
}

// Average 150 readings while the sensor is still: that is "upright".
bool calibrate() {
  show("KEEP STILL: 3 s", -1);
  Serial.println("Calibrating - keep the sensor still");
  float sx = 0, sy = 0, sz = 0;
  int good = 0;
  for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
    float x, y, z;
    if (readAccel(x, y, z)) {
      sx += x; sy += y; sz += z;
      good++;
    }
    delay(SAMPLE_MS);
  }
  float length = sqrt(sx * sx + sy * sy + sz * sz);
  if (good == 0 || length < 0.001) return false;
  baseX = sx / length;                // keep only the direction
  baseY = sy / length;
  baseZ = sz / length;
  Serial.println("Calibrated");
  return true;
}

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  Wire.begin(SDA_PIN, SCL_PIN);
  displayOk = display.begin(SSD1306_SWITCHCAPVCC, 0x3C) || display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
  if (!displayOk) Serial.println("OLED not found - see Lesson 1");

  if (answers(0x68)) mpuAddress = 0x68;
  else if (answers(0x69)) mpuAddress = 0x69;
  if (mpuAddress == 0) {
    Serial.println("GY-521 not found - unplug USB and check VCC 3V3, GND, SDA GPIO21, SCL GPIO22");
    show("GY-521 NOT FOUND", -1);
    while (true) delay(1000);         // stop here
  }
  Serial.print("GY-521 found at 0x");
  Serial.println(mpuAddress, HEX);
  Wire.beginTransmission(mpuAddress); // wake the sensor up (it starts asleep)
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  delay(50);
  if (!calibrate()) Serial.println("Calibration failed - is the sensor wired?");
}

void loop() {
  bool button = digitalRead(BUTTON_PIN);
  if (button == LOW && lastButton == HIGH) calibrate();   // press: learn a new "upright"
  lastButton = button;

  if (millis() - lastSample < SAMPLE_MS) return;
  lastSample = millis();
  float x, y, z;
  if (!readAccel(x, y, z)) return;
  float length = sqrt(x * x + y * y + z * z);
  if (length < 0.001) return;

  // The angle between "upright" and gravity now: the dot product of the two directions.
  float dot = (x * baseX + y * baseY + z * baseZ) / length;
  float angle = acos(constrain(dot, -1.0, 1.0)) * 180.0 / PI;

  if (millis() - lastReport >= REPORT_MS) {
    lastReport = millis();
    Serial.print("angle_deg:");
    Serial.print(angle, 1);
    Serial.print(",limit:");
    Serial.println(TILT_DEG, 0);
    show(angle >= TILT_DEG ? "TILTED past the limit" : "upright", (int)angle);
  }
}
ℹCompiler-checked for ESP32 Dev Module (ESP32 core 3.3.11, Adafruit SSD1306 2.5.17 / GFX 1.12.6 / BusIO 1.17.4, 0 warnings) and run in the kit simulator. Not yet bench-tested on the shipped parts.

Upload it

  1. Click Copy (or Download .ino and open the file).
  2. In the Arduino IDE choose File → New Sketch, select everything in it and paste.
  3. Check the bottom of the window says Arduino Uno and your port (Start here, step 5).
  4. Click Upload (the → arrow) and wait for Done uploading.

How the code works

In the codeWhat it does
readAccel(x, y, z)Reads the 6 bytes of acceleration from the GY-521 over I2C and turns them into g.
calibrate()Averages 150 readings and keeps only the direction: the "upright" baseline.
acos(dot) * 180 / PIThe angle between "upright" and gravity now, in degrees.
answers(0x68)Asks "is anyone at address 0x68?" - that is how the sketch finds the sensor.

Change it and see

Change: Change TILT_DEG from 55 to 30.What happens: "TILTED" appears much sooner.
Change: Calibrate with the book on its side (press the button), then lay it flat.What happens: Flat now reads about 90°: the angle is always from the baseline.

What you should see or hear

  • "GY-521 found at 0x68", then "KEEP STILL" for 3 seconds.
  • Flat and still: 0-2°. Tilted: the angle on the OLED follows you.
  • Past 55°: "TILTED past the limit". The Serial Plotter shows angle_deg and the limit line.

If it doesn't work

ProblemWhat to check
"GY-521 not found"Unplug USB. Check VCC to 3V3, GND, SDA to GPIO21 and SCL to GPIO22. Are the GY-521 header pins soldered? Loose pins pushed through the holes do not make contact.
The angle never comes back to 0It moved during the 3 seconds. Press the button and keep still.
The angle jumps aboutTape the sensor down firmly - a loose sensor wobbles.
"OLED not found" or a black screenUnplug USB. Check OLED VCC to 3V3 (not 5V), GND, SDA to GPIO21 and SCL to GPIO22 - by the labels printed on the OLED.
Nothing, or strange symbols, in the Serial MonitorSet the speed in the corner of the Serial Monitor to 115200 baud.

Small experiment

Calibrate, then turn the book upside down. What angle does it show? Why is 180° the biggest angle you can get?

Check what you learned

What does the accelerometer measure when it is sitting still?

Gravity: about 1 g.

Why does the sketch learn a baseline instead of using "flat"?

So the angle is right however the sensor is mounted.

How can two devices share SDA and SCL?

Each has its own I2C address.

▶In the simulator: In the simulator the tilt slider sets the angle directly, so the numbers are perfectly steady. A real sensor wobbles by a degree or two.

Next

  • Lesson 3 measures jolts and drops instead of slow tilts.

Stuck?

Most problems are one wire in the wrong hole. Check these first:

  1. Unplug, then compare every wire with the lesson's diagram and table. One pin off is the most common problem.
  2. Are all VCC wires on 3V3 (not 5V or VIN), and all GND wires on GND?
  3. OLED and GY-521: SDA on GPIO21 and SCL on GPIO22, by the labels on each module?
  4. Serial Monitor: is it set to 115200 baud?
  5. Upload problems: Tools → Board = ESP32 Dev Module, the right port, and hold BOOT if it stops at "Connecting". (Start here, step 6.)
Still stuck? Ask us on WhatsApp. Send this message with a photo of your wiring - it tells us exactly where you are.
Hi DIY KIT, I'm stuck on the ESP32 Fall-Detection Learning Prototype.
Lesson: __
Step number: __
What should happen: __
What happens instead: __
Error message (copied exactly): __
I'll attach a photo of my wiring.
Message us on WhatsApp

More from this kit

All projects →

Bot
ECHO
Online — usually replies instantly
Catalog assistant: verify final stock/price on the product page before checkout.