Search Code

โปรเจกต์ ESP32-S3 RGB LED Matrix 5 - ลูกศรชี้ขึ้นเสมอ

โปรเจกต์ ESP32-S3 RGB LED Matrix 5 - ลูกศรชี้ขึ้นเสมอ

โปรเจกต์ 5 – ลูกศรชี้ขึ้นเสมอ (ตัวบ่งชี้ทิศทางโดยใช้ QMI8658C)

โปรเจกต์ 5 ใช้เซนเซอร์ตรวจจับการเคลื่อนไหว QMI8658C เพื่อตรวจจับทิศทางของ ESP32-S3 RGB LED Matrix และแสดงลูกศรที่ชี้ขึ้นโดยสัมพันธ์กับแรงโน้มถ่วงเสมอ ไม่ว่าคุณจะหมุนบอร์ดอย่างไร—ด้าน USB ขึ้น ด้าน OUSB ขึ้น ด้าน “15” หรือด้าน “34”—ลูกศรจะหมุนโดยอัตโนมัติและชี้ไปยังทิศทางที่อยู่ด้านบนตามจริงทางกายภาพ

นี่คือการสาธิตอันทรงพลังของการตรวจจับทิศทางแบบเรียลไทม์โดยใช้มาตรความเร่งในตัว โปรเจกต์ทั้งหกของโมดูลนี้สาธิตในวิดีโอ YouTube หนึ่งรายการ ซึ่งฝังอยู่ในหน้านี้ด้วย โค้ดที่สมบูรณ์สำหรับโปรเจกต์ 5 จะโหลดโดยอัตโนมัติด้านล่างบทความ และลิงก์พันธมิตรจะปรากฏใต้ส่วนโค้ด

ภาพรวมโมดูล ESP32-S3 RGB LED Matrix

โมดูล ESP32-S3 RGB LED Matrix มีส่วนประกอบหลายอย่างที่ทำให้โปรเจกต์นี้เป็นไปได้:

  • ไมโครคอนโทรลเลอร์ ESP32-S3 — ให้ Wi-Fi, BLE และรันตรรกะของ LED/IMU
  • เมทริกซ์ LED RGB ขนาด 8×8 — แสดงลูกศรในทิศทางใดทิศทางหนึ่งจากสี่ทิศทาง
  • มาตรความเร่ง QMI8658C — ตรวจจับการเอียง การเคลื่อนไหว และทิศทาง
  • พอร์ต USB-C สำหรับจ่ายไฟและการเขียนโปรแกรมใน Arduino IDE
  • ปุ่ม Reset และ Boot สำหรับอัปโหลดสเก็ตช์
  • พิน GPIO ที่มีอยู่รอบบอร์ดสำหรับโปรเจกต์เพิ่มเติม

ทิศทางของลูกศรถูกกำหนดโดยค่าที่อ่านได้จากมาตรความเร่งทั้งหมด เมื่อหมุนบอร์ด QMI8658C จะตรวจจับค่า X/Y/Z ใหม่ และสเก็ตช์จะเลือกรูปแบบลูกศร (↑, ↓, ←, →) ที่ควรวาด

โปรเจกต์ที่ครอบคลุมในวิดีโอ (ไทม์สแตมป์)

  • 00:00 – บทนำ
  • 02:01 – การติดตั้งบอร์ด ESP32
  • 03:32 – การติดตั้งไลบรารี
  • 05:32 – โปรเจกต์ 1: จุดเคลื่อนที่
  • 11:11 – โปรเจกต์ 2: ข้อความเลื่อน
  • 12:59 – โปรเจกต์ 3: ข้อความ HTTP
  • 16:41 – โปรเจกต์ 4: จุดเอียง
  • 18:55โปรเจกต์ 5: ลูกศรชี้ขึ้นเสมอ (โปรเจกต์นี้)
  • 20:02 – โปรเจกต์ 6: เกมยิงเป้า

วิดีโอแสดงให้เห็นชัดเจนว่าลูกศรเปลี่ยนทิศทางทันทีตามการหมุนของโมดูล ขอแนะนำอย่างยิ่งให้ชมส่วนนี้

การติดตั้งบอร์ด ESP32 ใน Arduino IDE

หากคุณทำโปรเจกต์ก่อนหน้าเสร็จสิ้นแล้ว การตั้งค่าบอร์ดก็เสร็จเรียบร้อยแล้ว มิฉะนั้น:

  1. File > Preferences → เพิ่ม URL ของบอร์ด ESP32
  2. Tools > Board > Boards Manager… → ติดตั้ง “ESP32”
  3. เลือกบอร์ด ESP32-S3 ภายใต้ Tools > Board
  4. เลือกพอร์ต USB COM ที่ถูกต้องภายใต้ Tools > Port

การติดตั้งไลบรารีที่จำเป็น

โปรเจกต์ 5 ใช้:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX
  • QMI8658 (เซนเซอร์ตรวจจับการเคลื่อนไหว)
  1. Sketch > Include Library > Manage Libraries…
  2. ค้นหา: NeoMatrix → ติดตั้ง
  3. ติดตั้งไลบรารีที่ต้องพึ่งพา: NeoPixel + GFX
  4. ค้นหาและติดตั้ง QMI8658 โดยผู้เขียน

โปรเจกต์ 5 ทำงานอย่างไร

QMI8658C วัดแรงโน้มถ่วงบนแกน X, Y และ Z โดยการเปรียบเทียบค่าเหล่านี้ สเก็ตช์จะกำหนดว่าด้านใดของบอร์ดที่หงายขึ้นทางกายภาพ:

  • ด้าน USB ขึ้น
  • ด้าน OUSB ขึ้น (ตรงข้ามกับ USB)
  • ด้าน “15” ขึ้น
  • ด้าน “34” ขึ้น

แต่ละทิศทางสอดคล้องกับรูปแบบลูกศรที่แตกต่างกันบนเมทริกซ์ 8×8 การแมปเป็นไปตามตรรกะการวางแนวที่คุณยืนยันแล้วจากการดีบักครั้งก่อน การหมุนของบอร์ดจะถูกอ่านอย่างต่อเนื่อง และลูกศรจะอัปเดตทันทีที่ด้านที่หงายขึ้นเปลี่ยนแปลง

โปรเจกต์ 5 – การตั้งค่าโค้ด (ลูกศรชี้ขึ้นเสมอ)

ด้านล่างคือค่าที่ผู้ใช้ปรับได้จากพื้นที่การกำหนดค่า โค้ดโปรเจกต์ที่สมบูรณ์จะปรากฏโดยอัตโนมัติด้านล่างบทความ

การกำหนดค่าเมทริกซ์


// การกำหนดค่าเมทริกซ์
const int MATRIX_PIN    = 14;   // คงที่สำหรับโมดูลนี้
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// การวางแนวที่แนะนำ: จุดเริ่มต้นมุมบนซ้าย โหมด progressive
// (คอนสตรักเตอร์จริงอยู่ในโค้ดที่โหลดด้านล่าง)

โปรเจกต์นี้ใช้รูปแบบ NEO_MATRIX_PROGRESSIVE เพื่อให้แน่ใจว่าลูกศรชี้ถูกต้องตามการเคลื่อนไหวจริง

ความสว่าง


uint8_t matrixBrightness = 40;   // 0–255

คุณสามารถเพิ่มค่านี้สำหรับสภาพแวดล้อมที่สว่างกว่า สำหรับการใช้ในร่ม 30–60 ก็สบายตา

สีลูกศร


// สีลูกศร
uint8_t arrowRed   = 255;
uint8_t arrowGreen = 0;
uint8_t arrowBlue  = 0;

เปลี่ยนค่าเหล่านี้เพื่อปรับสีของลูกศร ตัวอย่างเช่น:

  • ลูกศรสีเขียว: (0, 255, 0)
  • ลูกศรสีน้ำเงิน: (0, 0, 255)
  • ลูกศรสีขาว: (255, 255, 255)

ความไวและการปรับให้เรียบ

เพื่อหลีกเลี่ยงการกระตุก โค้ดจึงรวมตรรกะการปรับให้เรียบและค่าเกณฑ์ ในการตั้งค่าคุณอาจพบสิ่งเช่น:


// การปรับความไว / การปรับให้เรียบ
float tiltThreshold = 0.30f;   // ปรับหากลูกศรเปลี่ยนง่ายเกินไป
  • หากลูกศรของคุณพลิกง่ายเกินไป → เพิ่ม ค่าเกณฑ์
  • หากลูกศรเปลี่ยนช้าเกินไป → ลด ค่าเกณฑ์

รูปแบบลูกศร

สเก็ตช์นี้มีรูปแบบบิตแมปของลูกศรสำหรับ:

  • ↑ ขึ้น
  • ↓ ลง
  • ← ซ้าย
  • → ขวา

คุณไม่จำเป็นต้องแก้ไขสิ่งเหล่านี้ แต่คุณสามารถเปลี่ยนรูปร่างภายในโค้ดได้หากต้องการสไตล์ที่แตกต่าง

สรุป

โปรเจกต์ 5 สาธิตการทำงานร่วมกันของ ESP32-S3 RGB LED Matrix และเซนเซอร์วัดความเร่ง QMI8658C เพื่อตรวจจับทิศทางและแสดงลูกศรที่ชี้ขึ้นด้านบนเสมอ โปรเจกต์นี้ต่อยอดจาก Tilt Dot (โปรเจกต์ 4) และเตรียมคุณให้พร้อมสำหรับเกมอินเทอร์แอคทีฟสุดท้ายในโปรเจกต์ 6

สเก็ตช์เต็ม “Arrow Always Up” มีให้ด้านล่างบทความนี้ (โหลดอัตโนมัติ) การดูวิดีโอส่วนที่เกี่ยวข้องเป็นสิ่งที่แนะนำอย่างยิ่งเพื่อให้เห็นว่าลูกศรตอบสนองต่อการหมุนบอร์ดได้ทันทีเพียงใด หากคุณต้องการสร้างโปรเจกต์นี้ที่บ้าน ลิงก์พันธมิตรสำหรับโมดูล ESP32-S3 RGB LED Matrix อยู่ใต้ส่วนโค้ด

ภาพ

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
ภาษา: 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);
}

สิ่งที่คุณอาจต้องการ

แหล่งข้อมูลและเอกสารอ้างอิง

ไฟล์📁

ไฟล์ Fritzing