ESP32 Timer(타이머) 설정 방법 (아두이노 코딩)
인터넷에 떠도는 ESP32 타이머 예제 코드를 실행하다가 최근에 함수가 변경되었음을 알게 되었다. 칩 제조사에서 이 부분을 상세하게 기록해놓았다. 파이썬에서 타이머를 아주 쉽게 사용하다가, 다소 까다로워 보이지만 차근차근 이해하면 재미있는 ESP32 타이머 설정 방법이다.
제조사 설명: https://docs.espressif.com/projects/arduino-esp32/en/latest/api/timer.html
Timer - - — Arduino ESP32 latest documentation
© Copyright 2016 - 2024, Espressif Systems (Shanghai) Co., Ltd.
docs.espressif.com
아래 코드는 LED를 2번 핀에 부착하고 0.2초마다 타이머(알람)를 켜서 깜빡이도록 한 코드다. 함수의 매개변수가 줄었거나, 함수 두 개를 하나로 합친 부분이 눈에 띤다. timerBegin함수의 인자가 3개에서 한 개로 줄었고, 여타 두 개의 함수가 timerAlarm 함수 하나로 합쳐졌다. 타이머 함수뿐만 아니라 ESP32의 최근 라이브러리에서 사라진 함수들이 여럿 있고 인자나 상수값이 바뀌어서 좀 혼란스러운데 구글 검색하면 결국 제조사의 최근 스펙 변경 사항에 좀 설명이 되어 있다. 가령 hallRead(자기장 감지 함수), MAC 어드레스 읽고 변경하는 함수들, 타이머 함수들... 그외에도 변경된 게 많아서, 인터넷의 예전 소스들을 실행시켜보면 실행되지 않는 소스코드들이 많다.
1) 최소한으로 작동하는 타이머
hw_timer_t * timer = NULL;
int ledPin = 2;
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// Set timer frequency to 1Mhz
timer = timerBegin(1000000);
// Attach on Timer function to our timer
timerAttachInterrupt(timer, &onTimer); // 타이머 인터럽트 함수를 등록
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited ount = 0 (fourth parameter)
timerAlarm(timer, 200000, true, 0);
}
void loop() {
// 아무것도 하지 않음
}
void onTimer() {
digitalWrite(ledPin, !digitalRead(ledPin)); // LED 상태를 읽어서 그 반대로 함 (켜져 있으면 꺼짐으로, 꺼져 있으면 켬으로 상태 변경)
}
timerAlarm 함수의 네번째 인자(반복 횟수를 정하는 인자) 값을 변경해도 먹히지 않는 것 같아서, 별도로 count 전역변수를 설정해서 타이머 지정 함수가 실행될 때마다 횟수를 세서, 특정 횟수가 지나면 반복 타이머를 해제하는 코드를 아래에 작성했다.
2) 특정 횟수 동안 작동하는 타이머
hw_timer_t * timer = NULL;
int ledPin = 2;
int count = 0;
int max_count = 60; // 알람 반복 횟수
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// Set timer frequency to 1Mhz
timer = timerBegin(1000000);
// Attach on Timer function to our timer
timerAttachInterrupt(timer, &onTimer); // 타이머 인터럽트 함수를 등록
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter)
timerAlarm(timer, 100000, true, 0);
}
void loop() {
}
void onTimer() {
digitalWrite(ledPin, !digitalRead(ledPin)); // LED 상태를 읽어서 그 반대로 함 (켜져 있으면 꺼짐으로, 꺼져 있으면 켬으로 상태 변경)
count += 1;
if (count > max_count) {
timerEnd(timer);
timer = NULL;
digitalWrite(ledPin, 0);
}
}
3) 특정 시간 동안 작동하는 타이머
hw_timer_t * timer = NULL;
int ledPin = 2; // 이벤트 발생할 시 깜빡여줄 LED
int motionSensor = 21; // 인체 감지 센서
int count = 0;
//int max_count = 60; // 알람 반복 횟수
int howlong = 3; // 타이머를 반복하는 시간(초), 즉 이 시간 뒤에는 타이머를 해제함, 이번에는 횟수로 세지 않고 지속시간으로 계산함
unsigned long startTime = 0; // 이벤트가 발생해서 타이머(알람)가 처음 호출된 시각.. 알람 발생할 때마다 이 시각과의 차이를 구해서 정해진 howlong을 초과하면 알람을 해제함.
unsigned long _now; // 알람 함수 내에서의 현재 시각
// 0.1초마다 타이머를 호출함. 반복적으로... 횟수나 시간은 알람 함수 내에서 체크해서 스스로 알람 해제함.
void onTimer() {
digitalWrite(ledPin, !digitalRead(ledPin)); // LED 상태를 읽어서 그 반대로 함 (켜져 있으면 꺼짐으로, 꺼져 있으면 켬으로 상태 변경)
_now = millis(); // 현재 시각을 1/1000초 단위로 구함
if (count == 0) { //알람 함수가 처음 실행되는 것이라면
startTime = _now; // 현재 시각을 이벤트 발생 및 첫 타이머 호출 시각으로 설정
}
if (_now - startTime > howlong * 1000) { // 현재 시각에서 첫 시작 시각을 빼서 그것이 지정 시간을 지나면, 타이머 해제.. millis는 1/1000초 단위로 셈.
timerEnd(timer);
timer = NULL;
digitalWrite(ledPin, 0);
}
count += 1; // 알람 실행 횟수를 셈
}
void timerStart() {
timer = timerBegin(1000000);
timerAttachInterrupt(timer, &onTimer); // 타이머 인터럽트 함수를 등록
timerAlarm(timer, 100000, true, 0);
}
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT); // 이벤트가 발생할 때 깜빡일 LED 설정
digitalWrite(ledPin, LOW);
timerStart();
}
void loop() {
}
4) 인체 움직임을 감지하면 LED를 20번 빠르게 깜빡이는 코드. 감지 센서에서 인체 움직임을 감지하면 인트럽트를 발생시키고 인트럽트 호출 함수에서 인체 감지 flag 를 켜준다. 그러면 0.2초마다 호출되어 실행되는 타이머 함수에서 그 flag를 확인해서, flag가 true면 20번 빠르게 깜박이고, flag가 false면 LED를 조작하지 않고 꺼둔 채로 놔둔다. 0.2초마다 타이머 함수를 계속 실행하는 것은 자원 낭비이지만, 가장 바람직한 것은 인체 감지 인트럽트에서 타이머를 호출하는 식으로 하면 되는데, 그렇게 코딩했더니 비정상 작동을 하길래, 어쩔 수 없이 타이머를 지속적으로 작동시키게 된 것이다. 한가지 확인한 문제는, SW CPU RESET 코드를 내뿜으며 코어 덤프하면서 esp32 칩이 자꾸 reboot되는데, 아뿔사, 전기 공급에 원인이 있는 듯하다. PC에서 USB 3.0에서 전원을 뽑아주는데도 불구하고, 노트북에서 전원을 뽑아서 연결하니 정상 작동한다(아래 코드의 경우, 겨우 그렇게 정상 작동을 확인했다. USB 데이터/전원 겸용 코드가 아닌 전원 코드만 꼽으면, esp32에 전원이 안 들어오고 작동하지 않는데 이것도 vin으로 해결해줘나 하나 싶다. 인터럽트 함수에서 타이머 함수를 실행하는 것이 안 되는 것이 전기적인 원인인지, 원래 잘 안되는 것인지 확인해볼 필요가 있다. (2025.5월 테스트 추가: 아래 5번 코드도 이상 없이 잘 작동함. esp32 만세...)
#define HOWMANY 20
hw_timer_t * timer = NULL;
int ledPin = 2; // 이벤트 발생할 시 깜빡여줄 LED
int motionSensor = 21; // 인체 감지 센서
int count = 0;
int max_count = HOWMANY; // 알람 반복 횟수
boolean motion = false; // 움직임 감지 여부
void onTimer_LED() {
if (motion == true) {
digitalWrite(ledPin, !digitalRead(ledPin));
count += 1;
if (count > HOWMANY) { //반복 횟수를 지났으면 모두를 초기화함(지난 일은 없었던 걸로)
motion = false;
count = 0;
//timerEnd(timer);
//timer = NULL;
digitalWrite(ledPin, 0);
//Serial.println("Motion stopped...");
}
}
}
void timerStart() {
timer = timerBegin(1000000);
timerAttachInterrupt(timer, &onTimer_LED); // 타이머 인터럽트 함수를 등록
timerAlarm(timer, 200000, true, 0);
}
void onTimer() {
motion = true; // 인체 움직임 감지했음을 알림
//Serial.println("MOTION DETECTED!!!");
}
void setup() {
//Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
pinMode(motionSensor, INPUT_PULLUP);
// 모션 센서가 인체 움직임을 감지했을 때 실행할 함수 설정
attachInterrupt(digitalPinToInterrupt(motionSensor), onTimer, RISING);
// 0.2초마다 호출되는 타이머 함수 설정 및 시작
timerStart();
}
void loop() {
}
5) 인체감지 센서 인트럽트 함수에서 타이머를 호출한 버전. 자꾸 재부팅이 되는 게 아무래도 전원 공급 문제인 듯... 그럭그럭 되는데 인체감지에서 민감도가 어째 좀 떨어진다는 느낌이 듦. 위의 4번 코드가 더욱 잘 작동했던 듯...
(2025.5월 테스트 추가: 아래 5번 코드도 이상 없이 잘 작동함. esp32 만세...)
//인터럽트 함수에서 타이머를 호출하는 코드... 제대로 실행이 되지 않았는데 전원 문제인지 아니면 원래 그런 건지...
hw_timer_t * timer = NULL;
int ledPin = 2;
int motionSensor = 21;
int count = 0;
int max_count = 20;
boolean motion = false;
void onTimer_LED() {
digitalWrite(ledPin, !digitalRead(ledPin));
count += 1;
if (count > max_count) {
count = 0;
timerEnd(timer);
timer = NULL;
digitalWrite(ledPin, 0);
//Serial.println("Motion stopped...");
}
}
void onTimer() {
motion = true;
//Serial.println("MOTION DETECTED!!!");
timer = timerBegin(1000000);
// Attach on Timer function to our timer
timerAttachInterrupt(timer, &onTimer_LED); // 타이머 인터럽트 함수를 등록
timerAlarm(timer, 100000, true, 0);
}
void setup() {
//Serial.begin(115200);
pinMode(ledPin, OUTPUT); // 이벤트가 발생할 때 깜빡일 LED 설정
digitalWrite(ledPin, LOW);
pinMode(motionSensor, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(motionSensor), onTimer, RISING);
}
void loop() {
}
6) 애초의 코드(타이머 없이 인터럽트만 있음). 인터럽트를 공부하던 중에 알게 된 코드를 수정한 것... 인체 감지 센서 + LED 켜기를 인터럽트 형식으로 작성. 여기서 인체 감지하면 LED를 5초간 깜박이게 하는 것은 타이머 기능을 구현해야 할 수 있을 것 같아서 현재 테스트 중...
#define timeSeconds 5
const int led = 2;
const int motionSensor = 21;
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
boolean motion = false;
void IRAM_ATTR detectsMovement() {
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
void setup() {
Serial.begin(115200);
pinMode(motionSensor, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
}
void loop() {
now = millis();
if ((digitalRead(led) == HIGH) && (motion == false)) {
motion = true;
Serial.println("MOTION DETECTED!!!");
}
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Motion stopped...");
digitalWrite(led, LOW);
startTimer = false;
motion = false;
}
}