DIY / / 2026. 5. 8. 17:46

[DIY] 매크로 패드 제작기 외전 (무선이 될까?)

728x90

안녕하세요 데브구일입니다 :D

 

만든 키보드 가지고 조금 사용을 해봤습니다!

게임할때도 써보고... 일할때도 써봤습니다.

 

근데 제가 사용하는게 대부분 무선 제품이라서,

유선 제품을 쓰려고 하니 조금 불편하더라구요!

 

그래서 어떻게 할까 하다가...

다이소에서 파는 보조 배터리를 보고 이걸 끼우고 사용 할 수 있을까??

라는 생각이 들더라구요?? ㅋㅋㅋㅋ

 

다이소 보조 배터리

 

그래서 한번 구매해서 연결을 해봤습니다!

따로 의도한거 전혀 아니였는데, 연결하고 보니까

지금 케이스를 딱히 수정하지 않아도 연결하는데 문제가 없겠더라구요!

 

보조 배터리가... 연결이 된다!

 

이제 이걸 연결하면 더이상 연결할 수 있는 유선 라인은 없어서...

무선으로 밖에 쓸 수가 없습니다!

그래서 펌웨어를 조금 수정해봤어요

수정할때는 esp_hid_device라는 예제를 참고해서 코드를 수정했어요!

 

기본으로 제공되는 예제 코드를 적극 활용하자

 

이번 무선 기능은 완전 처음부터 다 짠 게 아니라,

ESP-IDF 예제를 기반으로 가져와서 현재 프로젝트에 맞게 이식한 형태입니다.

실제로 가져온 부분은 아래입니다.

  • BLE HID 초기화 흐름 (esp_hid_gap_init, esp_hidd_dev_init)
  • BLE Advertising 시작/재시작 처리
  • BLE 보안/인증 이후 HID 전송 준비 상태를 여는 구조
  • HID report map 구성 방식

 

그리고 그대로 쓰지 않고, 프로젝트에 맞게 아래를 수정했습니다.

  • select_flag 기반 USB/BLE 전송 분기 추가
  • D_BUTTON 부팅 모드 선택 로직 추가
  • esp_hid_gap.c/.h를 main 폴더로 직접 이관해서 로컬 소스로 관리
  • 현재 ESP-IDF 환경에 맞게 BLE 광고 API 사용 정리
  • 로터리/버튼 공통 입력 로직과 BLE 전송을 자연스럽게 연결

요약하면, 예제의 "BLE 기본 뼈대"를 가져오고,

실제 매크로패드에 맞는 "입력/모드/안정화 로직"을 덧붙인 형태라고 보시면됩니다!

 

우선 유선과 무선을 모두 사용 할 수 있게하려고 별도의 플래그를 사용해서 기능을 나눴어요!

 

기존 입력 처리 코드는 잘 동작하고 있었기 때문에, 입력부를 갈아엎지 않고 전송부만 분기했습니다.

  • select_flag == false -> USB HID 전송
  • select_flag == true -> BLE HID 전송

이렇게 분리해두면 a_btn(), b_btn(), rotary_encoder() 같은 함수들은 거의 건드리지 않아도 됩니다!

bool select_flag = false;

static void key_code_send(uint8_t keycode)
{
    if (select_flag) {
        ble_send_key_code(keycode);
        return;
    }
    // USB keyboard report
}

그리고 여기서 부팅이 될때, 선택을 할 수 있도록하려고,

부팅 시 D_BUTTON을 누르고 있으면 BLE로 올라오게 만들었습니다. (active-low라서 0이면 눌림입니다)

vTaskDelay(pdMS_TO_TICKS(20));
select_flag = (gpio_get_level(D_BUTTON) == 0);
ESP_LOGI(TAG, "Boot mode select by D button: %s", select_flag ? "BLE" : "USB");

 

ble_hid_task_start_up()

BLE 인증 완료 시 호출되는 함수입니다.
여기서 report_ready = true로 바꿔서 "이제 리포트 보내도 된다" 상태를 열어줍니다.

void ble_hid_task_start_up(void)
{
    s_ble_hid.report_ready = true;
    ESP_LOGI(TAG, "BLE HID AUTH READY");
}

 

ble_hid_ready()

BLE 전송 가능 상태 체크 함수입니다.

  • 시작됨(started)
  • 연결됨(connected)
  • 인증/구독 완료(report_ready)
  • 핸들 유효(hid_dev != NULL)

이 조건이 모두 true일 때만 BLE 리포트를 보냅니다.

 

ble_send_key_code(uint8_t keycode)

BLE 키보드 입력 전송 함수입니다.

  • press report 전송
  • 짧게 대기
  • release report 전송

USB의 tud_hid_keyboard_report 흐름과 비슷하게 동작하도록 맞췄습니다.

static void ble_send_key_code(uint8_t keycode)
{
#if CONFIG_BT_BLE_ENABLED && CONFIG_BT_BLUEDROID_ENABLED
    if (!ble_hid_ready()) {
        return;
    }

    uint8_t report[8] = {0};
    report[2] = keycode;
    esp_err_t err = esp_hidd_dev_input_set(s_ble_hid.hid_dev, 0, BLE_KEYBOARD_REPORT_ID, report, sizeof(report));
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "BLE key report send failed: %s", esp_err_to_name(err));
        return;
    }
    vTaskDelay(pdMS_TO_TICKS(8));

    uint8_t release_report[8] = {0};
    err = esp_hidd_dev_input_set(s_ble_hid.hid_dev, 0, BLE_KEYBOARD_REPORT_ID, release_report, sizeof(release_report));
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "BLE key release send failed: %s", esp_err_to_name(err));
        return;
    }
    vTaskDelay(pdMS_TO_TICKS(4));
#endif
}

 

send_mouse_wheel(int8_t clicks)

로터리 휠 전송 함수입니다.
이 함수가 select_flag를 보고 USB/BLE를 알아서 분기합니다.

그래서 로터리 엔코더 쪽은 "휠 몇 칸 움직였는지"만 계산하면 되고, 실제 전송 경로는 공통 함수가 처리합니다.

static void send_mouse_wheel(int8_t clicks)
{
    if (clicks == 0) {
        return;
    }

    if (select_flag) {
#if CONFIG_BT_BLE_ENABLED && CONFIG_BT_BLUEDROID_ENABLED
        if (!ble_hid_ready()) {
            return;
        }
        uint8_t report[5] = {0, 0, 0, (uint8_t)clicks, 0};
        esp_err_t err = esp_hidd_dev_input_set(s_ble_hid.hid_dev, 0, BLE_MOUSE_REPORT_ID, report, sizeof(report));
        if (err != ESP_OK) {
            ESP_LOGW(TAG, "BLE wheel report send failed: %s", esp_err_to_name(err));
        }
#endif
        return;
    }

    if (!tud_mounted()) {
        return;
    }

    while (!tud_hid_ready()) {
        vTaskDelay(pdMS_TO_TICKS(1));
    }
    tud_hid_mouse_report(HID_ITF_PROTOCOL_MOUSE, 0x00, 0, 0, clicks, 0);
}

 

그런데, 여기서 처음엔 BLE 연결만 되면 바로 입력될 줄 알았는데,

그게 아니더라구요.

 

로그에 이런 메시지가 계속 나왔습니다.

  • Indicate Not Enabled
  • GATT_INSUF_AUTHENTICATION

즉, 연결은 되었지만 인증/구독이 완전히 끝나지 않아서 리포트 전송이 막히던 상황이었습니다.

 

그래서 report_ready 플래그를 두고, 준비 완료 전에는 전송 자체를 막는 방식으로 해결했습니다~!

이 부분 적용하고 나서 BLE 입력 안정성이 확실히 올라갔습니다!

로터리도 무선에서 동일하게 동작하도록 연결했습니다.

  • 회전 -> send_mouse_wheel() 호출
  • 로터리 버튼 -> key_code_send() 호출

 

그리고 워치독(task_wdt)도 같이 잡아줬습니다.

  • rotary_task 주기 완화 (1ms -> 5ms)
  • 태스크 우선순위 낮춤
  • 듀얼코어에서는 CPU1 고정
  • 메인 루프에서도 짧게 vTaskDelay 넣어서 시스템 태스크에 CPU 양보

 

이렇게 해서 코드를 업로드하고~

전원을 켤때 F4(D 버튼)키를 누른채로 리셋 버튼을 누르려고했는데..!!

막상 보니까 보조 배터리에 가려져서 못 누르겠더라구요 ㅠㅠ

그래서 F4키를 누른채 보조 배터리를 꽂으니!

 

 

디바이스 추가가 뜹니다!

저기에 나오는 핀 번호는 로그에 뜨는데!

캡처를 못했어요 ㅠ

저 상태에서 연결을 누르시면 연결이 됩니다!!

 

 

따란~ 블루투스 페어링이 가능해집니다!!

이렇게 연결이 되었고 잘 동작하는걸 확인했습니다!!

메모장에 이것저것 눌러보면!

입력이 됩니다!!

 

 

어쩌다보니 유선인걸 무선까지 해보았어요 ㅎㅎ

 

아래 깃 링크는 무선이 적용된 코드입니다!

참고하시면 되겠습니다!

 

GitHub - Dev-91/Macro_Pad_BLE

Contribute to Dev-91/Macro_Pad_BLE development by creating an account on GitHub.

github.com

 

 

이렇게 프로젝트를 하나 끝내보았는데요!

이번에 하면서 느낀게,

초보자가 보기엔 코드가 조금 복잡할 수 있으니... 이런 저런 설명을하고,

최대한 쉽게 풀어내려고 했지만 생각처럼 잘 안되었던거 같아요 ㅠㅠ

 

다음엔 좀 더 쉬운 방향으로 구현을 해볼까 싶고...

차라리 아두이노 같은 걸 이용하는게, 혹시나 보시는 분들은 더 편하실 것 같기도해서!!!

다음엔 아두이노를 코드와 라이브러리를 최대한 활용해서 구현해보도록 하겠습니다!!

 

PCBWay 감사합니다 :D

그리고 이 포스팅은 PCBWay의 지원을 받아 제작하게 되었다는 점 알려드립니다!

다시 한 번 감사드립니다.

PCBWay 감사합니다 :D

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유