ESP32와 PIR 모션 센서 사용 (인터럽트 및 타이머)
이 튜토리얼에서는 인터럽트와 타이머를 사용하여 ESP32 와 PIR 모션 센서를 연동하는 방법을 보여줍니다 . 이 예제에서는 ESP32가 타이머를 시작하여 지정된 시간(초) 동안 LED를 켜도록 합니다. 타이머가 카운트다운을 시작하고 종료되면 LED는 자동으로 꺼집니다.
이 예제를 통해 타이머와 인터럽트라는 두 가지 중요한 개념에 대해서도 살펴보겠습니다.
이 튜토리얼을 진행하기 전에 Arduino IDE에 ESP32 애드온 이 설치되어 있어야 합니다 . 이전에 설치하지 않았다면 다음 튜토리얼 중 하나를 사용하여 Arduino IDE 에 ESP32를 설치하십시오 .
ESP32 보드를 아두이노 IDE에 1분 안에 설치하세요
아두이노 IDE 2.0에 ESP32 보드를 설치하는 방법
부품 목록
이 프로젝트를 진행하려면 다음 항목들이 필요합니다.
ESP32 개발 보드
미니 PIR 모션 센서(AM312) 또는 PIR 모션 센서(HC-SR501)
5mm LED
330옴 저항
브레드보드 1개
점퍼선
인터럽트 소개
PIR 모션 센서로 이벤트를 발생시키려면 인터럽트를 사용합니다. 인터럽트는 마이크로컨트롤러 프로그램에서 작업을 자동으로 수행하는 데 유용하며 타이밍 문제를 해결하는 데 도움이 될 수 있습니다.
상위 6개
ESP32 전자책
초보자부터 전문가까지
ESP32 프로젝트
인터럽트를 사용하면 핀의 현재 값을 지속적으로 확인할 필요가 없습니다. 인터럽트는 값의 변화가 감지되면 이벤트(함수 호출)를 발생시킵니다.
아두이노 IDE 에서 인터럽트를 설정하려면 , attachInterrupt()GPIO 핀, 실행할 함수의 이름, 그리고 모드를 인수로 받는 함수를 사용합니다 .
attachInterrupt(digitalPinToInterrupt(GPIO), function, mode);
GPIO 인터럽트
첫 번째 인수는 GPIO 번호입니다. 실제 GPIO를 인터럽트 핀으로 설정하려면 종종 를 사용해야 합니다 digitalPinToInterrupt(GPIO). 예를 들어, 다음을 사용하여 를 GPIO 27인터럽트로 사용할 수 있습니다.
digitalPinToInterrupt(27)
다음 다이어그램에서 빨간색 사각형으로 표시된 모든 핀은 ESP32 보드 에서 인터럽트 핀으로 설정할 수 있습니다 . 이 예에서는 PIR 모션 센서를 GPIO 27인터럽트로 연결하겠습니다 .

인터럽트로 사용되는 핀
실행될 함수
인터럽트가 발생할 때마다 호출될 함수의 이름은 attachInterrupt()함수의 두 번째 인수입니다.
방법
세 번째 인수는 동작 모드 값입니다. 발생 모드에는 5가지 종류가 있습니다.
- LOW: 핀이 LOW일 때마다 인터럽트를 발생시킵니다.
- HIGH: 핀이 HIGH 상태일 때마다 인터럽트를 발생시킵니다.
- 변경: 핀 값이 변경될 때마다, 예를 들어 HIGH에서 LOW로 또는 LOW에서 HIGH로 변경될 때마다 인터럽트를 발생시키도록 설정합니다.
- 하강: 핀이 높은 위치에서 낮은 위치로 이동할 때를 나타냅니다.
- 상승형: 핀이 LOW에서 HIGH로 바뀔 때 작동합니다.
PIR 모션 센서에 연결된 GPIO는 움직임이 감지되면 LOW에서 HIGH로 상태가 바뀌기 때문에 이 예제에서는 RISING 모드를 사용하겠습니다.
타이머
이 예제에서는 타이머도 활용해 보겠습니다. 움직임이 감지되면 LED가 미리 설정된 시간 동안 빨간색으로 켜져 있도록 하고 싶습니다. 이때 , delay()특정 동작을 감지하면 그 시간 동안 다른 작업을 할 수 없게 되므로, 타이머를 사용하는 것이 더 효율적입니다.
delay() 함수
이 delay()함수는 널리 사용되므로 숙지해 두는 것이 좋습니다. 사용법은 매우 간단합니다. 인수로는 정수 하나를 받습니다. 이 숫자는 프로그램이 다음 코드 줄로 넘어가기 전에 기다려야 하는 시간을 밀리초 단위로 나타냅니다.
delay(time in milliseconds);
delay(1000)을 사용하면 프로그램이 해당 줄에서 1초 동안 멈춥니다.
블로킹 함수란 특정 함수 이벤트가 완료될 때까지 프로그램이 다른 작업을 수행하지 못하도록 막는 함수입니다. 여러 작업을 동시에 수행해야 하는 경우에는 블로킹 함수를 delay()사용할 수 없습니다.
대부분의 프로젝트에서는 지연 시간을 사용하는 대신 타이머를 사용하는 것이 좋습니다.
millis() 함수
특정 함수를 사용하면 millis() 프로그램이 처음 시작된 이후 경과된 밀리초 수를 반환할 수 있습니다.
millis()
그 함수가 정확히 왜 유용한가요? 기본적인 수학 계산만으로도 코드 실행을 막지 않고 시간이 얼마나 지났는지 쉽게 확인할 수 있는데요.
millis() 함수를 사용하여 LED를 깜빡이게 하기
다음 코드 조각은 함수를 사용하여 깜빡이는 LEDmillis() 프로젝트를 만드는 방법을 보여줍니다 . 이 함수는 LED를 1000밀리초 동안 켜고 끕니다.
/*********
BokFive
Complete project details at https://ebokify.com
*********/
// constants won't change. Used here to set a pin number :
const int ledPin = 26; // the number of the LED pin
// Variables will change :
int ledState = LOW; // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change :
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
delay() 함수 없이 작동하는 이 깜빡임 스케치를 좀 더 자세히 살펴보겠습니다.(delay 함수 대신 millis() 함수를 사용합니다.)
기본적으로 이 코드는 현재 시간( currentMillis)에서 이전에 기록된 시간( previousMillis)을 뺍니다. 프로그램은 previousMillis변수를 현재 시간으로 업데이트하고, 나머지가 지정된 간격(이 경우 1000밀리초)보다 크면 LED 를 켜고 끕니다.
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
(...)
이 코드 조각은 비차단 방식이기 때문에 첫 번째 if 문장 외부에 있는 코드는 모두 제대로 작동해야 합니다.
이제 함수에 작업을 추가하더라도 LED가 loop() 1초에 한 번씩 깜빡인다는 것을 이해할 수 있을 것입니다 .
이 코드가 어떻게 작동하는지 테스트하려면 ESP32 에 이 코드를 업로드 하고 다음 회로도를 구성하십시오. 초 단위 시간(밀리초)도 수정할 수 있습니다.

참고: ESP32 에 코드를 업로드하는 데 문제가 발생한 경우 ESP32 문제 해결 가이드를 참조하십시오 .
PIR 모션 센서가 내장된 ESP32
이제 인터럽트와 타이머에 대한 기본적인 이해를 하셨으니 프로젝트를 계속 진행해 보겠습니다.
개략도
LED 와 저항을 사용하여 조립하기 쉬운 회로를 만들 겠습니다 . LEDGPIO 26 는 여기에 연결됩니다. 3.3볼트에서 작동하는 미니 AM312 PIR 모션 센서를 사용할 것입니다. 센서 는 여기에 연결됩니다. 다음 회로도를 따라 연결하면 됩니다.GPIO 27

중요한: 본 프로젝트에 사용된 미니 AM312 PIR 모션 센서는 3.3볼트에서 작동합니다. 하지만 HC-SR501 과 같은 다른 PIR 모션 센서를 사용하는 경우 , 해당 센서는 5볼트에서 작동합니다. 이 경우 센서를 3.3볼트에서 작동하도록 개조하거나 Vin 핀을 사용하여 전원을 공급하면 됩니다.
다음 그림은 AM312 PIR 모션 센서의 핀 배열을 보여줍니다.

미니 PIR 핀아웃
코드 업로드
회로도를 참조하여 회로를 연결한 후, 제공된 코드를 아두이노 IDE에 복사하십시오.
코드를 그대로 업로드하거나, 동작 감지 후 LED가 켜지는 시간을 초 단위로 수정할 수 있습니다. 원하는 시간(초)을 얻으려면 timeSeconds변수를 변경하기만 하면 됩니다.
/*********
BokFive
Complete project details at https://ebokify.com
*********/
#define timeSeconds 10
// Set GPIOs for LED and PIR Motion Sensor
const int led = 26;
const int motionSensor = 27;
// Timer: Auxiliary variables
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
boolean motion = false;
// Checks if motion was detected, sets LED HIGH and starts a timer
void IRAM_ATTR detectsMovement() {
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
// PIR Motion Sensor mode INPUT_PULLUP
pinMode(motionSensor, INPUT_PULLUP);
// Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
// Set LED to LOW
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
}
void loop() {
// Current time
now = millis();
if((digitalRead(led) == HIGH) && (motion == false)) {
Serial.println("MOTION DETECTED!!!");
motion = true;
}
// Turn off the LED after the number of seconds defined in the timeSeconds variable
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Motion stopped...");
digitalWrite(led, LOW);
startTimer = false;
motion = false;
}
}
코드 작동 방식
소스 코드를 살펴보세요. 두 개의 GPIO 핀을 변수에 할당하는 것이 motionSensor첫 led번째 단계입니다.
// Set GPIOs for LED and PIR Motion Sensor
const int led = 26;
const int motionSensor = 27;
움직임이 감지될 때 LED를 끄도록 타이머를 설정할 수 있는 변수를 만드세요 .
// Timer: Auxiliar variables
long now = millis();
long lastTrigger = 0;
boolean startTimer = false;
해당 변수는 now현재 시간을 저장합니다. 이 lastTrigger변수는 PIR 센서가 움직임을 감지한 시간을 저장합니다. 움직임이 감지되면 이 startTimer불리언 변수가 타이머를 시작합니다.
setup()
먼저 setup()시리얼 포트를 115200 보드 속도로 초기화하십시오.
Serial.begin(115200);
PIR 모션 센서를 입력 풀업으로 설정하십시오.
pinMode(motionSensor, INPUT_PULLUP);
PIR 센서 핀을 인터럽트로 설정하려면 attachInterrupt() 앞서 설명한 함수를 사용하십시오.
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
움직임을 감지할 핀은 GPIO 27 이며, RISING 모드에서 해당 detectsMovement() 함수를 호출합니다 .
LED 는 출력이며, 초기 상태는 LOW입니다.
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
loop()
해당 loop() 함수는 끊임없이 반복 실행됩니다. 매 반복마다 now 변수는 현재 시간으로 업데이트됩니다.
now = millis();
그 외에는 아무것도 하지 않습니다.
그러나 동작이 감지되면, 이전에 setup()에서 인터럽트를 설정했기 때문에 detectsMovement() 함수가 호출됩니다.
이 detectsMovement() 함수는 시리얼 모니터에 메시지를 출력하고, LED 를 켜고 , startTimer 부울 변수를 true로 설정하고 true , lastTrigger 현재 시간으로 변수를 업데이트합니다.
void IRAM_ATTR detectsMovement() {
Serial.println("MOTION DETECTED!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
메모: IRAM_ATTR은 인터럽트 코드를 RAM에서 실행하는 데 사용됩니다. 그렇지 않으면 코드가 플래시에 저장되는데, 이는 속도가 더 느립니다.
이 단계를 거치면 코드는 다시 원래 loop() 위치로 돌아갑니다.
이번에는 startTimer 변수 값이 참입니다. 따라서 동작이 감지된 시점으로부터 정의된 시간(초)이 지나면 다음 if 문장이 참이 됩니다.
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Motion stopped...");
digitalWrite(led, LOW);
startTimer = false;
}
시리얼 모니터에 " 모션이 중지되었습니다... "라는 메시지가 출력되고, LED가 꺼지며, startTimer 변수가 false로 설정됩니다.
데모
ESP32 보드 에 코드를 업로드하세요 . 올바른 보드와 COM 포트가 선택되었는지 확인하십시오.
시리얼 모니터를 115200bps의 전송 속도로 엽니다.

시리얼 모니터
PIR 센서 앞에 손을 대십시오. 시리얼 모니터에 " 움직임 감지됨!!! "이라는 메시지가 출력되고 LED가 켜집니다. LED는 10초 후에 꺼집니다.

pir ESP32 데모
결론
정리하자면, 인터럽트는 GPIO 값을 지속적으로 읽지 않고도 GPIO 상태의 변화를 감지하는 데 사용됩니다. 인터럽트를 통해 변화가 감지되면 특정 함수가 실행됩니다. 또한, 코드를 중단하지 않고도 미리 정해진 시간이 경과했는지 확인할 수 있는 간단한 타이머 설정 방법도 배웠습니다.
위 튜토리얼의 원문을 참고하시려면 이 링크를 따라가세요.
'ESP32' 카테고리의 다른 글
| GY-291 ADXL345 i2c SPI 가속도 센서 (인터럽트 기능) - ESP32, ESP8266, STM32 및 아두이노 호환 (1) | 2026.02.17 |
|---|---|
| ESP32 MPU-6050 웹 서버 3D 객체 표현 (0) | 2026.02.14 |
| ESP32-P4 vs. C3: 어떤 Elecrow HMI 디스플레이가 당신에게 더 적합할까요? (0) | 2026.02.09 |
| ESP32 웹 업데이터: 브라우저를 통해 코드 업데이트(OTA) (0) | 2026.02.03 |
| Wit.ai를 사용하여 ESP32 텍스트 음성 변환 (0) | 2026.01.30 |
| ESP32 mDNS: 사용자 지정 호스트 이름으로 ESP32에 액세스 (0) | 2026.01.28 |
| TinyML-CAM ESP32에서 80FPS 이미지 인식 (1) | 2026.01.17 |
| YOLOX Nano를 사용하는 ESP32 HMI에서 실시간 인원 계수 (1) | 2026.01.15 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
캐어랩