아!두이노, 라즈베리, 코딩 노가다
ESP32C6_BLE_Gamepad 본문

/*
* ESP32-C6 BLE 게임패드 스케치 (핀맵: board5 기준)
* 보드: ESP32-C6-DevKitC-1
* 라이브러리: lemmingDev/ESP32-BLE-Gamepad + h2zero/NimBLE-Arduino
* ※ ESP32 Arduino core 3.x (C6 지원) + 최신 NimBLE-Arduino 필요
*
* ── 배선 요약 ─────────────────────────────────────────────
* 디지털 버튼 (각 버튼: GPIO ↔ GND, 코드에서 INPUT_PULLUP → 누르면 LOW)
* A = GPIO20 B = GPIO19 X = GPIO18 Y = GPIO9(BOOT)
* L1 = GPIO16 R1 = GPIO17 L2 = GPIO15 R2 = GPIO23
* Select = GPIO10 Start = GPIO11 L3 = GPIO22 R3 = GPIO21
* D-pad: Up=GPIO4 Down=GPIO5 Left=GPIO6 Right=GPIO7
*
* 아날로그 스틱 (ADC1 전용, 포텐셔미터 양단 = 3.3V / GND, 와이퍼 = 핀)
* 왼쪽 X=GPIO0(ADC0) Y=GPIO1(ADC1)
* 오른쪽 X=GPIO2(ADC2) Y=GPIO3(ADC3)
*
* ⚠ 부팅 순간 누르지 말 것: Y(GPIO9=BOOT), 그리고 strap 핀 Up/Down(GPIO4/5)·L2(GPIO15)
* ⚠ 스틱 전원은 반드시 3.3V (5V 금지 — ADC 손상)
* ─────────────────────────────────────────────────────────
*/
#include <Arduino.h>
#include <BleGamepad.h>
BleGamepad bleGamepad("ESP32-C6 Gamepad", "DIY", 100);
// ===== 디지털 버튼 → GPIO =====
struct BtnMap { uint8_t pin; uint8_t button; };
BtnMap buttons[] = {
{ 20, BUTTON_1 }, // A
{ 19, BUTTON_2 }, // B
{ 18, BUTTON_3 }, // X
{ 9, BUTTON_4 }, // Y (GPIO9 = BOOT)
{ 16, BUTTON_5 }, // L1
{ 17, BUTTON_6 }, // R1
{ 15, BUTTON_7 }, // L2 (strap)
{ 23, BUTTON_8 }, // R2
{ 10, BUTTON_9 }, // Select
{ 11, BUTTON_10 }, // Start
{ 22, BUTTON_11 }, // L3
{ 21, BUTTON_12 }, // R3
};
const int NUM_BUTTONS = sizeof(buttons) / sizeof(buttons[0]);
// ===== D-pad(햇) → GPIO =====
#define PIN_DPAD_UP 4
#define PIN_DPAD_DOWN 5
#define PIN_DPAD_LEFT 6
#define PIN_DPAD_RIGHT 7
// ===== 아날로그 스틱 → ADC1 핀 =====
#define PIN_LX 0
#define PIN_LY 1
#define PIN_RX 2
#define PIN_RY 3
const int AXIS_CENTER = 16383; // 0~32767 중앙
const int DEADZONE = 1500; // 중앙 흔들림 무시 폭
void setup()
{
Serial.begin(115200);
for (int i = 0; i < NUM_BUTTONS; i++)
pinMode(buttons[i].pin, INPUT_PULLUP);
pinMode(PIN_DPAD_UP, INPUT_PULLUP);
pinMode(PIN_DPAD_DOWN, INPUT_PULLUP);
pinMode(PIN_DPAD_LEFT, INPUT_PULLUP);
pinMode(PIN_DPAD_RIGHT, INPUT_PULLUP);
analogReadResolution(12); // 0~4095
analogSetAttenuation(ADC_11db); // ~0~3.3V 풀레인지
BleGamepadConfiguration cfg;
cfg.setButtonCount(12);
cfg.setHatSwitchCount(1);
cfg.setAutoReport(false); // loop 끝에서 한 번에 전송
bleGamepad.begin(&cfg);
Serial.println("ESP32-C6 BLE Gamepad 시작 - 호스트에서 페어링하세요.");
}
// 아날로그 읽기 + 데드존 → 0~32767
int readAxis(uint8_t pin)
{
int val = map(analogRead(pin), 0, 4095, 0, 32767);
if (abs(val - AXIS_CENTER) < DEADZONE) val = AXIS_CENTER;
return val;
}
void loop()
{
if (!bleGamepad.isConnected()) { delay(50); return; }
// --- 버튼 (풀업이라 LOW = 눌림) ---
for (int i = 0; i < NUM_BUTTONS; i++) {
if (digitalRead(buttons[i].pin) == LOW)
bleGamepad.press(buttons[i].button);
else
bleGamepad.release(buttons[i].button);
}
// --- D-pad → 8방향 햇 ---
bool up = digitalRead(PIN_DPAD_UP) == LOW;
bool down = digitalRead(PIN_DPAD_DOWN) == LOW;
bool left = digitalRead(PIN_DPAD_LEFT) == LOW;
bool right = digitalRead(PIN_DPAD_RIGHT) == LOW;
signed char hat = HAT_CENTERED;
if (up && right) hat = HAT_UP_RIGHT;
else if (up && left) hat = HAT_UP_LEFT;
else if (down && right) hat = HAT_DOWN_RIGHT;
else if (down && left) hat = HAT_DOWN_LEFT;
else if (up) hat = HAT_UP;
else if (down) hat = HAT_DOWN;
else if (left) hat = HAT_LEFT;
else if (right) hat = HAT_RIGHT;
bleGamepad.setHat1(hat);
// --- 아날로그 스틱 ---
int lx = readAxis(PIN_LX);
int ly = readAxis(PIN_LY);
int rx = readAxis(PIN_RX);
int ry = readAxis(PIN_RY);
// setAxes(x, y, z, rx, ry, rz, slider1, slider2)
bleGamepad.setAxes(lx, ly, 0, rx, ry, 0, 0, 0);
bleGamepad.sendReport(); // 모아둔 상태 한 번에 전송
delay(10); // ~100Hz
}
https://github.com/lemmingDev/ESP32-BLE-Gamepad
GitHub - lemmingDev/ESP32-BLE-Gamepad: Bluetooth LE Gamepad library for the ESP32
Bluetooth LE Gamepad library for the ESP32. Contribute to lemmingDev/ESP32-BLE-Gamepad development by creating an account on GitHub.
github.com
"strap"은 **strapping pin(스트래핑 핀)**을 줄여 쓴 말입니다. 앞의 핀맵에서 GPIO4·5·15 같은 핀에 "strap"이라고 붙여둔 게 이것입니다.
의미는 이렇습니다. ESP32 계열 칩은 **전원이 켜지는 바로 그 순간(리셋이 풀리는 시점)**에 특정 핀 몇 개의 전압이 HIGH인지 LOW인지를 읽어서, 부팅 방식을 결정합니다. 예를 들면 "일반 실행 모드로 부팅할지, 펌웨어 다운로드 모드로 들어갈지", "플래시 전압을 몇 V로 쓸지" 같은 설정을 이 핀들의 초기 상태로 정합니다. 이렇게 부팅 옵션을 정하는 데 쓰이는 핀들을 스트래핑 핀이라고 부릅니다. "strap"은 원래 회로에서 핀을 특정 레벨로 '묶어둔다(고정한다)'는 뜻에서 온 말입니다.
중요한 건 **읽는 시점이 부팅 순간뿐**이라는 겁니다. 부팅이 끝나고 나면 그 핀들은 다시 평범한 일반 GPIO로 쓸 수 있습니다. 그래서 스트래핑 핀에 버튼이나 센서를 연결해도 대부분 문제없이 동작합니다.
문제가 되는 건 딱 한 경우입니다. **전원을 켜는(또는 리셋하는) 바로 그 순간에 그 핀이 원치 않는 레벨로 눌려 있으면**, 칩이 부팅 옵션을 잘못 읽어서 엉뚱한 모드로 들어갑니다. 게임패드로 치면, 스트래핑 핀에 물린 버튼을 누른 채로 전원을 켜면 부팅이 안 되거나 다운로드 모드로 빠질 수 있다는 뜻입니다. 그래서 앞에서 "strap 핀에 연결된 버튼은 부팅 순간에 누르지 마세요"라고 안내드린 것입니다.
ESP32-C6의 스트래핑 핀은 GPIO4·5·8·9·15입니다. 이 중 GPIO9는 BOOT 버튼과 연결돼 있어서(다운로드 모드 진입용) 가장 민감하고, GPIO8은 부팅 시 HIGH여야 하며 온보드 RGB LED와도 엮여 있습니다. 나머지 GPIO4·5·15는 JTAG/플래시 관련 설정에 쓰이는데 일반 게임패드 용도에는 영향이 거의 없어, 켤 때만 안 눌리면 사실상 문제없습니다.
'ESP32' 카테고리의 다른 글
| ESP32 C6 보드 핀맵 (0) | 2026.09.15 |
|---|---|
| ESP32를 출시별 정리 (0) | 2026.09.04 |
