This tutorial is part of: ESP32-S3 RGB LED 매트릭스
ESP32-S3 RGB 매트릭스 모듈을 활용한 재미있고 실용적인 프로젝트를 만들어 보세요. 다른 영상 링크는 이 글 아래에 있습니다.
ESP32-S3 RGB LED 매트릭스 프로젝트 4 - 기울기 점
프로젝트 4 – 틸트 도트 (ESP32-S3 RGB LED 매트릭스를 기울여 도트 움직이기)
프로젝트 4는 ESP32-S3 RGB LED 매트릭스 모듈에 내장된 모션 센서를 소개합니다. 도트를 자동으로 움직이거나(프로젝트 1) 텍스트를 스크롤하는(프로젝트 2 및 3) 대신, 이 프로젝트는 보드를 기울이는 것만으로 도트의 위치를 제어할 수 있게 해줍니다. 도트는 모듈 뒷면의 QMI8658C 가속도계의 실시간 측정값을 기반으로 8×8 RGB 디스플레이에서 부드럽게 움직입니다.
이 시리즈의 6개 프로젝트 모두 하나의 YouTube 비디오에서 시연됩니다. 동일한 비디오가 이 페이지에 포함되어 있으므로, 보드를 기울일 때 도트가 실시간으로 어떻게 움직이는지 정확히 볼 수 있습니다. 이 프로젝트의 전체 코드는 아래 기사에 자동으로 로드되며, 모듈에 대한 제휴 구매 링크는 코드 섹션 아래에 나타납니다.


ESP32-S3 RGB LED 매트릭스 모듈 개요
이 프로젝트는 다음을 포함하는 ESP32-S3 RGB LED 매트릭스 모듈을 사용합니다:

- ESP32-S3 마이크로컨트롤러 (Wi-Fi 및 Bluetooth 포함)
- 8×8 RGB LED 매트릭스 (개별 주소 지정 가능한 64개 LED)
- QMI8658C 가속도계 (기울기 및 모션 감지용, 뒷면에 위치)
- USB 포트 (프로그래밍 및 전원 공급용)
- 부팅 / 리셋 버튼
- 사용 가능한 GPIO 핀 (향후 확장용)
QMI8658C 센서는 X, Y, Z 가속도 및 방향 값을 읽어, 보드가 기울어진 방향에 따라 도트가 위/아래/왼쪽/오른쪽으로 움직일 수 있게 합니다.:contentReference[oaicite:0]{index=0}

비디오에서 다루는 프로젝트 (타임스탬프)
- 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 보드를 선택합니다.도구 > 포트에서 올바른 COM 포트를 선택합니다.
필요한 라이브러리 설치
이 프로젝트에는 다음 라이브러리가 필요합니다:
Adafruit NeoMatrixAdafruit NeoPixelAdafruit GFX LibraryQMI8658(모션 센서)
라이브러리 매니저에서 설치하세요:
스케치 > 라이브러리 포함 > 라이브러리 관리…를 엽니다.- Adafruit NeoMatrix를 검색 → 설치합니다.
- Adafruit GFX 및 Adafruit NeoPixel의 자동 설치를 허용합니다.
- 나열된 작성자별로 QMI8658을 검색 → 설치합니다.:contentReference[oaicite:2]{index=2}
프로젝트 4 작동 방식
QMI8658C 센서는 X, Y, Z 축을 따라 가속도 데이터를 지속적으로 제공합니다. 이 프로젝트에서는 X 및 Y 축만 사용하여 다음을 결정합니다:
- 도트를 왼쪽 또는 오른쪽으로 얼마나 이동할지 (X 축)
- 도트를 위 또는 아래로 얼마나 이동할지 (Y 축)
센서 값은 0에서 7까지의 좌표 범위(8×8 LED 매트릭스용)로 매핑됩니다. 도트 위치는 초당 여러 번 업데이트되어 모듈을 기울일 때 부드러운 슬라이딩 효과를 제공합니다.:contentReference[oaicite:3]{index=3}
프로젝트 4 – 코드 설정 (틸트 도트)
아래는 프로젝트 코드 상단 근처에서 사용자가 편집할 수 있는 설정입니다. 전체 스케치는 아래 기사에 자동으로 나타납니다.
매트릭스 핀 및 크기
// 8×8 RGB 매트릭스 구성
const int MATRIX_PIN = 14; // 이 보드의 고정 핀
const int MATRIX_WIDTH = 8;
const int MATRIX_HEIGHT = 8;
MATRIX_PIN을 14로 유지하세요. 보드 내장 매트릭스에 하드와이어되어 있습니다.
밝기
// 전체 밝기 (0–255)
uint8_t matrixBrightness = 40;
필요하면 높이되, 가까이서 볼 때 극단적인 밝기는 피하세요.
도트 색상
// 도트 색상 (R, G, B)
uint8_t dotRed = 0;
uint8_t dotGreen = 200; // 연두색 (기본값)
uint8_t dotBlue = 0;
이 값을 변경하여 원하는 색상을 만들 수 있습니다. 예:
- 빨간색:
(255, 0, 0) - 노란색:
(255, 255, 0) - 흰색:
(255, 255, 255)
모션 감도
극단적인 점프를 방지하기 위해 가속도계 값은 일반적으로 클램프되거나 스케일링됩니다. 일반적인 설정은 다음과 같습니다:
// 기울기가 움직임에 영향을 주는 강도
float sensitivity = 4.0f; // 값이 클수록 화면에서 더 빠르게 이동
도트가 너무 느리게 움직이면 → 값을 높이세요. 도트가 너무 갑작스럽게 움직이면 → 값을 낮추세요.
업데이트 속도 (새로고침 빈도)
업데이트 사이에 약간의 지연을 추가하여 움직임을 부드럽게 할 수 있습니다:
// 위치 업데이트 사이의 지연 (ms)
int refreshDelayMs = 20; // 낮을수록 더 부드럽고 빠른 반응
10–30 ms 사이의 값은 매우 반응성이 뛰어납니다.
요약
프로젝트 4는 ESP32-S3에 내장된 QMI8658C 가속도계를 활용하여 물리적 움직임으로 LED 매트릭스를 제어할 수 있게 합니다. 보드를 약간 기울이면 점이 같은 방향으로 움직이며, 이 프로젝트는 더 고급인 “Arrow Up” 및 “Target Game” 프로젝트를 위한 완벽한 디딤돌이 됩니다.
전체 Tilt Dot 스케치는 이 문서 아래에 (자동으로) 로드됩니다. 가장 잘 이해하려면 비디오에서 기울기 데모를 시청하세요. 보드가 회전할 때 점이 얼마나 부드럽게 움직이는지 확인할 수 있습니다. 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 매트릭스 프로젝트 5 - 화살표는 항상 위로
- 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 4: Tilt Dot – 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
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 -> dot on opposite side: USB↔OUSB, 34↔15
// false -> dot on same side as UP
bool useOppositeMapping = false;
// Dot color (0–255 each)
uint8_t dotRed = 0;
uint8_t dotGreen = 100;
uint8_t dotBlue = 0;
// Board sides
enum Side {
SIDE_CENTER = 0,
SIDE_USB,
SIDE_OUSB,
SIDE_15,
SIDE_34
};
// Smooth dot position (in pixel coordinates, but kept as float for easing)
float dotPosX = 3.0f; // start at center
float dotPosY = 3.0f;
// Smoothing factor: smaller = slower movement (0.1 very slow, 0.5 faster)
const float dotSmooth = 0.25f;
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 "?";
}
}
// Detect which side is UP using calibrated axes:
// +X = USB, -X = OUSB, +Y = 34, -Y = 15
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;
// 1) Prefer Y axis for 15 / 34 if it's clearly tilted
if (fabs(ay_g) >= tiltThreshY) {
if (ay_g > 0) {
return SIDE_15; // +Y = 34 up
} else {
return SIDE_34; // -Y = 15 up
}
}
// 2) Otherwise, check 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
}
}
// 3) If nothing is strongly tilted, just call it CENTER
return SIDE_CENTER;
}
// Map from UP side to where the dot should go
Side dotSideFromUpSide(Side upSide) {
switch (upSide) {
case SIDE_USB:
return useOppositeMapping ? SIDE_OUSB : SIDE_USB;
case SIDE_OUSB:
return useOppositeMapping ? SIDE_USB : SIDE_OUSB;
case SIDE_34:
return useOppositeMapping ? SIDE_15 : SIDE_34;
case SIDE_15:
return useOppositeMapping ? SIDE_34 : SIDE_15;
case SIDE_CENTER:
default:
return SIDE_CENTER;
}
}
// Convert dot side to matrix coordinates
void getDotPixel(Side dotSide, int &px, int &py) {
// Matrix (0,0) = top-left
// top center: (3,0) → USB
// bottom center: (3,7) → OUSB
// left center: (0,3) → 15
// right center: (7,3) → 34
// center: (3,3)
switch (dotSide) {
case SIDE_USB: px = 3; py = 0; break;
case SIDE_OUSB: px = 3; py = 7; break;
case SIDE_15: px = 0; py = 3; break;
case SIDE_34: px = 7; py = 3; break;
case SIDE_CENTER:
default: px = 3; py = 3; break;
}
}
void setup() {
Serial.begin(115200);
delay(500);
matrix.begin();
matrix.setBrightness(20);
matrix.fillScreen(0);
matrix.show();
// IMU: SDA=11, SCL=12 for 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 dotSide = dotSideFromUpSide(upSide);
int targetX, targetY;
getDotPixel(dotSide, targetX, targetY);
// --- Smooth movement toward target ---
dotPosX += (targetX - dotPosX) * dotSmooth;
dotPosY += (targetY - dotPosY) * dotSmooth;
// Convert to integer pixel coordinates
int px = (int)round(dotPosX);
int py = (int)round(dotPosY);
// Clamp just in case
if (px < 0) px = 0;
if (px > 7) px = 7;
if (py < 0) py = 0;
if (py > 7) py = 7;
// --- Draw dot ---
matrix.fillScreen(0);
uint16_t color = matrix.Color(dotRed, dotGreen, dotBlue);
matrix.drawPixel(px, py, color);
matrix.show();
// 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(" | DOT="); Serial.print(sideName(dotSide));
Serial.print(" | px="); Serial.print(px);
Serial.print(" py="); Serial.println(py);
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