자바스크립트(JavaScript)로 배우는 피지컬 컴퓨팅(Physical Computing) — (3/8) 스위치 상태 읽기
이번 강좌에서는 디지털 입력(Digital Input)을 통해 스위치를 눌렀는지 아닌지의 상태를 읽어보도록 하자.
준비물
- 라즈베리파이 피코 — 1개
- 브레드보드(Breadboard) — 1개
- 택타일 스위치(Tactile Switch)— 1개
- 점퍼 와이어 — 여러개
택타일 스위치(Tactile Switch)는 버튼을 누르면 회로가 연결이 되는 매우 단순한 스위치이다. 우리 주변에서 많은 전자제품에서 이러한 스위치를 쉽게 찾아볼 수 있다. 택타일 스위치는 보통 4개의 핀이 있는데, 왼쪽 위아래 핀이 서로 연결되어 있고, 오른쪽 위아래 핀이 서로 연결되어 있다. 따라서 회로에 연결할 때에는 왼쪽 하나(위, 아래 아무거나), 오른쪽 하나(위, 아래 아무거나)를 사용해야 한다.
디지털 입력(Digital Input)
GPIO를 지원하는 핀(Pin)들은 모두 디지털 출력에 사용할 수도 있지만, 디지털 입력에 사용 할 수도 있다. 이것을 위해서 먼저 간단한 실험을 해보자. 먼저, GPIO14를 3V3에 점퍼와이어로 직접 연결해보자. 그리고 GPIO14를 입력 모드로 바꾸고 그 값을 읽어보자.
> pinMode(14, INPUT);
> digitalRead(14);
그러면 터미널에 1
이 출력될 것이다. 1
은 곧 HIGH
를 의미한다. 반대로 GPIO14를 GND에 연결하고 똑같이 읽어보자. 그러면 0
이 출력될 것이다. 0
은 곧 LOW
를 의미한다.
풀다운(Pull-Down) 모드
이제 아래 그림과 같이 GPIO14와 3V3 사이에 스위치를 연결해보자. 스위치를 누르면 GPIO14와 3V3이 연결된 상태이기 때문에 HIGH
값이 읽어지면 스위치를 눌렀다가 판단해도 될까? 스위치가 눌러졌을 때에는 HIGH
값이 분명하지만 스위치가 눌러지지 않았을 때는 어디에도 연결되어 있지 않아서 사실 허공에 있는 상태인데, 이런 경우에는 어떤 값이 들어올까?
반드시 기억해야 할 것은 스위치가 눌러지지 않으면 GPIO는 HIGH
도 LOW
도 아닌 “알 수 없는” 상태이다. 실제로 노이즈가 발생하는 등의 이유로 때때로 HIGH
값이 읽혀지기도 한다. 그러므로 사용하려고 하는 GPIO 입력핀을 이런 상태에 빠지게 하면 안된다. 그래서 스위치가 눌러지지 않았을 때에는 반드시 강제로 LOW
상태로 두어야 한다. 그것이 바로 풀다운(pull-down) 모드이다 (LOW
로 끌어 내린다는 의미로 이해하면 편하다). 그래서 이와 같은 경우에는 INPUT
모드가 아닌 INPUT_PULLDOWN
으로 설정해야 한다.
> pinMode(14, INPUT_PULLDOWN);
이렇게 해두면 스위치가 눌러지지 않았을 때에는 반드시 LOW
가 되고, 스위치가 눌러졌을 때 HIGH
가 되는 안정적인 상태가 된다.
풀업(Pull-Up) 모드
그렇다면 반대로 스위치가 눌러지지 않았을 때에는 항상 HIGH
상태이고, 스위치가 눌러졌을 때 LOW
상태가 되는 것도 가능한가? 가능하다! 아래와 같이 GPIO14와 GND 사이에 스위치를 연결해보자.
이제는 GPIO14를 풀업(pull-up — HIGH
로 끌어 올린다의 의미로 이해하면 편하다) 모드로 설정하고, 스위치를 눌렀을 때와 누르지 않았을 때 digitalRead()
의 값을 확인해보자. 평상시에는 HIGH
값이 나오다가 스위치를 눌렀을 때에만 LOW
값이 나타날 것이다.
> pinMode(14, INPUT_PULLUP);
원래는 풀업, 풀다운 모두 외부에 저항을 이용해서 회로를 구성해주었지만, 요즘의 마이크로컨트롤러에서는 내부적으로 기능을 제공하기 때문에 별도의 회로 없이 프로그램만으로 사용이 가능하다.
이벤트 발생시키기
자, 이제 스위치를 눌렀을 때, 터미널에 clicked!
라는 메시지를 출력해보자. 가장 먼저 떠오르는 방법은 아래와 같이 무한루프를 이용하는 것일 것이다. 위 코드를 작성하고 Pico에 업로드 해보자.
스위치를 눌렀을 때 clicked!
라는 메시지가 터미널에 출력이 될 것이다. 그런데 문제는 엄청나게 많은 메시지가 출력될 것이다. 루프가 워낙 빠르게 실행되기 때문에 내가 아무리 짧은 시간만 스위치를 누른다고 해도 무수히 많은 메시지가 출력된다. 두번째 문제는 터미널이 먹통이 된다. Pico가 무한루프를 처리하느라 터미널의 입력에 전혀 대응하지 못한다. 실행을 중단하고 싶다면 터미널에서 Ctrl+C 를 눌러서 멈출 수 있다.
스위치를 눌렀을 때 딱 한 번만 메시지를 출력할 수 없을까? 간단하게는 스위치의 이전 상태를 특정 변수에 저장하는 것이다. 즉, 이전 상태는 HIGH
인데, 지금의 상태는 LOW
라면 스위치가 눌러지는 정확히 그 순간을 판단할 수 있다. 하지만 여전히 무한루프 때문에 터미널이 먹통이 된다. 이러한 문제를 쉽게 해결하기 위해서 Kaluma에서는 setWatch()
함수를 제공한다. 이것을 사용하면 아래와 같이 매우 간결하게 문제가 해결되면서도, 터미널이 전혀 먹통이 되지 않는다.
setWatch()
함수는 특정 GPIO 핀을 관찰하면서 지정된 이벤트가 발생했을 때 지정된 콜백(callback) 함수가 호출되도록 하는 기능을 제공한다. 첫번째 인자는 호출될 콜백 함수, 두번째 인자로는 관찰할 GPIO 번호를 넘겨준다. 세 번째 인자는 반응할 이벤트의 종류를 의미하는데 HIGH
상태에서 LOW
상태로의 변화에 반응하고 싶기 때문에 FALLING
이벤트를 사용한다. 풀다운 모드를 사용중이라면 반대로 RISING
이벤트를 사용할 수 있고, 두 가지 모두에 반응하고 싶다면 CHANGE
이벤트를 사용할 수 있다. 그리고 마지막 네 번째는 디바운스(debounce) 시간(10ms)을 주면 된다. 여기서는 이 정도만 다루고 자세한 내용은 공식 문서를 참고하기 바란다.
이제 이전의 강좌 내용을 연결해서 스위치를 누를 때마다 LED를 켜고 끄고 할 수 있는 회로와 코드를 아래 그림과 같이 어렵지 않게 작성할 수 있을 것이다.
이번 강좌에서는 디지털 입력과 풀업, 풀다운 모드에 대해서 알아보았다. 이제 다음 강좌로 넘어가서 아날로드 입출력에 대해서 알아보자.