티스토리 뷰
728x90
XIAO ESP32C6 + ST7789 TFT 영상 재생 — 셋업 전체 기록
숨으로 바람개비를 돌리면 자석 센서가 회전을 감지하고, 그 주기에 맞춰 영상과 모터가 반응하는 인터랙티브 설치 작품을 만들면서 겪은 과정을 정리했다.
하드웨어
- XIAO ESP32C6 (Seeed Studio)
- ST7789 TFT 디스플레이 (2.0", 240×320)
- MLX90393 자석 센서
- N20 DC 모터 + 2N2222A 트랜지스터
1. 보드 설정
Arduino IDE에서 보드를 처음 잡을 때 ESP32 Family Device로 잡히는 경우가 있다. 이건 틀렸다.
ESP32 Family Device ← 너무 넓은 분류, 핀 번호 등 세부 설정이 안 맞음
XIAO_ESP32C6 ← 정확한 모델 지정 필요
보드를 정확히 지정해야 GPIO 번호, 메모리 주소, 플래시 크기가 맞게 들어간다.
2. TFT 라이브러리 선택
처음에 TFT_eSPI 라이브러리를 시도했다. User_Setup.h에서 핀 번호를 직접 수정해야 하고 설정이 복잡하다. 결국 Adafruit_ST7789로 교체했다.
최종 사용 라이브러리: Adafruit_ST7789
이유:
- 설정이 단순하고 코드에서 직접 핀을 지정할 수 있음
- 하드웨어 SPI를 명시적으로 지정 가능
- setSPISpeed() 지원
TFT 핀 연결
TFT XIAO ESP32C6
| MOSI | D10 (GPIO18) |
| SCK | D8 (GPIO19) |
| CS | D3 (GPIO21) |
| DC | D4 (GPIO22) |
| RST | D5 (GPIO23) |
| BLK | 3.3V |
3. 소프트웨어 SPI vs 하드웨어 SPI
처음 Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST) 방식으로 초기화하면 소프트웨어 SPI로 동작한다. 화면 출력 시간이 2169ms가 나왔다.
하드웨어 SPI로 바꾸면 24ms로 줄어든다.
// ❌ 소프트웨어 SPI (느림, 2169ms)
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST);
// ✅ 하드웨어 SPI (빠름, 24ms)
SPIClass* spi = new SPIClass(SPI);
Adafruit_ST7789 tft = Adafruit_ST7789(spi, TFT_CS, TFT_DC, TFT_RST);
// setup()에서
spi->begin(TFT_SCK, -1, TFT_MOSI, TFT_CS);
tft.init(240, 320, SPI_MODE0);
tft.setSPISpeed(40000000);
4. 영상 → TFT 출력 전체 파이프라인
전체 흐름
영상(mp4) → 프레임 추출(ffmpeg) → RGB565 변환(Python) → LittleFS 패키징 → Flash 업로드(esptool) → TFT 출력
Step 1. 영상 → 이미지 프레임 (ffmpeg)
5초 24fps 영상에서 6프레임마다 1장씩 총 21장 추출.
import subprocess, os, glob
videos = sorted(glob.glob("*.mp4"))
for video in videos:
name = os.path.splitext(video)[0]
output_dir = f"frames_{name}"
os.makedirs(output_dir, exist_ok=True)
cmd = [
"ffmpeg", "-i", video,
"-vf", "scale=200:200,select='not(mod(n\\,6))'",
"-vsync", "vfr",
"-q:v", "2",
os.path.join(output_dir, "frame_%04d.jpg")
]
subprocess.run(cmd)
Step 2. 이미지 → RGB565 변환 (Python)
TFT는 JPG를 직접 못 읽는다. 픽셀을 16bit 숫자로 변환한 .raw 파일로 저장해야 한다.
from PIL import Image
import os, glob, struct
input_dir = "frames"
output_dir = "data"
os.makedirs(output_dir, exist_ok=True)
files = sorted(glob.glob(os.path.join(input_dir, "*.jpg")))[:21]
for i, f in enumerate(files):
img = Image.open(f).convert("RGB").resize((200, 200))
pixels = []
for r, g, b in img.getdata():
rgb565 = ((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3)
pixels.append(struct.pack("<H", rgb565)) # little-endian
out_path = os.path.join(output_dir, f"img_{i:02d}.raw")
with open(out_path, "wb") as out:
out.write(b"".join(pixels))
Step 3. raw 파일 → LittleFS 이미지 (mklittlefs)
21개 .raw 파일을 하나의 littlefs.bin으로 패키징.
[Arduino 설치 경로]/packages/esp32/tools/mklittlefs/[버전]/mklittlefs \
-c [data 폴더 경로] \
-s 1966080 \
-b 4096 \
-p 256 \
littlefs.bin
Step 4. littlefs.bin → XIAO Flash (esptool)
Flash의 0x210000 주소(SPIFFS/LittleFS 영역)에 직접 굽는다.
[Arduino 설치 경로]/packages/esp32/tools/esptool_py/[버전]/esptool \
--chip esp32c6 \
--port /dev/cu.usbmodem[포트번호] \
--baud 921600 \
write_flash 0x210000 littlefs.bin
Flash 메모리 구조
XIAO Flash 4MB
├── 0x000000 부트로더
├── 0x010000 스케치 (Arduino IDE로 업로드)
└── 0x210000 LittleFS (esptool로 업로드)
img_00.raw ~ img_20.raw
5. 이미지 출력 코드
❌ 잘못된 방법 (위에서 아래로 렌더링 보임)
// 줄마다 drawRGBBitmap 호출 → 스캔라인이 눈에 보임
for (int y = 0; y < IMG_HEIGHT; y++) {
tft.drawRGBBitmap(x, y, lineBuf, IMG_WIDTH, 1);
}
✅ 올바른 방법 (한번에 전송)
uint16_t frameBuf[200 * 200];
// 전체 읽기
f.read((uint8_t*)frameBuf, IMG_WIDTH * IMG_HEIGHT * 2);
// 바이트 스왑 (색상 순서 보정)
for (int i = 0; i < IMG_WIDTH * IMG_HEIGHT; i++) {
uint16_t val = frameBuf[i];
frameBuf[i] = (val >> 8) | (val << 8);
}
// 한번에 전송
tft.startWrite();
tft.setAddrWindow(60, 20, IMG_WIDTH, IMG_HEIGHT); // 중앙 배치
tft.writePixels(frameBuf, IMG_WIDTH * IMG_HEIGHT);
tft.endWrite();
6. 다른 장치들과 연결시 초기화 순서
TFT가 먼저 시작되면, 뒤에 통신들을 못받는 에러가 난다.
1. Wire.begin(SDA, SCL) // I2C 먼저
2. sensor.begin_I2C()
3. ledcAttach(MOTOR_PIN, ...) // 모터
4. spi->begin(...) // SPI
5. LittleFS.begin()
6. tft.init() // TFT 마지막
728x90
'Iot > Arduino' 카테고리의 다른 글
| 아두이노(seeed Xiao) + 자석감지 센서 MLX90393 + 디스플레이 ST7789 TFT (2.0", 240×320) : 뮤토스코프 + 숨 프로젝트 (0) | 2026.03.01 |
|---|---|
| OV7670_아두이노 _카메라 (0) | 2024.04.26 |
| arduino nano 33 IoT _ 무선 wifi 연결 + mqtt 통신 (0) | 2024.04.15 |
| 미니모터(ja12-n20) +아두이노 + 드라이버 (HG7881) (0) | 2024.03.30 |
| 아두이노 + LED strip (6812 RGBW) (0) | 2024.02.21 |
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- RNN
- Unity
- node.js
- Java
- TouchDesigner
- OpenClaw
- Midjourney
- 4d guassian splatting
- AI
- three.js
- 후디니
- sequelize
- opencv
- ESP32
- 라즈베리파이
- zclaw
- 유니티
- DeepLeaning
- houdini
- VR
- Python
- opticalflow
- MCP
- Express
- MQTT
- colab
- VFXgraph
- CNC
- Arduino
- docker
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
글 보관함

