Arduino IDE에서 ESP32 듀얼 코어를 사용하는 방법
ESP32에는 Xtensa 32비트 LX6 마이크로프로세서 두 개(코어 0과 코어 1)가 탑재되어 있습니다. 즉, 듀얼 코어입니다. Arduino IDE에서 코드를 실행하면 기본적으로 코어 1에서 실행됩니다. 이 튜토리얼에서는 FreeRTOS 작업을 생성하여 ESP32의 두 번째 코어에서 코드를 실행하는 방법을 보여줍니다. 두 코어에서 동시에 코드를 실행하여 ESP32를 멀티태스킹으로 만들 수 있습니다.

ESP32 FreeRTOS 듀얼 코어 튜토리얼 Arduino IDE 코어 사용 방법
참고: 멀티태스킹을 위해 반드시 듀얼 코어를 실행할 필요는 없습니다.
ESP32 듀얼 코어 – 소개
ESP32에는 2개의 Xtensa 32비트 LX6 마이크로프로세서(코어 0, 코어 1)가 탑재되어 있습니다.*

ESP32 듀얼 코어 - 소개
* 일부 ESP32 모델은 듀얼 코어가 아닙니다. 이 튜토리얼을 따라하기 전에 사용 중인 ESP32 보드를 확인하세요. 듀얼 코어가 아닌 가장 인기 있는 모델은 다음과 같습니다 .
ESP32-S2 기반 :
-ESP32-S2-Saola-1(Espressif 개발 보드)
-ESP32-S2-Kaluga-1(디스플레이/카메라 지원이 있는 멀티미디어 개발 키트)
ESP32-C3 기반 :
-ESP32-C3-DevKitM-1(Espressif 미니 개발 키트)
-ESP32-C3-룸-0
-Seeed Studio XIAO ESP32-C3
-웨이브셰어 ESP32-C3-Zero
ESP32-C6 기반 :
-ESP32-C6-DevKitC-1(Espressif 개발 보드)
-Seeed Studio XIAO ESP32-C6
ESP32-H2 기반 :
-ESP32-H2-DevKitM-1
기본적으로 ESP32에서 코드를 실행하면 코어 1에서 실행됩니다. Arduino IDE를 사용하여 ESP32에 코드를 업로드하면 코드가 그냥 실행되므로 어떤 코어에서 코드를 실행할지 걱정할 필요가 없습니다.
코드가 어떤 코어에서 실행되고 있는지 반환하는 함수가 있습니다.
xPortGetCoreID( )
해당 함수를 아두이노 스케치에서 사용하면 setup()과 loop() 모두 코어 1에서 실행되는 것을 확인할 수 있습니다. 다음 스케치를 ESP32에 업로드하여 직접 테스트해 보세요.
/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
void setup() {
Serial.begin(115200);
Serial.print("setup() running on core ");
Serial.println(xPortGetCoreID());
}
void loop() {
Serial.print("loop() running on core ");
Serial.println(xPortGetCoreID());
}
115200의 전송 속도로 직렬 모니터를 열고 Arduino 스케치가 실행 중인 코어를 확인합니다.

코드를 실행하는 코어를 식별하는 ESP32 - 직렬 모니터
작업 만들기
Arduino IDE는 ESP32용 실시간 운영체제인 FreeRTOS를 지원합니다. 이를 통해 독립적으로 실행되는 여러 작업을 병렬로 처리할 수 있습니다.
작업은 무언가를 실행하는 코드 조각입니다. 예를 들어 LED 깜박임, 네트워크 요청, 센서 측정값 측정, 센서 측정값 게시 등이 있습니다.
코드의 특정 부분을 특정 코어에 할당하려면 작업을 생성해야 합니다. 작업을 생성할 때 실행할 코어와 우선순위를 선택할 수 있습니다. 우선순위 값은 0부터 시작하며, 0은 가장 낮은 우선순위입니다. 프로세서는 우선순위가 높은 작업을 먼저 실행합니다. 작업을 생성하려면 다음 단계를 따르세요.
작업을 생성하려면 다음 단계를 따라야 합니다.
1. 작업 핸들을 만듭니다. Task1의 예는 다음과 같습니다.
TaskHandle_t Task1;
2. setup() 함수 내에서 xTaskCreatePinnedToCore 함수를 사용하여 특정 코어에 할당된 태스크를 생성하십시오. 이 함수는 우선순위와 태스크가 실행될 코어(마지막 매개변수)를 포함한 여러 매개변수를 받습니다.
xTaskCreatePinnedToCore(
Task1code, /* Function to implement the task */
"Task1", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task */
&Task1, /* Task handle. */
0); /* Core where the task should run */
3. 작업 생성 후에는 생성된 작업의 코드를 포함하는 함수를 만들어야 합니다. 이 예시에서는 Task1code() 함수를 생성해야 합니다. 작업 함수는 다음과 같습니다:
Void Task1code( void * parameter) {
for(;;) {
Code for task 1 - infinite loop
(...)
}
}
for(;;)은 무한 루프를 생성합니다. 따라서 이 함수는 loop() 함수와 유사하게 동작합니다. 예를 들어 코드에서 두 번째 루프로 사용할 수 있습니다.
코드 실행 중에 생성된 작업을 삭제하려면 작업 핸들(Task1)을 인수로 받는 vTaskDelete() 함수를 사용할 수 있습니다:
vTaskDelete(Task1);
ESP32에 대한 FreeRTOS 시작 가이드를 읽어 작업 처리 방법에 대한 보다 자세한 튜토리얼을 읽어보세요 .
간단한 예를 통해 이러한 개념이 어떻게 작동하는지 살펴보겠습니다.
다른 코어에서 작업 만들기 – 예
이 예를 따르려면 다음 부품이 필요합니다.
ESP32 DOIT DEVKIT V1 보드 또는 듀얼 코어가 있는 기타 ESP32 보드 모델
2x 5mm LED
2x 330옴 저항기
브레드보드
점퍼 와이어
서로 다른 코어에서 실행되는 여러 작업을 생성하기 위해, 서로 다른 지연 시간으로 LED를 깜박이는 두 개의 작업을 생성합니다. 다음 다이어그램과 같이 두 개의 LED를 ESP32에 연결합니다.

다른 코어에서 작업 생성 - 듀얼 코어 예시
서로 다른 코어에서 실행되는 두 가지 작업을 생성해보겠습니다.
- Task1은 코어 0에서 실행됩니다.
- Task2는 코어 1에서 실행됩니다.

ESP32 듀얼 코어 0 및 코어 1 실행 작업
다음 스케치를 ESP32에 업로드하여 다른 코어의 각 LED를 깜박이게 하세요.
/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
TaskHandle_t Task1;
TaskHandle_t Task2;
// LED pins
const int led1 = 2;
const int led2 = 4;
void setup() {
Serial.begin(115200);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
}
//Task1code: blinks an LED every 1000 ms
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led1, HIGH);
delay(1000);
digitalWrite(led1, LOW);
delay(1000);
}
}
//Task2code: blinks an LED every 700 ms
void Task2code( void * pvParameters ){
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led2, HIGH);
delay(700);
digitalWrite(led2, LOW);
delay(700);
}
}
void loop() {
}
코드는 어떻게 작동하나요?
참고: 코드에서 두 개의 태스크를 생성하고, 하나는 코어 0에, 다른 하나는 코어 1에 할당합니다. 아두이노 스케치는 기본적으로 코어 1에서 실행됩니다. 따라서 Task2의 코드는 loop() 안에 작성할 수 있습니다(별도의 태스크를 생성할 필요가 없었습니다). 여기서는 학습 목적으로 두 개의 서로 다른 태스크를 생성했습니다.
하지만 프로젝트 요구 사항에 따라 이 예제에서 보여준 것처럼 코드를 작업별로 구성하는 것이 더 실용적일 수 있습니다.
코드는 Task1과 Task2라는 작업 핸들을 생성하는 것으로 시작합니다.
TaskHandle_t Task1;
TaskHandle_t Task2;
LED에 GPIO 2와 GPIO 4를 할당합니다.
const int led1 = 2;
const int led2 = 4;
setup() 함수에서 시리얼 모니터를 115200 보 속도로 초기화하십시오:
Serial.begin(115200);
LED를 출력으로 선언합니다.
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
그런 다음 xTaskCreatePinnedToCore() 함수를 사용하여 Task1을 만듭니다.
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
Task1은 Task1code() 함수로 구현됩니다. 따라서 코드 후반부에 해당 함수를 생성해야 합니다. 작업 우선순위를 1로 지정하고 코어 0에 고정합니다.
동일한 방법을 사용하여 Task2를 생성했지만, 이를 코어 1에 할당했습니다.
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
작업을 만든 후에는 해당 작업을 실행할 함수를 만들어야 합니다.
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led1, HIGH);
delay(1000);
digitalWrite(led1, LOW);
delay(1000);
}
}
Task1의 함수는 Task1code()라고 합니다(원하는 대로 이름을 지정할 수 있습니다). 디버깅을 위해 먼저 작업이 실행 중인 코어를 출력합니다:
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
그러면 우리는 다음과 유사한 무한 루프를 갖게 됩니다.고리()아두이노 스케치에서 루프를 돌면서 LED1을 1초마다 깜빡입니다.
Task2에서도 동일한 일이 발생하지만, LED를 다른 지연 시간으로 깜박입니다.
void Task2code( void * pvParameters ){
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led2, HIGH);
delay(700);
digitalWrite(led2, LOW);
delay(700);
}
}
마지막으로, loop() 함수는 비어 있습니다:
void loop() { }
참고: 앞서 언급한 바와 같이, Arduino의 loop() 함수는 코어 1에서 실행됩니다. 따라서 코어 1에서 실행할 태스크를 생성하는 대신, 코드를 loop() 함수 내에 작성할 수 있습니다. 그러나 FreeRTOS 태스크로 코드를 구성하는 것이 더 실용적일 수 있습니다.
데모
ESP32에 코드를 업로드하세요. 올바른 보드와 COM 포트를 선택했는지 확인하세요.
115200의 전송 속도로 직렬 모니터를 엽니다. 다음과 같은 메시지가 표시됩니다.

ESP32 듀얼 코어 - 각 작업이 실행되는 위치를 직렬 모니터에 인쇄
예상대로 Task1은 코어 0에서 실행되고, Task2는 코어 1에서 실행됩니다.
회로에서 하나의 LED는 1초마다 깜박여야 하고, 다른 LED는 700밀리초마다 깜박여야 합니다.

ESP32에서 두 개의 LED가 서로 다른 속도로 깜박임
마무리하기
요약하자면, 이 튜토리얼에서 여러분은 다음 내용을 배웠습니다.
- 대부분의 ESP32 모델은 듀얼 코어입니다.
- Arduino 스케치는 기본적으로 코어 1에서 실행됩니다.
- 코어 0을 사용하려면 FreeRTOS 작업을 만들어야 합니다.
- xTaskCreatePinnedToCore() 함수를 사용하여 특정 작업을 특정 코어에 고정할 수 있습니다.
- 이 방법을 사용하면 두 개의 코어를 사용하여 두 가지 다른 작업을 독립적으로 동시에 실행할 수 있습니다.
이 튜토리얼에서는 LED를 사용한 간단한 예제를 제공했습니다. 이 방법을 실제 응용 프로그램을 사용하는 고급 프로젝트에 적용하는 것이 목표입니다. 예를 들어, 하나의 코어를 사용하여 센서 값을 측정하고 다른 코어를 사용하여 홈 자동화 시스템에 해당 값을 게시하는 것이 유용할 수 있습니다.
'ESP32' 카테고리의 다른 글
| GPS 데이터 지도에 그려주는 사이트 (0) | 2025.11.29 |
|---|---|
| ESP32 NEO-6M GPS 모듈 문제 해결 가이드 (0) | 2025.11.28 |
| ESP32 NEO-6M GPS 모듈 인터페이스 (0) | 2025.11.28 |
| ESP-32 LVGL 그래픽을 사용한 고급 기술 - 5부 (0) | 2025.11.27 |
| ESP-32 LVGL 그래픽을 사용한 고급 기술 - 4부 (0) | 2025.11.27 |
| ESP-32 LVGL 그래픽을 사용한 고급 기술 - 3부 (0) | 2025.11.26 |
| ESP-32 LVGL 그래픽을 사용한 고급 기술 - 2부 (0) | 2025.11.26 |
| ESP-32 LVGL 그래픽을 사용한 고급 기술 - 1부 (1) | 2025.11.24 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
캐어랩