Tilt-Controlled Bouncing Ball Game Using MPU6050 and ST7789 LCD

In this interactive Arduino project, we simulate a bouncing green ball on a full-color ST7789 170×320 LCD screen, controlled entirely by the motion of an MPU6050 gyroscope. As you tilt the sensor, the ball moves with realistic physics, including velocity, damping, bouncing, and spin effects.

This is a perfect mini-game or user interface element for embedded systems, handheld devices, or motion-based controls.

PCBWay offers high-quality PCB prototyping and assembly at an affordable price, starting at just $5 for 5 PCBs. With fast turnaround, great customer support, and easy online ordering, it’s a top choice for hobbyists and professionals alike.

Hardware Components

You’ll need the following hardware components to get started:

ComponentModel / TypeQty
Arduino (ESP32/UNO)ESP32 recommended for SPI pins1
ST7789 LCD Display1.9″ 170×320 SPI TFT (Adafruit or generic)1
MPU6050 ModuleGyroscope + Accelerometer1
BreadboardHalf-size or full-size1
Jumper WiresMale-to-male1 set
Power SupplyUSB or battery1

Schematic

Make connections according to the circuit diagram given below.

Wiring / Connections

ComponentPin on Arduino (ESP32)Note
ST7789 VCC3.3VPower for LCD
ST7789 GNDGNDGround
ST7789 SCLD18SPI Clock (SCK)
ST7789 SDAD23SPI Data (MOSI)
ST7789 DCD2Data/Command pin
ST7789 RSTD4Reset pin
ST7789 CSD15Chip Select
MPU6050 VCC3.3V or 5V (based on module)Power
MPU6050 GNDGNDGround
MPU6050 SDAD21I2C Data
MPU6050 SCLD22I2C Clock

Arduino Code

#include <Wire.h>
#include <MPU6050_light.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>

// Display pins
#define TFT_CS    15
#define TFT_DC    2
#define TFT_RST   4
#define TFT_SCLK  18
#define TFT_MOSI  23

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

MPU6050 mpu(Wire);

// Ball physics
float ballX = 85;
float ballY = 160;
float ballVX = 0;
float ballVY = 0;
float angle = 0;

float prevX = ballX;
float prevY = ballY;

const float damping = 0.96;
const float sensitivity = 0.015;
const int radius = 15;

int screenW = 170;
int screenH = 320;

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

  // Init display
  tft.init(170, 320);
  tft.setRotation(0);
  tft.fillScreen(ST77XX_BLACK);

  // Init MPU6050
  Wire.begin(21, 22);
  byte status = mpu.begin();
  if (status != 0) {
    tft.setCursor(10, 10);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(1);
    tft.println("MPU6050 Error!");
    while (true);
  }

  delay(1000);
  mpu.calcOffsets(true, true);
}

void loop() {
  mpu.update();

  // Gyro input
  float gyroX = mpu.getGyroX();
  float gyroY = mpu.getGyroY();

  // Velocity from gyro
ballVX += -gyroY * sensitivity;
ballVY += -gyroX * sensitivity;

  ballVX *= damping;
  ballVY *= damping;

  // Save previous position
  prevX = ballX;
  prevY = ballY;

  // Position update
  ballX += ballVX;
  ballY += ballVY;

  // Bounce
  if (ballX - radius <= 0 || ballX + radius >= screenW) {
    ballVX = -ballVX * 0.8;
    ballX = constrain(ballX, radius, screenW - radius);
  }

  if (ballY - radius <= 0 || ballY + radius >= screenH) {
    ballVY = -ballVY * 0.8;
    ballY = constrain(ballY, radius, screenH - radius);
  }

  // Angle update for spin effect
  angle += (ballVX + ballVY) * 0.3;

  // ERASE old ball
  tft.fillCircle(prevX, prevY, radius + 1, ST77XX_BLACK);

  // DRAW new ball
  tft.fillCircle(ballX, ballY, radius, ST77XX_GREEN);

  // Ball rotation line
  int lineX = ballX + cos(angle) * (radius - 3);
  int lineY = ballY + sin(angle) * (radius - 3);
  tft.drawLine(ballX, ballY, lineX, lineY, ST77XX_BLACK);

  delay(16);  // ~60 FPS
}" 

The project reads gyroscope data from the MPU6050 and uses it to move a virtual ball on the ST7789 display. The ball responds to tilt with acceleration and velocity, bounces off screen edges, and spins.

Key Features:

  • Realistic physics: damping, bouncing, velocity
  • Gyro-based control: intuitive movement with tilting
  • High-FPS rendering: ~60 frames per second

Code Highlights

#include <Wire.h>
#include <MPU6050_light.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
  • Libraries for I2C (MPU6050), SPI (ST7789), and graphics rendering.
float ballVX = 0, ballVY = 0;
float damping = 0.96;
  • Applies friction-like effect to slow the ball over time.
tballVX += -gyroY * sensitivity;
ballVY += -gyroX * sensitivity;
  • Converts tilt into directional velocity.
if (ballX ± radius exceeds bounds) → bounce
  • Reverses and dampens velocity when hitting screen edges.
tft.fillCircle(prevX, prevY, radius + 1, BLACK);
tft.fillCircle(ballX, ballY, radius, GREEN);
  • Efficient screen updates: erase old position, then draw new.
delay(16);
  • Aims for 60 FPS animation (1000ms / 60 ≈ 16.6ms per frame).

Conclusion

This project combines physics, sensor data, and graphics into a fun, responsive animation. Perfect for learning about real-time input-output systems, game loops, and embedded UI!