This tutorial is part of: ESP32-S3 RGB LED 매트릭스
ESP32-S3 RGB 매트릭스 모듈을 활용한 재미있고 실용적인 프로젝트를 만들어 보세요. 다른 영상 링크는 이 글 아래에 있습니다.
ESP32-S3 RGB LED 매트릭스 프로젝트 5 - 화살표는 항상 위로
프로젝트 5 – 화살표는 항상 위로 (QMI8658C를 사용한 방향 표시기)
프로젝트 5는 QMI8658C 모션 센서를 사용하여 ESP32-S3 RGB LED 매트릭스의 방향을 감지하고 중력에 대해 항상 위를 가리키는 화살표를 표시합니다. 보드를 어떻게 회전시키든—USB 쪽이 위로, OUSB 쪽이 위로, “15” 쪽, 또는 “34” 쪽—화살표는 자동으로 회전하여 물리적으로 위쪽 방향을 가리킵니다.
이것은 온보드 가속도계를 사용한 실시간 방향 감지의 강력한 데모입니다. 이 모듈의 6개 프로젝트 모두 하나의 YouTube 비디오에서 시연되며, 이 페이지에도 포함되어 있습니다. 프로젝트 5의 전체 코드는 기사 아래에 자동으로 로드되며, 제휴 링크는 코드 섹션 아래에 나타납니다.

ESP32-S3 RGB LED 매트릭스 모듈 개요
ESP32-S3 RGB LED 매트릭스 모듈에는 이 프로젝트를 가능하게 하는 여러 구성 요소가 포함되어 있습니다:

- ESP32-S3 마이크로컨트롤러 — Wi-Fi, BLE를 제공하고 LED/IMU 로직을 실행합니다.
- 8×8 RGB LED 매트릭스 — 네 가지 방향 중 하나로 화살표를 표시합니다.
- QMI8658C 가속도계 — 기울기, 움직임 및 방향을 감지합니다.:contentReference[oaicite:0]{index=0}
- USB-C 포트 — Arduino IDE에서 전원 공급 및 프로그래밍용.
- 리셋 및 부트 버튼 — 스케치 업로드용.
- 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: 타겟 게임
비디오는 모듈이 회전됨에 따라 화살표가 어떻게 즉시 방향을 바꾸는지 명확하게 보여줍니다. 이 부분을 시청하는 것을 강력히 권장합니다.:contentReference[oaicite:1]{index=1}

Arduino IDE에서 ESP32 보드 설치
이전 프로젝트를 완료했다면 보드 설정은 이미 완료된 것입니다. 그렇지 않다면:
파일 > 기본 설정→ ESP32 보드 URL 추가도구 > 보드 > 보드 매니저…→ “ESP32” 설치도구 > 보드에서 ESP32-S3 보드 선택도구 > 포트에서 올바른 USB COM 포트 선택
필요한 라이브러리 설치
프로젝트 5는 다음을 사용합니다:
Adafruit NeoMatrixAdafruit NeoPixelAdafruit GFXQMI8658(모션 센서)
스케치 > 라이브러리 포함 > 라이브러리 관리…- 검색: NeoMatrix → 설치
- 종속성 설치: NeoPixel + GFX
- 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;
// 권장 방향: 왼쪽 상단 원점, 프로그레시브 모드
// (실제 생성자는 아래에 로드된 코드 내부에 있습니다)
이 프로젝트는 실제 움직임에 따라 화살표가 올바르게 가리키도록 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 매트릭스와 QMI8658C 가속도계가 함께 작동하여 방향을 감지하고 항상 위쪽을 가리키는 화살표를 표시하는 방법을 보여줍니다. 이 프로젝트는 기울기 점(프로젝트 4)을 기반으로 하며 프로젝트 6의 최종 인터랙티브 게임을 준비합니다.
전체 “항상 위를 가리키는 화살표” 스케치는 이 문서 아래에 있습니다(자동 로드됨). 보드 회전에 화살표가 얼마나 즉각적으로 반응하는지 확인하려면 해당 비디오 부분을 시청하는 것이 좋습니다. 이 프로젝트를 집에서 만들고 싶다면 ESP32-S3 RGB LED 매트릭스 모듈에 대한 제휴 링크가 코드 섹션 아래에 있습니다.
이미지
This tutorial is part of: ESP32-S3 RGB LED 매트릭스
- ESP32-S3 RGB LED 매트릭스 프로젝트 1- 기본 점
- ESP32-S3 RGB LED 매트릭스 프로젝트 2 - 스크롤 텍스트
- ESP32-S3 RGB LED 매트릭스 프로젝트 3 - 휴대폰에서 텍스트 보내기
- ESP32-S3 RGB LED 매트릭스 프로젝트 4 - 기울기 점
- ESP32-S3 RGB LED 매트릭스 프로젝트 6 - 사격 게임
- ESP32-S3 RGB LED 매트릭스 Wi-Fi + NTP 시간 시계 프로젝트 -1 기본 시계
- ESP32-S3 RGB LED 매트릭스 인터넷 시계 프로젝트 - 2개의 시계 멀티 컬러 시간 및 날짜 표시
- ESP32-S3 RGB LED 매트릭스 인터넷 시계 프로젝트 - 날짜 포함 3가지 야간 색상
- ESP32-S3 RGB LED 매트릭스 인터넷 시계 프로젝트 - 5 무지개 색상
- ESP32-S3 RGB LED 매트릭스 인터넷 시계 프로젝트 - 4가지 랜덤 색상
- ESP32-S3 RGB LED Matrix test for RGB, GRB setting
/*
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);
}
필요할 수 있는 것들
-
아마존
-
이베이
-
알리익스프레스Purchase ESP32-S3 RGB Matrix from AliExpresss.click.aliexpress.com
-
알리익스프레스Purchase ESP32-S3 RGB Matrix from AliExpress (2)s.click.aliexpress.com
자원 및 참고자료
-
내부🎨 Color picker Toolrobojax.com
파일📁
프리징 파일
-
esp32-S3-supermini-tht fritzing part
esp32-S3-supermini-tht.fzpz0.02 MB