이 튜토리얼에서는 다양한 회로 구성으로 아두이노에서 푸시 버튼을 사용하는 방법을 배웁니다.
또한 다양한 애플리케이션에 푸시 버튼을 사용하는 방법과 인터럽트와 같은 일부 아두이노 기능을 활용하는 방법도 살펴봅니다. 여기서는 아두이노 우노 보드를 사용하지만 이 튜토리얼은 다른 아두이노 보드에서도 작동합니다. 시작해 보겠습니다!
푸시 버튼 회로 준비물:
- 아두이노 보드
- 브레드보드
- 전선 몇 개
- 푸시 버튼
다음은 우리가 만들 회로입니다.
아두이노를 사용하여 나만의 프로젝트를 만드는 방법을 배우고 있나요? 초보자를 위한 아두이노를 확인하고 단계별로 배워보세요.
회로 구축 단계:
- 아두이노의 전원을 끄세요.
- 그림과 같이 브레드보드 중앙에 푸시 버튼을 연결합니다. 버튼의 한쪽 다리에 와이어(가능한 경우 검은색)를 연결하여 아두이노 보드의 GND 핀에 연결합니다.
- 버튼의 왼쪽 상단과 왼쪽 하단 다리는 서로 연결하고 오른쪽 상단과 오른쪽 하단 다리는 서로 연결합니다.
- 왼쪽에 GND 와이어를 꽂았다면 오른쪽에 다른 와이어를 연결하여 서로 연결되지 않도록 합니다. 이 다른 와이어는 디지털 핀(예: 4번)으로 연결됩니다.
현재로서는 회로에 저항이 포함되어 있지 않습니다. 이 글의 다음 부분에서 이에 대해 자세히 설명하겠습니다. 또한 아두이노 핀에 대해 자세히 알고 싶다면 이 아두이노 우노 핀아웃 가이드를 확인하세요.
푸시 버튼의 상태를 읽는 아두이노 코드
여기서 하려는 것은 단순히 버튼에서 상태를 읽고 초당 10회 인쇄하는 것입니다. >> 이 튜토리얼 섹션의 추가 자료로 이 비디오를 시청하세요:
푸시 버튼의 상태 인쇄하기
버튼의 상태를 1초에 10번 인쇄하는 코드는 다음과 같습니다.
#define BUTTON_PIN 4
void setup()
{
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop()
{
Serial.println(digitalRead(BUTTON_PIN));
delay(100);
}
이 코드를 한 줄씩 분석해 보겠습니다.
푸시 버튼을 설정하는 코드
#define BUTTON_PIN 4
먼저 버튼 핀에 #define을 생성하면 코드에서 이 핀을 사용할 때마다 "4"를 작성할 필요가 없습니다. 이렇게 하면 특히 나중에 버튼을 다른 핀에 연결하려는 경우 작업이 더 쉬워집니다. 이렇게 하면 프로그램 상단에 있는 코드 한 줄만 수정하면 되므로 쉽게 찾을 수 있습니다.
void setup()
{
Serial.begin(9600);
void setup() 함수가 먼저 한 번만 호출됩니다. 여기서 하는 일은 직렬 통신을 초기화하여 직렬 모니터를 사용하여 버튼에서 얻은 데이터를 인쇄할 수 있도록 하는 것입니다.
이것이 코드에서 푸시 버튼을 초기화하는 방법입니다. void setup()에서 두 개의 인수를 가진 pinMode() 함수를 사용합니다. 먼저 버튼의 핀(여기서는 "4"로 대체됨)과 핀에 원하는 모드입니다.
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
쓰기보다는 버튼에서 데이터를 읽어야 하므로 입력 모드를 선택합니다. 그리고 여전히 두 가지 옵션이 있습니다: INPUT 또는 INPUT_PULLUP. 여기서는 회로에 저항이 없으므로 INPUT_PULLUP을 사용하여 아두이노 보드의 내부 풀업 저항을 사용하겠습니다. 이 튜토리얼에서는 INPUT_PULLUP에 대해 자세히 설명하지 않겠지만, 더 자세히 알고 싶다면 이 아두이노 INPUT_PULLUP 튜토리얼을 확인하세요.
푸시 버튼의 상태를 읽는 코드
void loop()
{
Serial.println(digitalRead(BUTTON_PIN));
이제 버튼의 핀이 입력으로 초기화되고 무한히 반복해서 호출되는 void loop() 함수에 들어갑니다.
버튼의 상태를 읽기 위해 버튼의 핀을 하나의 인수로 하는 digitalRead() 함수를 사용합니다. 이 함수의 결과는 HIGH 또는 LOW가 됩니다. 여기서는 풀업 구성(INPUT_PULLUP 사용)이므로 버튼을 누르지 않으면 HIGH를 읽습니다. 버튼을 누르면 LOW를 읽습니다.
그런 다음 디지털 Read()의 결과를 Serial.println() 함수에 넣어 직렬 모니터에 데이터를 인쇄합니다.
delay(100);
}
버튼의 상태를 읽고 인쇄한 직후 100밀리 초의 지연()을 추가하여 초당 약 10회 정도 이 동작을 수행하도록 합니다. void 루프()를 종료한 후 다시 호출하는 식으로 코드를 컴파일하고 아두이노에 업로드하여 테스트할 수 있습니다. 이제 직렬 모니터를 열고 푸시 버튼을 여러 번 눌렀다 놓으면 다음과 같은 내용이 표시됩니다:
1
1
1
1
0
0
0
0
1
1
1
1
100밀리 초마다 0 또는 1이 포함된 새 줄이 생깁니다. 앞서 작성했듯이 디지털 Read()에서 얻은 값은 HIGH 또는 LOW이지만 Serial을 통해 전송하면 1(HIGH) 또는 0(LOW)으로 변환됩니다. 따라서 버튼을 누르지 않으면 "1"이 인쇄되고 버튼을 누르면 "0"이 표시됩니다. 개선되었습니다:
보다 명확한 메시지 인쇄
이 첫 번째 코드는 푸시 버튼에서 나오는 데이터를 확인하기 위한 최소한의 예시로 좋습니다. 하지만 실제 애플리케이션에서는 단순히 버튼의 상태를 인쇄하는 것보다 더 흥미로운 작업을 하고 싶을 수 있습니다. 예를 들어 버튼의 상태를 읽고 변수 안에 보관한 다음 값에 따라 특정 작업을 수행할 수 있습니다. 다음 Arduino 코드를 사용해 보겠습니다.
#define BUTTON_PIN 4
void setup()
{
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop()
{
byte buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW) {
Serial.println("Button is pressed");
}
else {
Serial.println("Button is not pressed");
}
delay(100);
}
설정 단계는 동일합니다. loop() 함수에서 볼 수 있듯이, 이제 디지털Read()에서 얻은 값을 변수에 저장하고 버튼스테이트(buttonState)라는 이름을 지정합니다. 이 변수의 데이터 유형은 바이트이며, 값 HIGH와 LOW에 필요합니다.
그런 다음 간단한 if 구조를 사용하여 상태가 LOW인지, 즉 내부 Arduino 풀업 저항을 사용한 구성에서 버튼이 눌렀는지 확인합니다. 이를 감지하면 원하는 작업을 수행하기로 결정할 수 있습니다. 조건이 거짓인 경우, 즉 상태가 HIGH(가능한 상태가 2개뿐이므로)인 경우 버튼이 눌리지 않았음을 알 수 있습니다.
이제 이 코드를 실행하면 100밀리초마다 "버튼이 눌리지 않았습니다"가 인쇄되고, 푸시 버튼을 누르면 "버튼이 눌렸습니다"가 인쇄되는 것을 확인할 수 있습니다.
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is pressed
Button is pressed
Button is pressed
Button is not pressed
Button is not pressed
이것은 이전 코드를 개선한 것으로, 더 많은 콘텍스트를 제공하고 코드를 더 많은 기능으로 확장할 수 있습니다.
풀업/다운 저항기 사용
지금은 회로에 저항기를 사용하지 않았지만 실제로는 내부 아두이노 저항기를 사용했습니다. 기본적으로 푸시 버튼을 디지털 핀에 연결하면 아두이노가 읽는 값은 0V와 5V 사이입니다. 값이 0V에 가까우면 코드에서 LOW 값이 나오고 5V에 가까우면 HIGH 값이 나옵니다. 저항을 넣지 않으면 값이 0V와 5V 사이에서 "유동적"이므로 무작위적이고 이상한 결과가 나올 수 있습니다. 저항을 추가하면 기본 상태를 LOW 또는 HIGH로 강제 설정할 수 있습니다. 버튼을 누르면 상태가 반대가 됩니다.
여기에는 3가지 옵션이 있습니다.
- 저항을 추가하지 않고 INPUT_PULLUP 모드를 사용하여 내부 풀업 저항을 활성화합니다(기본 판독 전압을 5V - HIGH로 강제 설정).
- 풀업 저항을 추가합니다(기본값: HIGH).
- 풀다운 저항을 추가합니다(기본값: LOW).
풀업/다운 저항에 대한 자세한 내용은 이 INPUT_PULLUP 튜토리얼을 참조하세요. 이미 옵션 1을 사용했으니 이제 옵션 2와 3으로 작업하는 방법을 살펴보겠습니다.
저항 없이 회로를 만들 수 있는데 왜 옵션 2와 3을 고려해야 할까요? 내부 풀업 저항은 회로에 수동으로 추가할 저항에 비해 매우 약하기 때문입니다. 예를 들어 전선이 긴 경우 불안정한 결과를 초래할 수 있습니다.
외부 풀업 저항이 있는 아두이노 푸시 버튼
이 회로에는 10k Ohm 저항과 이전에 사용한 다른 모든 부품이 필요합니다.
- 회로를 수정하기 전에 하드웨어에 대한 위험을 피하기 위해 아두이노의 전원을 꺼야 합니다.
- 접지선(검은색)과 데이터선(파란색)은 이전과 동일하게 연결합니다. 버튼의 다른 면에 있는지 확인합니다(예: 왼쪽은 GND, 오른쪽은 디지털 핀).
- 디지털 핀 연결과 같은 면(이 예에서는 오른쪽)에 10k옴 저항을 추가할 것입니다. 저항의 다른 쪽 다리는 브레드보드의 다른 라인으로 연결되며, 이 다리에서 아두이노의 5V 핀에 전선(빨간색)을 추가합니다. 이것이 풀업 저항으로, 코드에서 읽은 기본 전압이 5V이므로 코드에서 HIGH가 되도록 합니다.
이제 코드는 다음과 같습니다.
#define BUTTON_PIN 4
void setup()
{
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT);
}
void loop()
{
byte buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW) {
Serial.println("Button is pressed");
}
else {
Serial.println("Button is not pressed");
}
delay(100);
}
보시다시피 코드는 동일하며 핀모드() 함수에서 모드만 수정했습니다. 핀을 INPUT으로 설정하고 내부 풀업 저항을 활성화하는 INPUT_PULLUP을 사용하는 대신 INPUT만 사용합니다.
외부 풀다운 저항이 있는 아두이노
푸시 버튼 이 회로에서는 10k Ohm 저항도 사용합니다.
원리는 동일하지만 여기서는 약간 다릅니다.
- 다시 한 번, 작업을 수행하기 전에 아두이노의 전원을 꺼야 합니다.
- 접지에 연결된 쪽에 10k 옴 저항을 추가할 것입니다. 따라서 버튼의 한쪽 다리부터 저항이 있고 그다음에는 GND에 연결된 와이어(검은색)가 있습니다.
- 이제 디지털 핀에 연결된 와이어는 저항과 같은 쪽에 있어야 합니다(이 예에서는 GND와 디지털 핀 연결이 모두 왼쪽에 있습니다).
- 버튼의 한쪽 다리(GND 반대쪽, 즉 여기서는 오른쪽)와 5V 사이에 와이어(빨간색)를 추가합니다.
그리고 여기에 코드가 있습니다.
#define BUTTON_PIN 4
void setup()
{
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT);
}
void loop()
{
byte buttonState = digitalRead(BUTTON_PIN);
if (buttonState == HIGH) {
Serial.println("Button is pressed");
}
else {
Serial.println("Button is not pressed");
}
delay(100);
}
보시다시피 핀모드() 함수에서도 입력 모드를 사용합니다.
하지만 이제 푸시 버튼에서 상태를 읽을 때 LOW는 버튼이 눌리지 않았음을 의미합니다. 따라서 코드에서 데이터를 분석하는 방식을 변경해야 합니다. 13줄에서 볼 수 있듯이 상태가 HIGH이면 이제 버튼이 눌렸다는 뜻입니다.
상태 변경 감지
좋아요, 이제 버튼의 상태를 읽고 버튼이 눌렸는지 여부에 따라 코드에서 다른 작업을 수행할 수 있습니다. 하지만 상태 자체가 아니라 상태의 변화를 감지하고 싶다면 어떻게 해야 할까요? 예를 들어 버튼의 상태가 '안 눌림'에서 '눌림'으로 바뀌거나(누름), 그 반대(놓음)로 바뀌는 경우입니다. 애플리케이션에 따라 매우 유용할 수 있습니다.
이를 위해 인터럽트와 폴링이라는 두 가지 방법을 사용할 수 있습니다. 이 두 가지 방법에 대해 여기서는 기본적인 이해를 돕기 위한 코드를 소개하지만 더 자세히 알고 싶다면 이 튜토리얼의 결론 섹션에 다른 심층 튜토리얼 링크를 넣었습니다.
푸시 버튼으로 인터럽트 사용하기
인터럽트를 사용하면 기본적으로 디지털 핀에서 특정 변경이 발생할 때 호출할 함수를 만들 수 있습니다. 이 함수를 호출하면 프로그램의 정상적인 흐름이 "중단"됩니다. 인터럽트에는 특정 디지털 핀만 사용할 수 있습니다. 아두이노 우노에서는 핀 2와 3을 사용할 수 있습니다. 현재 버튼이 4번 핀에 연결되어 있으므로 회로를 수정해야 합니다.
다음은 외부 풀다운 저항이 있는 회로이지만 이번에는 데이터 와이어가 디지털 3번 핀에 연결되어 있습니다.
이제 버튼이 해제되는 시점을 감지하는 코드입니다:
#define BUTTON_PIN 3
volatile byte buttonReleased = false;
void buttonReleasedInterrupt() {
buttonReleased = true;
}
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN),
buttonReleasedInterrupt,
FALLING);
}
void loop() {
if (buttonReleased) {
buttonReleased = false;
// do something here, for example print on Serial
Serial.println("Button released");
}
}
버튼을 초기화하는 방법은 이전과 동일합니다.
그런 다음 buttonReleasedInterrupt()라는 새 함수를 만듭니다. 이 함수는 인터럽트가 트리거될 때 호출되며, 버튼의 상태가 HIGH에서 LOW로 바뀌는 FALLING 모드에서 호출됩니다.
따라서 버튼이 해제되면(상태가 HIGH에서 LOW로 바뀌면) 이 함수가 호출됩니다. 부울 값(인터럽트 내에서 수정하기 때문에 "휘발성"으로 표시됨)을 true로 설정하고 void 루프()에서 해당 부울을 확인합니다. 참이면 버튼이 해제되었음을 알 수 있습니다. 부울을 다시 false로 설정하고 원하는 작업을 수행합니다(예: 직렬 모니터에 "버튼 해제됨"을 인쇄할 수 있습니다).
중요 참고: 인터럽트 함수 내에서 직렬을 직접 사용하지 마십시오. 더 자세한 설명을 원하시면 인터럽트를 올바르게 사용하기 위한 모든 해야 할 일과 하지 말아야 할 일을 알려주는 Arduino 인터럽트에 대한 전체 자습서도 작성했습니다.
폴링을 통해 상태 변경 감지
버튼이 해제되었는지 확인하는 다른 방법을 사용해 보겠습니다.
#define BUTTON_PIN 3
byte lastButtonState = LOW;
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT);
}
void loop() {
byte buttonState = digitalRead(BUTTON_PIN);
if (buttonState != lastButtonState) {
lastButtonState = buttonState;
if (buttonState == LOW) {
// do an action, for example print on Serial
Serial.println("Button released");
}
}
}
상태를 지속적으로 폴링하여 버튼이 해제되었는지 확인해 보도록 하죠.
여기서도 이전과 같은 방식으로 버튼을 초기화합니다.
또한 마지막 버튼 상태라는 전역 변수를 유지하여 현재 읽은 버튼의 상태를 이전 버튼과 비교할 수 있습니다. void loop()에서는 버튼의 상태를 지속적으로 확인합니다. 상태가 이전 상태와 다르면 방금 버튼을 눌렀거나 방금 버튼을 눌렀을 때 어떤 일이 발생했음을 의미합니다. 두 경우 모두 현재 상태를 마지막 상태로 저장합니다.
그런 다음 상태가 LOW인지 확인합니다. 풀다운 저항 구성에서 그렇다면 버튼이 해제되었음을 의미합니다. 풀업 구성을 사용하는 경우에는 반대로 상태가 HIGH인지 확인해야 합니다.
버튼 디바운스
위 코드의 문제점은 실행할 때 버튼을 한 번만 놓아도 "버튼이 릴리스 되었습니다"가 여러 번 표시되는 경우가 있다는 것입니다.
이는 공을 바닥에 떨어뜨렸을 때처럼 버튼을 터치할 때 물리적으로 "튀기"(짧은 간격으로 LOW에서 HIGH, HIGH에서 LOW로 여러 번 이동) 때문인데요, 이는 버튼이 바닥에 떨어질 때처럼 버튼이 튕기는 현상입니다. 공이 바닥에 닿은 후 그대로 머무르는 것이 아니라 몇 번 튕기다가 안정화됩니다. 푸시 버튼도 이와 비슷합니다.
다행히도 코드에서 이 문제를 해결할 수 있습니다.
#define BUTTON_PIN 3
byte lastButtonState = LOW;
unsigned long debounceDuration = 50; // millis
unsigned long lastTimeButtonStateChanged = 0;
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT);
}
void loop() {
if (millis() - lastTimeButtonStateChanged > debounceDuration) {
byte buttonState = digitalRead(BUTTON_PIN);
if (buttonState != lastButtonState) {
lastTimeButtonStateChanged = millis();
lastButtonState = buttonState;
if (buttonState == LOW) {
// do an action, for example print on Serial
Serial.println("Button released");
}
}
}
}
여기서 우리가 하는 일은 기본적으로 상태가 변경된 후 주어진 시간 동안 푸시 버튼의 모든 결과를 무시하는 것입니다. 이렇게 하면 버튼이 물리적으로 바운스 될 때 상태를 읽지 않아도 됩니다.
이를 위해 버튼의 상태를 읽기 전에 마지막으로 상태가 변경된 후의 지속 시간이 설정한 debounceDuration(여기서는 50밀리초)보다 큰지 확인합니다. 그렇다면 버튼의 상태를 읽습니다. 그런 다음 상태가 마지막 상태와 다른 것을 발견하면 millis()를 사용하여 lastTimeButtonStateChanged 변수를 현재 시간으로 재설정합니다. 이렇게 하면 다음번에는 최소 50밀리 초를 다시 기다려야 합니다.
디바운스 메커니즘에 대한 자세한 설명은 이 동영상을 시청하세요:
결론 - 아두이노 푸시 버튼
이 아두이노 푸시 버튼 튜토리얼에서는 아두이노 보드에 연결된 푸시 버튼으로 회로를 올바르게 만들고, 버튼의 상태를 읽고, 이 상태 또는 상태 변경을 다양한 사용 사례에 사용하는 방법을 배웠습니다.
더 나아가서 푸시 버튼으로 LED를 켜고 끄는 방법에 대한 이 자습서를 확인하는 것이 좋습니다. 이 튜토리얼에서는 LED를 사용하여 다양한 간단한 애플리케이션에 푸시 버튼을 포함하는 방법을 살펴봅니다. 또한 디바운스 메커니즘에 대한 훨씬 더 완벽한 설명도 얻을 수 있습니다.
참고:
'아두이노우노 R4' 카테고리의 다른 글
아두이노 우노 R4 조이스틱 3D Model Processing 코드 (0) | 2024.08.20 |
---|---|
아두이노 우노 R4 Minima 4x4 16key 멤브레인 매트릭스 키패드 스위치 HAM2913 (1) | 2024.08.16 |
TTP223B 터치 센서와 아두이노를 연동하는 방법 (1) | 2024.08.12 |
아두이노 나노 33 IoT 기반 NTP 세계 시계 사용 (1) | 2024.08.07 |
Nano 33 IoT BLE Scanner 코드 (1) | 2024.08.05 |
아두이노 나노, PM2008 미세먼지 SSD1306 Oled Display (1) | 2024.07.29 |
i2c 충돌나면 풀업저항 다는 게 제일 먼저 할 일 (1) | 2024.07.28 |
DS1302 아두이노 RTC 모듈 사용법 (2) | 2024.07.12 |
더욱 좋은 정보를 제공하겠습니다.~ ^^