회로 실제 모습 및 회로도
회로 연결 방법
- 라즈베리 파이4 GPIO17(물리핀 11) -> 브레드보드(+극) -> 버튼 -> 브레드보드(-극) -> 라즈베리 파이 GND
- 주의 : 버튼을 연결할 때 같은 방향의 핀하고 연결하면 안된다.
버튼의 원리
버튼 한 번의 동작
- 사람이 푸시버튼을 누른다.
- 물리적 접점이 닫힌다.
BCM GPIO17 전압이 HIGH 3.3V -> LOW 0V로 변경.
HIGH 3.3V : 버튼을 떼었을 때의 기본 상태 (내부 풀업)
LOW 0V : 버튼을 눌렸을 때의 상태 (GND로 연결)
gpiozero Button(17)이 상태를 해석 (Active-Low 반전).
GPIO HIGH (3.3V) -> is_pressed = False
GPIO LOW (0V) -> is_pressed = True
Active-Low : 전기적 LOW(0V)가 논리적 True(눌림)로 해석.
Python 콜백이 실행.
- 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()