안녕하세요! 데브구일입니다 :D
이번엔 펌웨어 차례입니다!
여러가지 방법을 사용 할 수 있을텐데요.
우선은 먼저 유선을 먼저 적용시켜 볼게요!
우선 먼저 ESP-IDF 설치가 되어있어야합니다!
이건 이전에 설치했던 포스팅을 참고해주세요!
먼저 간단한 예제를 실행해보겠습니다!
예제를 기반으로해서 수정할 생각입니다!
ESP-IDF에서 new project를 실행하시고, hid를 검색하시면!
아래와 같은 프로젝트가 있습니다!
여기서 tusb_hid가 유선 hid 관련 예제이니 참고하시구요!
이걸로 먼저 실행해보겠습니다!

프로젝트를 여시고..!
기본적인 타겟은 MCU가 ESP32-S3 모델이기 때문에
ESP32-S3로 설정하겠습니다!
설정하시고 난 다음, 시리얼 포트를 연결하셔야하는데요!
USB 별도의 ftdi를 사용하는게 아니기 때문에 별도의 조작이 필요합니다!
BOOT핀을 누르고 있는 상태에서! 리셋 버튼을 누르셔야 하는데요!
BOOT핀을 가장 좌측 상단 버튼(F1)에 지정해뒀으니,
F1을 누른상태에서 뒤쪽에 있는 리셋 버튼을 한번 눌러줍니다!


이렇게 해서 시리얼 포트를 검색하시면!
이런식으로 없던 포트가 생겨납니다!
이걸 연결하시고! 기본 예제를 업로드 하시면 됩니다!

기본 예제는 boot핀 버튼을 누르면 A가 입력되고, 커서가 사각형으로 움직이는 예제인데요!
업로드 되고 나서 boot핀을 눌러보세요! 그럼 A키가 입력되고 마우스가 움직이는걸 보실 수 있습니다!
코드는 직접 수정하셔도 되는데요!
일단은 제가 사용한 핀들이 있기때문에 핀을 수정해보도록 하겠습니다!
하지만 여기서 문제가 생겼습니다.
로터리 엔코더의 핀을 제가 잘 못세팅했나봐요 ㅠ
테스트를 하는데 동작이 안되더라구요 ㅠㅠ
로터리 엔코더가 제대로 인식안되는 문제가생겨서 로터리 엔코더의 GPIO핀을 변경했습니다! ㅠㅠ
35, 36, 37을 사용하다가 12, 13, 14로 변경했어요!
물론 기존 포스팅에는 전부 수정해뒀습니다!
#define A_BUTTON (GPIO_NUM_0) // Use BOOT signal by default
#define B_BUTTON (GPIO_NUM_18)
#define C_BUTTON (GPIO_NUM_17)
#define D_BUTTON (GPIO_NUM_16)
#define E_BUTTON (GPIO_NUM_5)
#define F_BUTTON (GPIO_NUM_6)
#define G_BUTTON (GPIO_NUM_7)
#define H_BUTTON (GPIO_NUM_15)
#define ROTARY_PIN_A (GPIO_NUM_14)
#define ROTARY_PIN_B (GPIO_NUM_13)
#define ROTARY_PIN_SW (GPIO_NUM_12)
// #define ROTARY_PIN_A (GPIO_NUM_37)
// #define ROTARY_PIN_B (GPIO_NUM_36)
// #define ROTARY_PIN_SW (GPIO_NUM_35)
이제 이 키의 GPIO를 사용할 수 있도록 세팅해줘야하는데요!
저는 기존 main에 있는 초기화를 조금 수정해서!
아래와 같은 함수로 만들어줬고
이걸 main에서 호출하는 형태로 구성했어요!
void button_init(gpio_num_t button_pin)
{
const gpio_config_t button_config = {
.pin_bit_mask = BIT64(button_pin),
.mode = GPIO_MODE_INPUT,
.intr_type = GPIO_INTR_DISABLE,
.pull_up_en = true,
.pull_down_en = false,
};
ESP_ERROR_CHECK(gpio_config(&button_config));
}
...
void app_main(void)
{
button_init(A_BUTTON);
button_init(B_BUTTON);
button_init(C_BUTTON);
button_init(D_BUTTON);
button_init(E_BUTTON);
button_init(F_BUTTON);
button_init(G_BUTTON);
button_init(H_BUTTON);
button_init(ROTARY_PIN_SW);
...
이제 키 입력부분을 좀 더 쉽게 사용하기 위에 함수를 하나 추가해줄게요!
여기에 입력할 키를 넣어주면 입력되도록 수정해봤습니다!
static void key_code_send(uint8_t keycode)
{
if (!tud_mounted()) {
return;
}
uint8_t keycode_array[6] = {keycode};
while (!tud_hid_ready()) {
vTaskDelay(pdMS_TO_TICKS(1));
}
tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, keycode_array);
vTaskDelay(pdMS_TO_TICKS(8));
while (!tud_hid_ready()) {
vTaskDelay(pdMS_TO_TICKS(1));
}
tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, NULL);
vTaskDelay(pdMS_TO_TICKS(4));
}
다음은 코드에서 버튼을 읽어서 키가 입력되도록 할 건데요!
저는 아래와 같이 수정을 했고 각 버튼에 위 함수를 호출하는 형태로 구성했어요!
똑같은 형태가 반복되는 형태에요!
// Button state tracking for debouncing
static bool last_a_btn = true; // pull-up: HIGH when not pressed
static bool last_b_btn = true;
static bool last_c_btn = true;
static bool last_d_btn = true;
static bool last_e_btn = true;
static bool last_f_btn = true;
static bool last_g_btn = true;
static bool last_h_btn = true;
static bool last_rt_btn = true;
while (1) {
if (tud_mounted()) {
bool a_btn_bool = gpio_get_level(A_BUTTON);
bool b_btn_bool = gpio_get_level(B_BUTTON);
bool c_btn_bool = gpio_get_level(C_BUTTON);
bool d_btn_bool = gpio_get_level(D_BUTTON);
bool e_btn_bool = gpio_get_level(E_BUTTON);
bool f_btn_bool = gpio_get_level(F_BUTTON);
bool g_btn_bool = gpio_get_level(G_BUTTON);
bool h_btn_bool = gpio_get_level(H_BUTTON);
bool rt_btn_bool = gpio_get_level(ROTARY_PIN_SW);
// APP_BUTTON: detect falling edge (pressed)
if (last_a_btn && !a_btn_bool) {
ESP_LOGI(TAG, "A button pressed!");
a_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_a_btn = a_btn_bool;
// B_BUTTON: detect falling edge (pressed)
if (last_b_btn && !b_btn_bool) {
ESP_LOGI(TAG, "B button pressed!");
b_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_b_btn = b_btn_bool;
// C_BUTTON: detect falling edge (pressed)
if (last_c_btn && !c_btn_bool) {
ESP_LOGI(TAG, "C button pressed!");
c_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_c_btn = c_btn_bool;
// D_BUTTON: detect falling edge (pressed)
if (last_d_btn && !d_btn_bool) {
ESP_LOGI(TAG, "D button pressed!");
d_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_d_btn = d_btn_bool;
// E_BUTTON: detect falling edge (pressed)
if (last_e_btn && !e_btn_bool) {
ESP_LOGI(TAG, "E button pressed!");
e_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_e_btn = e_btn_bool;
// F_BUTTON: detect falling edge (pressed)
if (last_f_btn && !f_btn_bool) {
ESP_LOGI(TAG, "F button pressed!");
f_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_f_btn = f_btn_bool;
// G_BUTTON: detect falling edge (pressed)
if (last_g_btn && !g_btn_bool) {
ESP_LOGI(TAG, "G button pressed!");
g_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_g_btn = g_btn_bool;
// H_BUTTON: detect falling edge (pressed)
if (last_h_btn && !h_btn_bool) {
ESP_LOGI(TAG, "H button pressed!");
h_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_h_btn = h_btn_bool;
// RT_BUTTON: detect falling edge (pressed)
if (last_rt_btn && !rt_btn_bool) {
ESP_LOGI(TAG, "RT button pressed!");
rt_btn();
vTaskDelay(pdMS_TO_TICKS(10)); // debounce delay
}
last_rt_btn = rt_btn_bool;
}
else {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
호출되는 a_btn()과 같은 함수는 아래와 같이 생겼어요
a_btn()가 호출되면 key_code_send(HID_KEY_A)가 호출되는 형태인데요!
여기에 여러 키가 입력이 되도록 하려면 b_btn()처럼 구성하시면 됩니다!
여기는 필요한 버튼을 구성해보시면 좋아요!
static void a_btn(void)
{
key_code_send(HID_KEY_A);
}
static void b_btn(void)
{
key_code_send(HID_KEY_1);
vTaskDelay(pdMS_TO_TICKS(10));
key_code_send(HID_KEY_2);
}
static void c_btn(void)
{
key_code_send(HID_KEY_C);
}
...
이 코드를 먼저 테스트 해보겠습니다!
aaabbbccddabcdabcdefghefghefghabcdabcdefghefghabcdabcd << 입력한 흔적 ㅋㅋ
입력이 잘됩니다!!

이제 여기에 로터리 엔코더도 연결해야하는데요!
코드가 너무 길어져서 엔코더는 다른 포스팅에 이어서 진행하겠습니다!!

그리고 이 포스팅은 PCBWay의 지원을 받아 제작하게 되었다는 점 알려드립니다 :D
PCBWay에 감사드립니다!
'DIY' 카테고리의 다른 글
| [DIY] 매크로 패드 제작기 7편 (로터리 펌웨어) (0) | 2026.04.12 |
|---|---|
| [DIY] 매크로 패드 제작기 5편 (케이스 제작) (0) | 2026.03.22 |
| [DIY] 매크로 패드 제작기 4편 (SMT 작업) (0) | 2026.03.16 |
| [DIY] 매크로 패드 제작기 3편 (PCB 주문하기) (1) | 2026.03.09 |
| [DIY] 매크로 패드 제작기 2편 (회로 및 아트웍) (0) | 2026.03.07 |
