BOOTSEL 버튼으로 5분마다 Enter 보내는 Pico 2 입력기
BOOTSEL 버튼으로 5분마다 Enter 보내는 Pico 2 입력기
USB 케이블 하나만 꽂아 쓰는 자동 Enter 키보드의 펌웨어를 작성하고 빌드했다. 부팅 직후에는 꺼져 있고, 정상 부팅 후 BOOTSEL을 누르면 Enter를 한 번 보낸 뒤 5분 간격으로 반복하도록 설계했다.
0. 먼저 결과부터
| 확인 항목 | 이번 기록의 결과 | 근거와 한계 |
|---|---|---|
| RP2350 부트로더 | 확인 | INFO_UF2.TXT에 Model: Raspberry Pi RP2350. 이 표기만으로 비-W Pico 2 실물까지 단정할 수는 없다. |
| 펌웨어 빌드 | 성공 | Arduino-Pico 6.3.1, Cortex-M33 ARM, 종료 코드 0 |
| UF2 산출 | 성공 | Pico2_Bootsel_Enter.ino.uf2 생성 및 SHA-256 기록 |
| 장치 업로드 | 미완료 | 에이전트 환경의 볼륨 복사에 Operation not permitted |
| USB HID와 버튼 동작 | 미검증 | 업로드되지 않아 LED, Enter, 5분 주기를 확인하지 못함 |
1. 필요한 것은 보드와 데이터 케이블
대상은 Raspberry Pi Pico 2 일반 모델이다. 추가 GPIO 배선이나 외부 버튼 없이 내장 BOOTSEL 버튼과 LED를 쓴다. 준비물은 Pico 2, 데이터 전송 가능한 Micro USB 케이블, Arduino IDE 2.x가 설치된 PC다. 처음 보드 패키지를 설치할 때는 인터넷도 필요하다.
BOOTSEL 토글 · LED 상태→ USB HID →PC
현재 포커스 창에 Enter
이 그림은 신호 흐름을 설명한 개념도이며 실제 보드 연결 사진이나 기능 검증 사진이 아니다.
설계한 동작
| 상황 | 예상 상태 | 예상 입력 |
|---|---|---|
| USB로 부팅 | OFF · LED 꺼짐 | 없음 |
| 정상 부팅 후 BOOTSEL 첫 누름 | ON · LED 켜짐 | 디바운스 확인 직후 Enter 1회 |
| ON 유지 | LED 켜짐 | 마지막 전송 시도 후 300,000 ms마다 1회 |
| BOOTSEL 다시 누름 | OFF · LED 꺼짐 | 없음, 반복 중지 |
| 다시 ON | LED 켜짐 | 즉시 1회, 5분 타이머 새로 시작 |
35 ms 동안 안정된 눌림만 인정하고 10 ms마다 버튼을 확인한다. 길게 누른다고 여러 번 토글되지 않도록 눌림 에지만 처리했다. BOOTSEL은 일반 GPIO가 아니므로 digitalRead() 대신 Arduino-Pico의 BOOTSEL 값을 사용한다.
2. Arduino 설정과 코드
Arduino IDE의 추가 보드 관리자 URL에 아래 주소를 등록하고 Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower를 설치한다. 이 기록의 성공 빌드는 6.3.1을 사용했다. 보드는 Raspberry Pi Pico 2, USB Stack은 Pico SDK, CPU는 ARM(Cortex-M33)이다. 이 코드의 Keyboard.h를 쓰면서 Adafruit TinyUSB 설정을 선택하지 않는다.
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
다음은 사양서에 실린 전체 스케치다. 실제 업로드 및 실물 테스트를 통과한 코드라는 뜻은 아니다.
/*
* Project: Pico 2 BOOTSEL Enter Auto Keyboard
* Board: Raspberry Pi Pico 2 (RP2350; standard non-W board)
* Core: Earle F. Philhower's Arduino-Pico
* USB Stack: Pico SDK (not Adafruit TinyUSB)
*
* On USB power-up: OFF, LED OFF, no keystrokes.
* BOOTSEL short press while running:
* OFF -> ON: immediately send one Enter, then every 5 minutes.
* ON -> OFF: immediately stop sending Enter.
*
* Note: BOOTSEL is NOT a normal GPIO. Arduino-Pico exposes it as a bool.
*/
#include <Arduino.h>
#include <Keyboard.h>
// ---------------------- Adjustable settings ------------------------------
constexpr uint32_t ENTER_INTERVAL_MS = 5UL * 60UL * 1000UL;
constexpr uint32_t DEBOUNCE_MS = 35UL;
constexpr uint32_t POLL_INTERVAL_MS = 10UL;
// ---------------------- Internal state -----------------------------------
bool autoEnterEnabled = false;
bool lastRawPressed = false;
bool stablePressed = false;
uint32_t rawChangedAtMs = 0;
uint32_t lastEnterAtMs = 0;
// Called only while the feature is enabled.
void sendEnter() {
// Keyboard.write() sends a press + release sequence.
Keyboard.write(KEY_RETURN);
// Schedule the next repeat 5 minutes after this key event.
lastEnterAtMs = millis();
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Keyboard.begin();
// Use current button state as the baseline; never auto-enable at boot.
// BOOTSEL held during plugging in USB instead enters firmware upload mode.
lastRawPressed = (bool)BOOTSEL;
stablePressed = lastRawPressed;
rawChangedAtMs = millis();
}
void loop() {
uint32_t now = millis();
bool rawPressed = (bool)BOOTSEL;
// Debounce: restart the clock whenever raw input changes.
if (rawPressed != lastRawPressed) {
lastRawPressed = rawPressed;
rawChangedAtMs = now;
}
// Count one action per stable pressed edge, not continuously while held.
if ((uint32_t)(now - rawChangedAtMs) >= DEBOUNCE_MS &&
rawPressed != stablePressed) {
stablePressed = rawPressed;
if (stablePressed) {
autoEnterEnabled = !autoEnterEnabled;
digitalWrite(LED_BUILTIN, autoEnterEnabled ? HIGH : LOW);
if (autoEnterEnabled) {
// First press: send Enter immediately (after debouncing).
sendEnter();
}
// When switching OFF, no key is sent. No keys remain pressed.
}
}
// Unsigned subtraction remains correct across millis() rollover.
now = millis();
if (autoEnterEnabled &&
(uint32_t)(now - lastEnterAtMs) >= ENTER_INTERVAL_MS) {
sendEnter();
}
// Keep polling responsive without blocking 5 minutes.
delay(POLL_INTERVAL_MS);
}
Keyboard.write(KEY_RETURN)가 키 눌림/떼기를 담당한다. 긴 5분 지연 대신 millis() 차이를 비교해 버튼을 계속 읽는다. 타이머는 전송 시도 시점에 갱신된다. PC의 활성 프로그램이 입력을 실제로 받았는지는 펌웨어가 알 수 없다.
3. 빌드하면서 막힌 곳과 확인한 수치
처음 실행 환경에서는 보드 패키지가 없었고 패키지 인덱스 다운로드도 DNS 조회 실패로 중단됐다. 그래서 첫 컴파일 시도는 코드 검사 단계까지 가지 못했다.
lookup downloads.arduino.cc: no such host
lookup github.com: no such host
Platform 'rp2040:rp2040' not found: platform not installed
이후 사용자 macOS 터미널에서 빌드 스크립트를 실행해 보드 패키지를 설치하고 빌드에 성공했다. 처음의 실패가 최종 빌드 상태는 아니다. 보드 설정은 rp2040:rp2040:rpipico2, 빌드 종료 코드는 0이었다.
| 항목 | 기록된 값 |
|---|---|
| Flash | 54,256 / 4,186,112 바이트 |
| 전역 RAM | 10,280 / 524,288 바이트 |
| UF2 SHA-256 | 0c7318a36ad48c167d2f26a704d44976cb6c3ce9eed1f3cd48c6654ee8b404f2 |
| 원본 스케치 SHA-256 | 7f72f4fc44ba6ea77544855025b6da1b0b387a71b8bf16eca45def5139568859 |
BOOTSEL을 누른 채 USB에 연결했을 때 RP2350 볼륨이 나타났고 부트로더 정보는 다음과 같았다.
UF2 Bootloader v1.0
Model: Raspberry Pi RP2350
Board-ID: RP2350
하지만 이 실행 환경에서 picotool은 접근 가능한 장치를 찾지 못했고 UF2 복사는 Operation not permitted로 실패했다. 따라서 빌드 성공과 업로드 성공은 다른 결과다.
4. 실기 검증은 아직 남았다
다음 단계는 Pico 2를 BOOTSEL을 누른 채 USB에 꽂아 부트로더 볼륨을 확인한 다음, 생성된 UF2를 해당 볼륨에 복사하는 것이다. 기존 펌웨어는 덮어쓸 수 있으므로 필요한 파일은 먼저 백업한다. 볼륨 이름은 환경마다 달라질 수 있으니 INFO_UF2.TXT를 확인하고 복사 대상도 다시 점검해야 한다. 이 절차는 이 글에서 실행 완료로 기록하지 않는다.
복사 후 부트로더 볼륨이 사라지는 것만으로 성공을 단정하지 않는다. 재부팅 뒤 USB HID 키보드로 잡히는지 보고, 안전한 빈 텍스트 문서에 커서를 둔 다음 아래 항목을 시험해야 한다.
- 전원 연결 직후 LED가 꺼져 있고 입력이 없는지 확인한다.
- BOOTSEL을 짧게 한 번 눌러 LED ON과 Enter 1회를 확인한다.
- 버튼을 2초 이상 누르고 있어도 추가 토글이나 Enter가 없는지 본다.
- 실제 5분 뒤 Enter 1회가 추가되는지 확인한다.
- 다시 눌러 OFF로 만든 후 5분 이상 추가 입력이 없는지 확인한다.
- 다시 ON으로 바꿔 즉시 1회와 새 5분 간격을 확인하고, 전원을 분리했다 재연결하면 OFF에서 시작하는지도 확인한다.
빠른 반복 시험이 필요하다면 ENTER_INTERVAL_MS를 일시적으로 5000UL로 바꿔 별도 빌드·업로드할 수 있다. 이번 기록에서는 5초 시험용 펌웨어를 만들거나 올리지 않았다. 시험 후에는 원래의 5UL * 60UL * 1000UL로 되돌리고 재업로드해야 한다.
5. Enter 키인 만큼 조심할 점
이 장치는 특정 앱을 찾아 입력하지 않는다. PC에서 현재 포커스를 가진 창으로 Enter가 들어간다. 확인 대화상자, 터미널, 메신저처럼 Enter가 뜻밖의 동작을 할 수 있는 곳에서는 먼저 BOOTSEL로 OFF로 바꾸거나 USB 전원을 끊자. 절전 중이거나 USB 연결이 끊긴 상태의 전달은 보장되지 않는다. BOOTSEL을 누른 상태로 전원을 연결하면 토글이 아니라 펌웨어 업로드 모드로 들어간다.
지금 확보한 결과는 컴파일된 UF2까지다. 실제로 5분마다 Enter를 누르는지에 관한 답은 업로드와 실물 시험 뒤에만 쓸 수 있다.
댓글
댓글 쓰기