05. 라즈베리 파이하고 버튼 연결 - EnglishNo/EmbeddedSystem GitHub Wiki

회로 실제 모습 및 회로도

회로 연결 방법

  • 라즈베리 파이4 GPIO17(물리핀 11) -> 브레드보드(+극) -> 버튼 -> 브레드보드(-극) -> 라즈베리 파이 GND
  • 주의 : 버튼을 연결할 때 같은 방향의 핀하고 연결하면 안된다.

버튼의 원리

버튼 한 번의 동작

  1. 사람이 푸시버튼을 누른다.
  2. 물리적 접점이 닫힌다.
  3. BCM GPIO17 전압이 HIGH 3.3V -> LOW 0V로 변경.
    • HIGH 3.3V : 버튼을 떼었을 때의 기본 상태 (내부 풀업)
    • LOW 0V : 버튼을 눌렸을 때의 상태 (GND로 연결)
  4. gpiozero Button(17)이 상태를 해석 (Active-Low 반전).
    • GPIO HIGH (3.3V) -> is_pressed = False
    • GPIO LOW (0V) -> is_pressed = True
    • Active-Low : 전기적 LOW(0V)가 논리적 True(눌림)로 해석.
  5. Python 콜백이 실행.
  6. LED 또는 애플리케이션이 반응.

실습 - 버튼을 통해 입력을 받자

간단한 버튼 실습


from gpiozero import Button     # 버튼 입력 클래스

button = Button(17)             # BCM 17번(GPIO 17), 내부 풀업 사용

print("버튼을 누르세요.")         # 사용자 안내
button.wait_for_press()         # 눌림 상태가 될 때까지 대기

print("버튼이 눌렸습니다.")       # 대기 종료 후 실행
button.close()                  # 사용한 GPIO 자원 반환

  • 오류 : 어째서인지 버튼을 누르지도 않았는데 버튼이 눌렸다는 입력이 나오기 시작. 추후 수정 예정

현재 버튼 상태를 일정한 간격으로 반복 확인

  • 정해진 간격으로 버튼 상태를 계속 확인.
  • 해당 코드는 0.1초 간격(sleep)으로 버튼 상태를 계속 확인.
  • 단, 간격 사이의 짧은 입력을 놓칠 수 있으며, 반복 검사를 할 때 CPU 사용 증가.

from gpiozero import Button     # 버튼 입력 클래스
from time import sleep          # 반복 사이의 대기 함수

button = Button(17)             # BCM 17번, 내부 풀업 사용

try:                            # 실행 후에는 finally에서 자원 정리
    while True:                 # 버튼 상태를 계속 확인
        if button.is_pressed:   # 누르면 True(전압은 LOW)
            print("PRESSED")    # 누르는 동안 반복 출력
        else:                   # 버튼을 떼고 있는 상태
            print("RELEASED")
        sleep(0.1)              # 0.1초 대기 후 다시 확인

finally:                        # Ctrl+C 등으로 벗어나도 정리
    button.close()              # GPIO 자원 반환

버튼하고 이벤트 연결

  • 상태 변화(엣지)가 발생하면 즉시 콜백이 호출.
  • 엣지가 발생하면서 짧은 입력도 정확히 인식.
  • 이벤트 방식이 코드가 더 간단하고, CPU가 여유 있게 됨.
  • 그리고 버튼 입력은 대부분 이벤트 방식을 사용하면 더 정확하고 효율적으로 처리 가능.

버튼을 눌려 이벤트 방식으로 함수 출력


from gpiozero import Button     # 버튼 입력 클래스
from signal import pause        # 메인 실행 흐름 대기

button = Button(17)             # BCM 17번, 내부 풀업 사용

def pressed():                  # 눌림 이벤트에서 실행할 함수
    print("버튼을 눌렀습니다.")

# 함수 자체를 등록: pressed()처럼 호출하지 않음
button.when_pressed = pressed
try:
    pause()                     # 프로그램을 유지하며 이벤트 처리
finally:                        # Ctrl+C 등으로 종료할 때 정리
    button.close()              # GPIO 자원 반환

눌림과 해제 이벤트 서로 다른 함수에 연결


from gpiozero import Button         # 버튼 입력 클래스
from signal import pause            # 메인 실행 흐름 대기

button = Button(17)                 # BCM 17번, 내부 풀업 사용

# 누름 시작: HIGH에서 LOW로 변화
def pressed():
    print("PRESSED")

# 해제: LOW에서 HIGH로 변화
def released():
    print("RELEASED")

button.when_pressed = pressed       # 눌림 콜백 등록
button.when_released = released     # 해제 콜백 등록

# 프로그램을 유지하며 이벤트 처리
try:
    pause()
# 종료 시 자원 정리
finally:
    button.close()                  # GPIO 자원 반환

GPIO17 버튼 입력으로 GPIO18 LED 출력 제어.

  • 버튼을 누르지 않는 상태
    • 입력상태 (GPIO 17 / 물리핀 11) : HIGH 3.3V 상태로 내부 풀업으로 3.3V.
    • 소프트웨어 동작 : 아무 동작 없음 (when_released 후 대기).
    • 출력상태 (GPIO 18 / 물리핀 12) : LOW 0V (전류가 흐르지 않음)
    • LED 상태 : OFF (꺼진 상태).
  • 버튼을 누른 상태
    • 입력상태 (GPIO 17 / 물리핀 11) : LOW 0V 상태로 내부 버튼으로 GND 연결.
    • 소프트웨어 동작 : when_pressed 콜백 실행 -> led.on() 호출.
    • 출력상태 (GPIO 18 / 물리핀 12) : HIGH 3.3V (전류가 흐름)
    • LED 상태 : ON (켜진 상태).
  • 주의 : GPIO에 5V를 연결하지 않음 (GPIO는 3.3V 전용).

실제 회로 모습하고 회로도

회로 구성 과정

  • 입력 관련 회로 : 라즈베리 파이4 GPIO 17(물리핀 11) -> 버튼 -> 라즈베리 파이 GND
  • 출력 관련 회로 : 라즈베리 파이4 GPIO 18(물리핀 12) -> 브레드보드(+극) -> 저항 -> LED -> 브레드보드(-극) -> 라즈베리 파이 GND

버튼을 누르는 동안 LED가 켜지도록 메서드 직접 연결


from gpiozero import Button, LED                # 입력·출력 클래스
from signal import pause                        # 메인 실행 흐름 대기

button = Button(17)                             # BCM 17번 버튼, 내부 풀업
led = LED(18)                                   # BCM 18번 LED 출력(초기 OFF)

button.when_pressed = led.on                    # 누르면 켜기
button.when_released = led.off                  # 떼면 끄기

# led.on과 led.off는 괄호 없이 메서드 자체를 등록
try:
    pause()                                     # 프로그램을 유지하며 이벤트 처리
# 종료 시 LED를 끄고 자원 정리
finally:
    led.off()                                   # LED 끄기

    button.close()                              # 입력 GPIO 자원 반환
    led.close()                                 # 출력 GPIO 자원 반환

  • 현재 라즈베리 파이4 이상으로 인해 테스트 불가...

버튼을 누를 때마다 LED 상태 반대로


from gpiozero import Button, LED            # 입력·출력 클래스
from signal import pause                    # 메인 실행 흐름 대기

# BCM 17번, 내부 풀업, 디바운스 시간 0.1초
button = Button(17, bounce_time=0.1)
led = LED(18)                               # BCM 18번 LED 출력(초기 OFF)

# 눌릴 때마다 호출할 함수
def toggle_led():
    led.toggle()                            # 켜짐과 꺼짐을 서로 바꿈
    state = "ON" if led.is_lit else "OFF"   # 현재 상태
    print(f"LED={state}")                   # 변경된 상태 출력

button.when_pressed = toggle_led            # 눌림 콜백 등록

try:
    pause()                                 # 프로그램을 유지하며 이벤트 처리
# 종료 시 자원 정리
finally:
    led.off()  # LED 끄기
    button.close()  # 입력 GPIO 자원 반환
    led.close()  # 출력 GPIO 자원 반환

여러 번 튀는 버튼

  • 기계식 스위치 바운스 현상
    • 접정이 붙었다 떨어지기를 수 ms 동안 여러 번 반복.
    • 그 결과, 한 번의 누름이 여러 번의 엣지(변화)와 콜백(이벤트)로 인식.
  • 소프트웨어 디바운싱(필터링) 원리
    • 바운스 구간을 무시, 신호가 안정된 후 하나의 논리 이벤트만 생성.
      • 디바운스 없음 (bounce_time = 0) : 여러 번 카운트 (한 번 누름이 여러 개의 이벤트로 인식).
      • 적절한 디바운스 (bounce_time = 0.1) : 한 번만 카운트 (바운스 무시 후 하나의 이벤트로 인식).
      • 과도한 디바운스 (bounce_time = 1.0) : 빠른 연속 누름 누락 (두 번째 누름이 필터 구간 내에 있어 이벤트로 인식 X).

짧은 접점 흔들림을 하나의 입력으로 정리


from gpiozero import Button             # 버튼 입력 클래스
from signal import pause                # 메인 실행 흐름 대기

# 입력 객체 생성
button = Button(
    17,                 # BCM GPIO17 사용
    pull_up=True,       # 내부 풀업: 누르면 LOW
    bounce_time=0.1     # 디바운스 시간 0.1초
)

count = 0                               # 눌림 횟수의 초기값

# 눌림 이벤트 처리
def count_press():
    global count                        # 함수 밖의 count를 수정
    count += 1                          # 콜백이 호출될 때 1 증가
    print(f"count={count}")             # 누적 횟수 출력

button.when_pressed = count_press       # 콜백 등록

try:
    pause()                             # 프로그램을 유지하며 이벤트 처리
finally:
    button.close()

짧게 누르기와 길게 누르기를 서로 다른 명령으로 사용


from gpiozero import Button             # 버튼 입력 클래스
from signal import pause                # 메인 실행 흐름 대기

# 내부 풀업을 사용하는 입력
button = Button(
    17,                 # BCM GPIO17 사용
    bounce_time=0.1,    # 디바운스 시간 0.1초
    hold_time=2.0       # 2초 유지하면 held 이벤트
)

# 누름 시작에 호출(길게 눌러도 실행)
def pressed():
    print("짧게 누름")  # 이 문구는 짧은 누름 판정이 아님

# 2초 유지 후 추가 호출(기본 1회)
def held():
    print("2초 이상 누름")

button.when_pressed = pressed           # 누름 시작 콜백
button.when_held = held                 # 길게 유지 콜백

try:
    pause()                             # 프로그램을 유지하며 이벤트 처리
finally:
    button.close()