Search Code

Dự án Ma trận LED RGB ESP32-S3 6 - Trò chơi Cible

Dự án Ma trận LED RGB ESP32-S3 6 - Trò chơi Cible

Dự án 6 – Trò chơi bắn trúng mục tiêu (Nghiêng bảng để chạm vào vòng tròn)

Dự án 6 là dự án cuối cùng và mang tính tương tác cao nhất trong chuỗi bài về ESP32-S3 RGB LED Matrix này. Sử dụng cảm biến chuyển động QMI8658C tích hợp trên bo mạch, bạn nghiêng bảng để di chuyển một chấm tròn quanh ma trận 8×8. Một “vùng mục tiêu” hình tròn nằm ở giữa màn hình. Khi chấm tròn của bạn chạm vào vòng tròn, màu sắc sẽ thay đổi và (tùy chọn) một còi báo sẽ kêu. Điều này tạo nên một trò chơi cân bằng/phối hợp đơn giản nhưng thú vị một cách bất ngờ.

Tất cả sáu dự án được trình diễn trong một video YouTube duy nhất (được nhúng trên trang này). Mã trò chơi hoàn chỉnh được tải tự động bên dưới bài viết này và các liên kết mua hàng liên kết của mô-đun xuất hiện bên dưới phần mã.

Tổng quan về Mô-đun ESP32-S3 RGB LED Matrix

Mô-đun này bao gồm:

  • Vi điều khiển ESP32-S3 (Wi-Fi + BLE)
  • Ma trận LED RGB 8×8 (64 đèn LED có thể định địa chỉ)
  • Gia tốc kế QMI8658C để cảm nhận độ nghiêng và hướng
  • Cổng USB-C để cấp nguồn và lập trình
  • Các nút Boot / Reset
  • Các chân GPIO khả dụng cho các phụ kiện như còi báo hoặc cảm biến

Đối với trò chơi này, gia tốc kế là yếu tố thiết yếu—nó liên tục báo cáo độ nghiêng X/Y, cho phép chấm tròn di chuyển mượt mà dựa trên góc của bảng. Phần video cho dự án này cho thấy rõ ràng cách chấm tròn lướt và phản ứng khi bạn nghiêng mô-đun sang trái/phải/tiến/lùi.:contentReference[oaicite:0]{index=0}

Các dự án được đề cập trong video (Dấu mốc thời gian)

  • 00:00 – Giới thiệu
  • 02:01 – Cài đặt bo mạch ESP32
  • 03:32 – Cài đặt thư viện
  • 05:32 – Dự án 1: Chấm tròn chuyển động
  • 11:11 – Dự án 2: Cuộn chữ
  • 12:59 – Dự án 3: Văn bản HTTP
  • 16:41 – Dự án 4: Chấm tròn nghiêng
  • 18:55 – Dự án 5: Mũi tên lên
  • 20:02Dự án 6: Trò chơi bắn trúng mục tiêu (dự án này)

Phần này của video cho thấy cả chuyển động của chấm tròn và logic phát hiện vòng tròn đang hoạt động, giúp bạn dễ hiểu hơn về cách hoạt động của các tọa độ.:contentReference[oaicite:1]{index=1}

Cài đặt bo mạch ESP32 trong Arduino IDE

Nếu bạn đã hoàn thành các dự án trước đó, việc cài đặt bo mạch đã xong. Nếu không, hãy làm theo:

  1. File > Preferences → Thêm URL bo mạch ESP32.
  2. Tools > Board > Boards Manager… → Cài đặt “ESP32”.
  3. Chọn bo mạch ESP32-S3 trong Tools > Board.
  4. Chọn cổng COM chính xác trong Tools > Port.

Cài đặt các thư viện cần thiết

Trò chơi này sử dụng:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX
  • QMI8658 (cảm biến chuyển động)

Cài đặt chúng qua:

  1. Sketch > Include Library > Manage Libraries…
  2. Tìm kiếm: NeoMatrix → Cài đặt
  3. Cài đặt các phần phụ thuộc (GFX + NeoPixel)
  4. Tìm kiếm và cài đặt QMI8658

Cách hoạt động của Trò chơi bắn trúng mục tiêu

Gia tốc kế QMI8658C cung cấp các giá trị độ nghiêng X và Y. Các giá trị này được ánh xạ tới tọa độ LED (0–7 theo cả hai hướng). Chấm tròn của bạn di chuyển quanh ma trận dựa trên góc của bảng.

Một vòng tròn (hoặc vành tròn) được vẽ trên màn hình như “khu vực mục tiêu.” Bạn giành được một “cú trúng” bất cứ khi nào chấm tròn chồng lên vòng tròn. Tại thời điểm đó:

  • Màu của mục tiêu thay đổi (ngẫu nhiên hoặc được xác định trước)
  • Một còi báo tùy chọn kêu (nếu được kết nối)

Vì ma trận nhỏ, vòng tròn được vẽ bằng cách kiểm tra bán kính đơn giản. Trò chơi chạy liên tục, vì vậy bạn có thể nghiêng qua lại để chạm vào vòng tròn nhiều lần.

Dự án 6 – Cài đặt mã (Trò chơi bắn trúng mục tiêu)

Dưới đây là các cài đặt chính mà người dùng có thể điều chỉnh nằm ở đầu bản phác thảo. Mã hoàn chỉnh được tự động bao gồm bên dưới bài viết này.

Cấu hình ma trận


// Cấu hình ma trận
const int MATRIX_PIN    = 14;
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

Ma trận RGB được kết nối cứng với GPIO 14—không thay đổi điều này.

Độ sáng


uint8_t matrixBrightness = 40;   // 0–255

Đối với sử dụng trong nhà, 30–60 là lý tưởng.

Màu chấm tròn


// Màu chấm tròn (R, G, B)
uint8_t dotRed   = 255;
uint8_t dotGreen = 255;
uint8_t dotBlue  = 255;

Đây là chấm tròn bạn di chuyển bằng cách nghiêng bảng.

Màu vòng tròn (Ngẫu nhiên hoặc Cố định)


// Màu vòng tròn (mục tiêu)
uint8_t circleRed   = 0;
uint8_t circleGreen = 0;
uint8_t circleBlue  = 255;

// Nếu đúng, chọn một màu ngẫu nhiên mới mỗi khi chấm tròn chạm vào
bool randomCircleColor = true;

Đặt randomCircleColor = false; nếu bạn muốn một màu cố định.

Bán kính vòng tròn


// Kích thước mục tiêu (bán kính)
int targetRadius = 3;   // 3 phù hợp tốt trên ma trận 8×8

Bán kính lớn hơn làm trò chơi dễ hơn; bán kính nhỏ hơn làm trò chơi khó hơn.

Độ nhạy nghiêng


// Độ nhạy của ánh xạ độ nghiêng
float tiltScale = 4.0f;   // tăng = chuyển động nhanh hơn trên màn hình

Nếu chấm tròn di chuyển quá nhanh hoặc nhảy, hãy giảm con số này.

Cài đặt còi báo (Tùy chọn)


// Chân buzzer (tùy chọn)
int buzzerPin = 6;      // kết nối chân + của buzzer với chân 6, – với GND
bool useBuzzer = true;  // đặt false để tắt âm thanh

Nếu bạn không kết nối buzzer, chỉ cần đặt useBuzzer = false;

Tóm tắt

Dự án 6 kết hợp mọi thứ đã học từ các dự án trước: vẽ ma trận, đầu vào cảm biến gia tốc, điều khiển màu sắc, làm mượt chuyển động và âm thanh tùy chọn. Nghiêng bảng sẽ di chuyển chấm tròn, và chạm vào vòng tròn sẽ thay đổi màu của nó và (tùy chọn) kích hoạt tiếng bíp. Đây là một minh họa thú vị về cảm biến chuyển động trên màn hình RGB nhỏ gọn.

Mã đầy đủ của “Trò chơi Mục tiêu” được hiển thị tự động bên dưới bài viết này. Bạn cũng có thể xem phần Dự án 6 của video để thấy cách chấm tròn di chuyển và cách phát hiện va chạm. Nếu bạn muốn tự xây dựng trò chơi của mình, các liên kết liên kết để mua mô-đun Ma trận LED RGB ESP32-S3 xuất hiện bên dưới phần mã.

Hình ảnh

ESP32 S3 Matrix
ESP32 S3 Matrix
ESP32 S3 Matrix  pin out
ESP32 S3 Matrix pin out
ESP32-S3_RGB_8x8_matrix-3
ESP32-S3_RGB_8x8_matrix-3
ESP32 S3 Matrix displaying rainbow heart 3
ESP32 S3 Matrix displaying rainbow heart 3
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix-2
ESP32-S3_RGB_8x8_matrix-2
804-ESP32-S3 RGB LED Matrix Project 6 - Cible game
Ngôn ngữ: C++
/* 
  Project 6: Tilt Circle Game – ESP32-S3 RGB LED Matrix (Waveshare)

  This sketch reads tilt from the QMI8658C IMU and smoothly moves a dot 
  on the 8×8 RGB LED matrix based on board orientation.

  ▶️ Video Tutorial:
  https://youtu.be/JKLuYrRcLMI

  📚⬇️ Resources & Code Page:
  https://robojax.com/RJT829

  QMI8658_RGB_2
*/

#include <Arduino.h>
#include <math.h>

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

#include <QMI8658.h>   // by Lahav Gahali

// -------- LED MATRIX SETUP --------
#define MATRIX_PIN    14
#define MATRIX_WIDTH  8
#define MATRIX_HEIGHT 8

// Buzzer pin – change this to your actual buzzer GPIO.
const int BUZZER_PIN = 6;   // TODO: set to your buzzer pin

// Matrix brightness (0–255)
const uint8_t MATRIX_BRIGHTNESS = 10;

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
  MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_PIN,
  NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
  NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
  NEO_GRB + NEO_KHZ800
);

// -------- IMU SETUP --------
QMI8658 imu;

// -------- GAME CONFIG --------

// How often the dot is allowed to move (ms).
// Bigger = slower movement.
const uint16_t MOVE_INTERVAL_MS = 150;   // try 120–250

// How much tilt (m/s^2) before the dot moves.
// Increase if it feels too sensitive.
const float ACC_TILT_THRESHOLD = 2.0f;   // about ~0.2 g

// Dot base color (RGB)
const uint8_t DOT_R = 255;
const uint8_t DOT_G = 255;
const uint8_t DOT_B = 255;


// Circle geometry (centered on 8x8)
const float CIRCLE_CENTER_X = (MATRIX_WIDTH - 1) / 2.0f;   // 3.5
const float CIRCLE_CENTER_Y = (MATRIX_HEIGHT - 1) / 2.0f;  // 3.5
const float CIRCLE_RADIUS   = 3.0f;
const float CIRCLE_THICKNESS = 0.8f; // +- thickness around radius

// -------- GAME STATE --------

// Dot position on the 8x8 grid (0..7)
int dotX = 3;
int dotY = 3;

// Colors (16-bit NeoMatrix colors)
uint16_t dotColor;
uint16_t circleColor;

// To detect “just touched circle” vs “still on circle”
bool wasOnCircle = false;

// Timer for rate-limiting movement
unsigned long lastMoveTime = 0;

// -------- HELPER FUNCTIONS --------

// Classic NeoPixel color wheel (0-255 -> rainbow)
uint16_t wheel(byte pos) {
  if (pos < 85) {
    return matrix.Color(pos * 3, 255 - pos * 3, 0);
  } else if (pos < 170) {
    pos -= 85;
    return matrix.Color(255 - pos * 3, 0, pos * 3);
  } else {
    pos -= 170;
    return matrix.Color(0, pos * 3, 255 - pos * 3);
  }
}

// Is a given pixel approximately on the circle?
bool isOnCircle(int x, int y) {
  float dx = x - CIRCLE_CENTER_X;
  float dy = y - CIRCLE_CENTER_Y;
  float d2 = dx * dx + dy * dy;

  float rMin = CIRCLE_RADIUS - CIRCLE_THICKNESS;
  float rMax = CIRCLE_RADIUS + CIRCLE_THICKNESS;

  return (d2 >= rMin * rMin) && (d2 <= rMax * rMax);
}

// Draw circle + dot
void drawScene() {
  matrix.fillScreen(0);

  // Draw circle
  for (int y = 0; y < MATRIX_HEIGHT; y++) {
    for (int x = 0; x < MATRIX_WIDTH; x++) {
      if (isOnCircle(x, y)) {
        matrix.drawPixel(x, y, circleColor);
      }
    }
  }

  // Draw dot (on top)
  matrix.drawPixel(dotX, dotY, dotColor);

  matrix.show();
}

// Simple blocking beep (short)
void buzzOnce() {
  digitalWrite(BUZZER_PIN, HIGH);
  delay(40);
  digitalWrite(BUZZER_PIN, LOW);
}

// Use accelerometer to decide dot movement
void updateDotFromTilt(float ax, float ay) {
  unsigned long now = millis();
  if (now - lastMoveTime < MOVE_INTERVAL_MS) {
    return; // too soon, wait
  }

  int dx = 0;
  int dy = 0;

  // On this board, Y tilt feels like "left/right" on the matrix,
  // and X tilt feels like "up/down" → so we swap.

  // --- Horizontal movement from AY (tilt left/right) ---
  if (ay > ACC_TILT_THRESHOLD) {
    dx = 1;   // tilt to the right → move dot to the right
  } else if (ay < -ACC_TILT_THRESHOLD) {
    dx = -1;  // tilt to the left → move dot to the left
  }

  // --- Vertical movement from AX (tilt forward/back) ---
  // This was inverted. We flip the signs:
  // ax >  threshold  = tilt "forward" (away)  → move dot UP  (dy = -1)
  // ax < -threshold  = tilt "back"    (toward)→ move dot DOWN(dy =  1)
  if (ax > ACC_TILT_THRESHOLD) {
    dy = -1;  // was +1 before
  } else if (ax < -ACC_TILT_THRESHOLD) {
    dy = 1;   // was -1 before
  }

  if (dx != 0 || dy != 0) {
    dotX = constrain(dotX + dx, 0, MATRIX_WIDTH  - 1);
    dotY = constrain(dotY + dy, 0, MATRIX_HEIGHT - 1);
    lastMoveTime = now;
  }
}


// -------- SETUP & LOOP --------

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

  // Matrix init
  matrix.begin();
  matrix.setBrightness(MATRIX_BRIGHTNESS);
  matrix.fillScreen(0);
  matrix.show();

  // Buzzer init
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);

  // IMU init
if (!imu.begin(11, 12)) {
    Serial.println("Failed to initialize QMI8658!");
    while (1) {
      delay(100);
    }
  }

  // Use m/s^2 and dps (optional, but nice)
  imu.setAccelUnit_mps2(true);
  imu.setGyroUnit_dps(true);
  imu.setDisplayPrecision(3);

  Serial.println("QMI8658 initialized.");

  // Game initial state
  randomSeed((uint32_t)micros());

  dotX = MATRIX_WIDTH  / 2;
  dotY = MATRIX_HEIGHT / 2;

  dotColor    = matrix.Color(DOT_R, DOT_G, DOT_B);
  circleColor = wheel(random(256));

  drawScene();
}

void loop() {
  // Read accelerometer
  float ax, ay, az;
  if (imu.readAccelMPS2(ax, ay, az)) {
    // Debug if needed:
    // Serial.print("AX: "); Serial.print(ax);
    // Serial.print("  AY: "); Serial.print(ay);
    // Serial.print("  AZ: "); Serial.println(az);

    updateDotFromTilt(ax, ay);
  }

  // Check collision with circle
  bool onCircle = isOnCircle(dotX, dotY);
  if (onCircle && !wasOnCircle) {
    // Just touched circle: change color + beep
    circleColor = wheel(random(256));
    buzzOnce();
  }
  wasOnCircle = onCircle;

  // Redraw
  drawScene();

  // Small delay so we don’t hammer I2C too hard
  delay(10);
}

Nghĩ bạn có thể cần

Tài nguyên & tài liệu tham khảo

Tập tin📁

Tập tin Fritzing