ESP32 Project : Spirit Level

In a world where technology meets everyday tasks, we designed a digital “Spirit Level” making use of the ESP32 microcontroller, an ADXL345 accelerometer, and an OLED display. This practical instrument measures roll and pitch angles in real time, making it ideal for builders, DIYers, and hobbyists.

How this Spirit Level works?

In this project, an ESP32 microcontroller connects with an ADXL345 accelerometer to measure real-time roll and pitch angles. The accelerometer detects changes in orientation and provides data to the ESP32. The ESP32 uses this data and displays the angles on an OLED screen, along with a moving circle to graphically indicate the object’s orientation. This smart “Spirit Level” simplifies tasks like object leveling by providing accurate angle measurements.

Components Required

  • ESP32
  • ADXL345 Accelerometer Sensor
  • 0.96 inch OLED Display

Introduction to ADXL345 Accelerometer Sensor

The ADXL345 is a flexible 3-axis accelerometer sensor that measures acceleration in three dimensions precisely. It has high resolution (up to 13-bit), a wide range of sensitivity options, and low power consumption, making it perfect for motion, tilt, and vibration detection in a variety of applications. This sensor can communicate through I2C or SPI so it is suitable for use in microcontroller-based projects.

Pinout of ADXL345

This is a pinout diagram of ADXL345 Sensor

Pin NamePin Description
GNDGround Pin
VCCPower Supply (3V to 6V)
CSChip Select Pin
INT1Interrupt 1 Output Pin
INT2Interrupt 2 Output Pin
SDOSerial Data Output Pin
SDASerial Data Input & Output
SDLSerial Communication Clock
Pinout of ADXL345

Circuit Diagram

This is a simple circuit diagram of Spirit Level

OLED PinESP32 Pin
VCC3V3 Pin
GNDGND of ESP32
SDAGPIO21
SCLGPIO22
OLED Connections With ESP32
ADXL PinESP32 Pin
VCC3V3 Pin
GNDGND of ESP32
SDAGPIO21
SCLGPIO22
ADXL345 Connections With ESP32

Physical Connections

This is a physical connection diagram. We used breadboard to connect the OLED and ADXL345 with ESP32.

Program/Code:

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

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_ADXL345_Unified accelerometer = Adafruit_ADXL345_Unified(12345);

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

  if (!accelerometer.begin()) {
    Serial.println("Could not find a valid ADXL345 sensor, check wiring!");
    while (1);
  }

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;) ;
  }

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Spirit Level");
  display.display();
  delay(2000);
}

void loop() {
  sensors_event_t event;
  accelerometer.getEvent(&event);

  float roll = atan2(event.acceleration.x, event.acceleration.z) * 180.0 / PI;
  float pitch = atan2(event.acceleration.y, event.acceleration.z) * 180.0 / PI;

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Roll: ");
  display.println(roll);
  display.print("Pitch: ");
  display.println(pitch);

  display.drawLine(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2, SSD1306_WHITE);
  display.drawLine(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, SSD1306_WHITE);

  int bubbleX = map(roll, -90, 90, 0, SCREEN_WIDTH);
  int bubbleY = map(pitch, -90, 90, 0, SCREEN_HEIGHT);
  display.drawCircle(bubbleX, bubbleY, 3, SSD1306_WHITE);

  display.display();
  delay(100);
}

Program Explanation

Library Includes:

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

The code includes several libraries required for the project:

Wire.h: I2C communication library.

Adafruit_GFX.h: Graphics library for drawing on the display.

Adafruit_SSD1306.h: OLED display control library.

Adafruit_ADXL345_U.h: Library for interfacing with the ADXL345 accelerometer.

Constant Definitions:

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Constants are defined for the width, height, and reset pin of the OLED display. These values are used to configure the display.

Display and Accelerometer Object Initialization:

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_ADXL345_Unified accelerometer = Adafruit_ADXL345_Unified(12345);

Instances of the Adafruit_SSD1306 and Adafruit_ADXL345_Unified classes are generated. The display object is used to control the OLED display, while the accelerometer object is used to communicate with the ADXL345 accelerometer.

Setup Function:

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

  if (!accelerometer.begin()) {
    Serial.println("Could not find a valid ADXL345 sensor, check wiring!");
    while (1);
  }

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;) ;
  }

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Spirit Level");
  display.display();
  delay(2000);
}
  • For debugging purposes, serial communication is enabled.
  • The code checks if the accelerometer is detected and ready to use. If not, it prints an error message and enters an infinite loop.
  • The OLED display is initialized, and if initialization fails, it enters an infinite loop.
  • The OLED display gets cleared, and some initial text (“Spirit Level”) is displayed. Then, there’s a 2-second delay.

Loop Function:

void loop() {
  sensors_event_t event;
  accelerometer.getEvent(&event);

  float roll = atan2(event.acceleration.x, event.acceleration.z) * 180.0 / PI;
  float pitch = atan2(event.acceleration.y, event.acceleration.z) * 180.0 / PI;

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Roll: ");
  display.println(roll);
  display.print("Pitch: ");
  display.println(pitch);

  display.drawLine(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2, SSD1306_WHITE);
  display.drawLine(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, SSD1306_WHITE);

  int bubbleX = map(roll, -90, 90, 0, SCREEN_WIDTH);
  int bubbleY = map(pitch, -90, 90, 0, SCREEN_HEIGHT);
  display.drawCircle(bubbleX, bubbleY, 3, SSD1306_WHITE);

  display.display();
  delay(100);
}
  • The loop reads accelerometer data continuously.
  • It computes the roll and pitch angles based on accelerometer data using trigonometric functions.
  • The OLED display is cleared, and the roll and pitch values are displayed.
  • On the display, horizontal and vertical reference lines are drawn.
  • Based on the roll and pitch angles, a tiny circle (bubble) showing the orientation is drawn on the display.
  • The display is updated, and there’s a 100-millisecond delay before the loop repeats.

Conclusion

In this project, we successfully converted an ESP32, an ADXL345 accelerometer, and an OLED display into a digital spirit level. Whether you’re fixing a picture or playing with a robot, this digital spirit level is like a smart, modern bubble level. It helps you make things straight and balanced easily.

Leave a Comment