Search Code

Dự án Ma trận LED RGB ESP32-S3 5 - Mũi tên luôn hướng lên

Dự án Ma trận LED RGB ESP32-S3 5 - Mũi tên luôn hướng lên

Dự án 5 – Mũi tên Luôn Hướng Lên (Chỉ báo Định hướng sử dụng QMI8658C)

Dự án 5 sử dụng cảm biến chuyển động QMI8658C để phát hiện định hướng của ESP32-S3 RGB LED Matrix và luôn hiển thị một mũi tên hướng LÊN so với trọng lực. Dù bạn xoay bo mạch theo cách nào—cổng USB hướng lên, cổng OUSB hướng lên, cạnh “15”, hay cạnh “34”—mũi tên sẽ tự động xoay và chỉ về hướng vật lý hướng lên trên.

Đây là một minh chứng mạnh mẽ về khả năng cảm biến định hướng thời gian thực sử dụng gia tốc kế tích hợp. Tất cả sáu dự án cho mô-đun này được trình diễn trong một video YouTube, cũng được nhúng trên trang này. Mã hoàn chỉnh cho Dự án 5 tự động tải bên dưới bài viết, và các liên kết tiếp thị liên kết xuất hiện dưới phần mã.

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

Mô-đun ESP32-S3 RGB LED Matrix bao gồm một số thành phần giúp thực hiện dự án này:

  • Vi điều khiển ESP32-S3 — cung cấp Wi-Fi, BLE và chạy logic LED/IMU.
  • Ma trận LED RGB 8×8 — hiển thị mũi tên ở một trong bốn định hướng.
  • Gia tốc kế QMI8658C — phát hiện độ nghiêng, chuyển động và định hướng.:contentReference[oaicite:0]{index=0}
  • Cổng USB-C để cấp nguồn và lập trình trong Arduino IDE.
  • Các nút Reset và Boot để nạp chương trình.
  • Các chân GPIO có sẵn xung quanh bo mạch cho các dự án bổ sung.

Hướng của mũi tên được xác định hoàn toàn bằng các số đọc từ gia tốc kế. Khi bo mạch được xoay, QMI8658C cảm nhận các giá trị X/Y/Z mới và chương trình chọn mẫu mũi tên (↑, ↓, ←, →) nào sẽ được vẽ.

Các Dự án 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 di chuyển
  • 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 nghiêng
  • 18:55Dự án 5: Mũi tên Luôn Hướng Lên (dự án này)
  • 20:02 – Dự án 6: Trò chơi bắn mục tiêu

Video cho thấy rõ ràng cách mũi tên thay đổi hướng ngay lập tức dựa trên cách mô-đun được xoay. Xem phân đoạn này được khuyến nghị mạnh mẽ.:contentReference[oaicite:1]{index=1}

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

Nếu bạn đã hoàn thành bất kỳ dự án nào trước đó, việc thiết lập bo mạch đã hoàn tất. Nếu không:

  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 USB chính xác trong Tools > Port

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

Dự án 5 sử dụng:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX
  • QMI8658 (cảm biến chuyển động)
  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: NeoPixel + GFX
  4. Tìm kiếm và cài đặt QMI8658 theo tác giả của nó

Cách Dự án 5 Hoạt động

QMI8658C đo trọng lực trên các trục X, Y và Z. Bằng cách so sánh các giá trị này, chương trình xác định cạnh vật lý nào của bo mạch đang hướng lên:

  • Cạnh USB hướng lên
  • Cạnh OUSB hướng lên (đối diện USB)
  • Cạnh “15” hướng lên
  • Cạnh “34” hướng lên

Mỗi định hướng tương ứng với một mẫu mũi tên khác nhau trên ma trận 8×8. Việc ánh xạ tuân theo logic định hướng đã xác nhận của bạn từ các phiên gỡ lỗi trước đó. Sự xoay của bo mạch được đọc liên tục và mũi tên cập nhật ngay khi cạnh hướng lên thay đổi.

Dự án 5 – Cài đặt Mã (Mũi tên Luôn Hướng Lên)

Dưới đây là các giá trị người dùng có thể điều chỉnh từ khu vực cấu hình. Mã dự án đầy đủ tự động xuất hiện bên dưới bài viết.

Cấu hình Ma trận


// Cấu hình ma trận
const int MATRIX_PIN    = 14;   // cố định cho mô-đun này
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// Định hướng được khuyến nghị: Gốc trên-trái, chế độ tuần tự
// (hàm tạo thực tế nằm trong mã được tải bên dưới)

Dự án này sử dụng bố cục NEO_MATRIX_PROGRESSIVE để đảm bảo mũi tên chỉ đúng dựa trên chuyển động thực tế.

Độ sáng


uint8_t matrixBrightness = 40;   // 0–255

Bạn có thể tăng giá trị này cho môi trường sáng hơn. Đối với sử dụng trong nhà, 30–60 là thoải mái.

Màu Mũi tên


// Màu mũi tên
uint8_t arrowRed   = 255;
uint8_t arrowGreen = 0;
uint8_t arrowBlue  = 0;

Thay đổi các giá trị này để sửa màu của mũi tên. Ví dụ:

  • Mũi tên xanh lá: (0, 255, 0)
  • Mũi tên xanh dương: (0, 0, 255)
  • Mũi tên trắng: (255, 255, 255)

Độ nhạy và Làm mịn

Để tránh rung lắc, mã bao gồm logic làm mịn và ngưỡng. Trong cài đặt, bạn có thể thấy một cái gì đó như:


// Điều chỉnh độ nhạy / làm mịn
float tiltThreshold = 0.30f;   // điều chỉnh nếu mũi tên thay đổi quá dễ dàng
  • Nếu mũi tên của bạn lật quá dễ dàng → tăng ngưỡng.
  • Nếu mũi tên đổi hướng quá chậm → giảm ngưỡng.

Các Mẫu Mũi Tên

Bản phác thảo bao gồm các mẫu bitmap mũi tên cho:

  • ↑ lên
  • ↓ xuống
  • ← trái
  • → phải

Bạn không cần phải sửa đổi những mẫu này, nhưng bạn có thể thay đổi hình dạng bên trong mã nếu muốn có kiểu khác.

Tóm tắt

Dự án 5 minh họa cách ESP32-S3 RGB LED Matrix và cảm biến gia tốc QMI8658C hoạt động cùng nhau để phát hiện hướng và hiển thị một mũi tên luôn chỉ lên trên. Dự án này xây dựng dựa trên Tilt Dot (Dự án 4) và chuẩn bị cho bạn trò chơi tương tác cuối cùng trong Dự án 6.

Bản phác thảo đầy đủ “Arrow Always Up” có sẵn bên dưới bài viết này (tự động tải). Xem phần tương ứng của video được khuyến nghị cao để thấy mũi tên phản ứng tức thì với việc xoay bo mạch. Nếu bạn muốn tự xây dựng dự án này tại nhà, các liên kết liên kết cho mô-đun ESP32-S3 RGB LED Matrix xuất hiệ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 attached with buzzer to pin6 and GND
ESP32 S3 Matrix attached with buzzer to pin6 and GND
ESP32 S3 Matrix displaying red heart
ESP32 S3 Matrix displaying red heart
ESP32 S3 Matrix displaying green heart
ESP32 S3 Matrix displaying green heart
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
ESP32-S3-Mtrix - Alway Up
ESP32-S3-Mtrix - Alway Up
803-ESP32-S3 RGB LED Matrix Project 5 - Arrow always up
Ngôn ngữ: C++
/* 
  Project 5: Arrow Always Up – 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/RJT833

  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

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

// -------- QMI8658 IMU SETUP --------
QMI8658 imu;
QMI8658_Data imuData;

// -------- USER SETTINGS --------

// true  -> arrow points to opposite side
//         USB↔OUSB, 34↔15
// false -> arrow points to the same side that is UP
bool useOppositeMapping = false;

// Arrow color (0–255 each)
uint8_t dotRed   = 0;
uint8_t dotGreen = 150;
uint8_t dotBlue  = 0;

// Board sides
enum Side {
  SIDE_CENTER = 0,
  SIDE_USB,
  SIDE_OUSB,
  SIDE_15,
  SIDE_34
};

// Direction for arrow drawing
enum ArrowDir {
  ARROW_CENTER,
  ARROW_UP,
  ARROW_DOWN,
  ARROW_LEFT,
  ARROW_RIGHT
};

bool isFlat = false;

const char* sideName(Side s) {
  switch (s) {
    case SIDE_CENTER: return "CENTER";
    case SIDE_USB:    return "USB";
    case SIDE_OUSB:   return "OUSB";
    case SIDE_15:     return "15";
    case SIDE_34:     return "34";
    default:          return "?";
  }
}

// -------- ARROW DRAWING (YOUR CODE, UNCHANGED) --------

// Draw a simple arrow on 8x8 matrix pointing in the given direction
void drawArrow(ArrowDir dir, uint16_t color) {
  matrix.fillScreen(0);

  switch (dir) {
    case ARROW_UP:
      // Tip
      matrix.drawPixel(3, 0, color);
      matrix.drawPixel(4, 0, color);
      // Second row
      matrix.drawPixel(2, 1, color);
      matrix.drawPixel(3, 1, color);
      matrix.drawPixel(4, 1, color);
      matrix.drawPixel(5, 1, color);
      // Shaft
      matrix.drawLine(3, 2, 3, 6, color);
      matrix.drawLine(4, 2, 4, 6, color);
      break;

    case ARROW_DOWN:
      // Tip
      matrix.drawPixel(3, 7, color);
      matrix.drawPixel(4, 7, color);
      // Row above tip
      matrix.drawPixel(2, 6, color);
      matrix.drawPixel(3, 6, color);
      matrix.drawPixel(4, 6, color);
      matrix.drawPixel(5, 6, color);
      // Shaft
      matrix.drawLine(3, 1, 3, 5, color);
      matrix.drawLine(4, 1, 4, 5, color);
      break;

    case ARROW_LEFT:
      // Tip
      matrix.drawPixel(0, 3, color);
      matrix.drawPixel(0, 4, color);
      // Column after tip
      matrix.drawPixel(1, 2, color);
      matrix.drawPixel(1, 3, color);
      matrix.drawPixel(1, 4, color);
      matrix.drawPixel(1, 5, color);
      // Shaft
      matrix.drawLine(2, 3, 6, 3, color);
      matrix.drawLine(2, 4, 6, 4, color);
      break;

    case ARROW_RIGHT:
      // Tip
      matrix.drawPixel(7, 3, color);
      matrix.drawPixel(7, 4, color);
      // Column before tip
      matrix.drawPixel(6, 2, color);
      matrix.drawPixel(6, 3, color);
      matrix.drawPixel(6, 4, color);
      matrix.drawPixel(6, 5, color);
      // Shaft
      matrix.drawLine(1, 3, 5, 3, color);
      matrix.drawLine(1, 4, 5, 4, color);
      break;

    case ARROW_CENTER:
    default:
      // Simple plus in the center
      matrix.drawLine(3, 3, 4, 3, color);
      matrix.drawLine(3, 4, 4, 4, color);
      matrix.drawLine(3, 3, 3, 4, color);
      matrix.drawLine(4, 3, 4, 4, color);
      break;
  }

  matrix.show();
}

// -------- IMU → SIDE DETECTION --------
// We calibrated earlier:
//  +X = USB,   -X = OUSB
//  +Y = 15,    -Y = 34    (after your correction)

Side detectSideUp(float ax_g, float ay_g, float az_g) {
  // Flat detection
  const float flatThreshXY = 0.15f;
  const float flatThreshZ  = 0.15f;

  if (fabs(ax_g) < flatThreshXY &&
      fabs(ay_g) < flatThreshXY &&
      fabs(az_g - 1.0f) < flatThreshZ) {
    isFlat = true;
    return SIDE_CENTER;
  }
  isFlat = false;

  // Thresholds to say "this axis is really tilted"
  const float tiltThreshY = 0.5f;
  const float tiltThreshX = 0.5f;

  // Prefer Y axis for 15 / 34
  if (fabs(ay_g) >= tiltThreshY) {
    if (ay_g > 0) {
      return SIDE_34;   // +Y = 34 up
    } else {
      return SIDE_15;   // -Y = 15 up
    }
  }

  // Otherwise, use X axis for USB / OUSB
  if (fabs(ax_g) >= tiltThreshX) {
    if (ax_g > 0) {
      return SIDE_USB;   // +X = USB up
    } else {
      return SIDE_OUSB;  // -X = OUSB up
    }
  }

  // Not clearly tilted → treat as center
  return SIDE_CENTER;
}

// Map from UP side to where the arrow should point
Side arrowSideFromUpSide(Side upSide) {
  if (!useOppositeMapping) {
    // Arrow shows the side that is UP
    return upSide;
  }

  // Arrow shows the opposite side
  switch (upSide) {
    case SIDE_USB:   return SIDE_OUSB;
    case SIDE_OUSB:  return SIDE_USB;
    case SIDE_15:    return SIDE_34;
    case SIDE_34:    return SIDE_15;
    case SIDE_CENTER:
    default:         return SIDE_CENTER;
  }
}

// Convert SIDE to ArrowDir
ArrowDir arrowDirFromSide(Side s) {
  switch (s) {
    case SIDE_USB:    return ARROW_UP;
    case SIDE_OUSB:   return ARROW_DOWN;
    case SIDE_15:     return ARROW_LEFT;
    case SIDE_34:     return ARROW_RIGHT;
    case SIDE_CENTER:
    default:          return ARROW_CENTER;
  }
}

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

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

  matrix.begin();
  matrix.setBrightness(20);
  matrix.fillScreen(0);
  matrix.show();

  // IMU: SDA=11, SCL=12 on ESP32-S3-Matrix
  if (!imu.begin(11, 12)) {
    Serial.println("Failed to initialize QMI8658!");
    while (1) { delay(1000); }
  }

  imu.setAccelUnit_mg(true);
  imu.setGyroUnit_dps(true);
  imu.setDisplayPrecision(4);

  Serial.print("QMI8658 initialized. useOppositeMapping = ");
  Serial.println(useOppositeMapping ? "TRUE" : "FALSE");
}

void loop() {
  if (!imu.readSensorData(imuData)) {
    return;
  }

  float ax_g = imuData.accelX / 1000.0f;
  float ay_g = imuData.accelY / 1000.0f;
  float az_g = imuData.accelZ / 1000.0f;

  Side upSide     = detectSideUp(ax_g, ay_g, az_g);
  Side arrowSide  = arrowSideFromUpSide(upSide);
  ArrowDir dir    = arrowDirFromSide(arrowSide);

  uint16_t color = matrix.Color(dotRed, dotGreen, dotBlue);
  drawArrow(dir, color);

  // Debug
  Serial.print("AX="); Serial.print(ax_g, 3);
  Serial.print(" AY="); Serial.print(ay_g, 3);
  Serial.print(" AZ="); Serial.print(az_g, 3);
  Serial.print(" | UP=");    Serial.print(sideName(upSide));
  Serial.print(" | ARROW="); Serial.println(sideName(arrowSide));

  delay(80);
}

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