이 가이드에서는 Arduino IDE를 사용하여 ESP32에서 작업 간 안전하고 효율적인 통신을 위해 FreeRTOS 큐를 사용하는 방법을 알아봅니다. 큐를 사용하면 작업 간에 안전하게 데이터를 교환할 수 있습니다. 큐의 기본 작동 원리를 살펴보고, 세 가지 실제 사례를 통해 작업 간 데이터 전달 방법을 살펴봅니다.

FreeRTOS 대기열을 사용한 ESP32: 작업 간 통신(Arduino IDE)
FreeRTOS를 처음 사용하시나요? 이 튜토리얼부터 시작해 보세요: ESP32 with FreeRTOS (Arduino IDE) - 시작 가이드: 작업 생성
목차
이 튜토리얼에서는 다음 주제를 다루겠습니다.
FreeRTOS 작업 간 통신
FreeRTOS 큐란 무엇인가요?
FreeRTOS 큐 기본 사항
예제 1: 큐를 사용하여 LED 밝기 제어
예제 2: FreeRTOS 대기열 - 세 가지 작업 간 데이터 공유
예제 3: 화면에 센서 데이터 표시(데이터 구조 공유)
FreeRTOS 작업 간 통신
FreeRTOS에는 다양한 목적에 맞게 작업 간 통신과 작업 간 데이터 동기화를 위한 다양한 방법이 있습니다.
- Queue 큐 : 이 튜토리얼에서 다루는 예제에서 볼 수 있듯이, 작업이 센서 판독값, 버튼 상태 또는 기타 데이터와 같은 데이터를 주고받을 수 있도록 합니다. 여기서는 FreeRTOS 큐에 중점을 두겠습니다.
- Semaphore 세마포어는 FreeRTOS에서 작업 조정에 사용되는 신호 전달 도구와 유사합니다. 세마포어는 이벤트 발생 또는 리소스 준비 완료를 나타내는 데 자주 사용됩니다. 세마포어는 데이터를 전달하지 않고, 특정 이벤트 발생 후 동작을 트리거하는 카운트(또는 플래그)만 전달합니다. 이 부분은 이후 튜토리얼에서 다루겠습니다.
- Mutex 뮤텍스 (상호 배제)는 한 번에 하나의 작업만 공유 리소스에 접근할 수 있도록 허용하여 리소스를 보호하는 데 사용됩니다. 이를 통해 동일한 데이터에 접근해야 하는 동시 작업에서 충돌이 발생하는 것을 방지합니다. 큐와 달리 뮤텍스는 데이터 전송보다는 리소스 접근에 중점을 둡니다. 이 부분은 이후 튜토리얼에서 다루겠습니다.
FreeRTOS 큐란 무엇인가요?
FreeRTOS 큐는 멀티태스킹 환경에서 동기화된 통신을 위해 작업 간에 데이터를 보내고 받을 수 있는 스레드 안전 데이터 구조입니다.
이들은 충돌이나 데이터 손실 없이 작업 간에 데이터가 안전하게 전달되도록 보장합니다.
큐는 특히 한 작업이 데이터를 전송하고 다른 작업이 나중에 이를 처리해야 할 때 유용합니다. 예를 들어, 센서 작업이 로깅 작업에 판독값을 전송하거나 처리된 데이터를 화면에 표시하는 경우입니다. 이렇게 하면 각 작업이 다른 작업을 방해하지 않고 자체 속도로 실행될 수 있습니다.
FreeRTOS 큐 기본 사항
ESP32용 Arduino IDE 코드에서 대기열을 만들고 처리하는 데 대한 몇 가지 기본 개념은 다음과 같습니다.
대기열 만들기
사용하다xQueueCreate(크기, 항목_크기)작업을 생성하려면크기대기열에 있을 수 있는 항목 수에 해당합니다.품목_크기대기열의 각 항목에 할당된 힙의 바이트 크기입니다.
큐에 데이터 보내기
대기열에 데이터를 보내려면 다음을 사용하세요.xQueueSend()대기열에 데이터를 추가합니다. 또는 다음을 사용합니다.xQueueSendFromISR()ISR(인터럽트 서비스 루틴)에서 데이터를 보내는 경우.
큐에서 데이터 수신
그만큼xQueueReceive()이 함수는 사용 가능한 경우 큐에서 데이터를 읽습니다.
예제 1: 큐를 사용하여 LED 밝기 제어

LED와 전위차계가 있는 ESP32
이 예제에서는 두 개의 작업을 생성합니다. 한 작업은 큐로 데이터를 전송하고, 다른 작업은 큐에서 데이터를 읽습니다.
전위차계를 사용하여 LED 밝기를 제어해 보겠습니다. 이는 아날로그 읽기 와 PWM 에 대해 배우기 위한 기본적인 예시이며, 아마 이전에도 보셨을 것입니다 . 이 경우, 이 기본적인 예시를 시작점으로 삼아 큐를 설명하겠습니다.
다음 이름으로 두 가지 작업을 생성합니다.
- potTask: 전위차계에서 값을 읽어 큐에 보냅니다.
- LED 밝기 작업: 대기열에서 수신할 값이 있는지 확인하고 그에 따라 LED 밝기를 조정합니다.
필요한 부품
이 프로젝트에는 다음 부품이 필요합니다.
귀하가 선택한 ESP32 보드 - 최고의 ESP32 개발 보드를 읽어보세요
LED
220옴 저항기
전위차계
브레드보드
점퍼 와이어
회로 배선
이 예제에서는 LED와 전위차계를 ESP32 보드에 연결합니다. LED는 GPIO 2에, 전위차계는 GPIO 15에 연결합니다.

ESP32 LED와 전위차계에 연결됨
코드 - LED 밝기 제어(큐 기본 예제)
다음 코드는 두 가지 작업을 생성합니다. 하나는 전위차계에서 값을 읽어 큐에 전송합니다. 다른 하나는 큐에서 값을 읽어 그에 따라 LED 밝기를 제어합니다.
다음 코드를 Arduino IDE에 복사하여 보드에 업로드할 수 있습니다.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-freertos-queues-inter-task-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15
#define LED_PIN 2
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
#define QUEUE_SIZE 5
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf("potTask: Sent pot value %u\n", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
void LEDBrightnessTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
uint16_t brightness = potValue;
ledcWrite(LED_PIN, brightness);
Serial.printf("LEDBrightnessTask: Set brightness %u\n", brightness);
}
}
}
void setup() {
Serial.begin(115200);
// Setup PWM for LED
ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
// Create tasks
xTaskCreatePinnedToCore(
potTask,
"potTask",
3000, // Task stack
NULL,
1,
NULL,
1 // Core 1
);
xTaskCreatePinnedToCore(
LEDBrightnessTask,
"LEDBrightnessTask",
3000,
NULL,
1,
NULL,
1
);
}
void loop() {
// Empty
}
코드는 어떻게 작동하나요?
코드가 어떻게 작동하는지 살펴보겠습니다.
LED 및 전위차계 GPIO 정의
먼저 LED와 전위차계의 GPIO 핀을 정의합니다.
#define POT_PIN 15
#define LED_PIN 2
PWM 속성
그런 다음 LED 밝기를 조정하기 위해 PWM 주파수와 해상도를 정의합니다. 12비트 해상도(아날로그 판독값의 기본 해상도와 동일)를 설정합니다.
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
ESP32를 사용한 PWM을 처음 접한다면 이 튜토리얼을 확인하여 자세히 알아보세요: Arduino IDE를 사용한 ESP32 PWM(아날로그 출력)
FreeRTOS 대기열 크기
FreeRTOS 대기열의 크기를 정의합니다. 대기열에 저장할 수 있는 항목의 개수입니다. 이 경우에는 5로 설정합니다.
#define QUEUE_SIZE 5
우리의 경우, 이는 좋은 추정치입니다. 대기열에 전송된 항목은 다른 작업에 의해 거의 즉시 소모되기 때문입니다.
큐 핸들
큐에 대한 핸들을 생성합니다. 코드 전체에서 이 핸들이 큐를 참조하는 방식입니다. 큐를 다음과 같이 참조합니다.팟큐.
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
potTask 함수
포텐셔미터 작업을 위한 함수 potTask를 생성합니다. 이 함수는 포텐셔미터로부터 값을 읽어오는 작업입니다.
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf("potTask: Sent pot value %u\n", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
먼저 포텐셔미터의 값을 읽어 uint16_t 형식의 변수 potValue에 저장합니다.
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
ESP32를 사용하여 아날로그 값을 읽는 방법에 대해 자세히 알아보려면 이 튜토리얼을 확인하세요: ESP32 ADC – Arduino IDE를 사용하여 아날로그 값 읽기
대기열에 항목 보내기
그런 다음, 우리는 다음을 사용합니다.xQueueSend()대기열에 항목을 보내는 함수입니다.팟밸류에게팟큐다음과 같습니다.
Serial.printf("potTask: Sent pot value %u\n", potValue);
마지막 매개변수,포트MAX_DELAY대기열에 새 항목을 위한 공간이 생길 때까지 작업이 무기한 대기함을 의미합니다. 대기하지 않고 즉시 반환하려면 다음을 전달하세요.0대신에.
vTaskDelay
그런 다음 각 판독 사이에 100밀리초를 기다립니다.
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
FreeRTOS 태스크에서는 일반적인 delay() 아두이노 함수 대신 vTaskDelay() 함수를 사용해야 합니다. 이 함수는 대기할 틱 수를 인자로 받습니다. ESP32의 경우, 하나의 틱은 1ms=portTICK_PERIOD_MS에 해당합니다.
LED 밝기 작업
다음 코드 줄은 LED 밝기를 제어하는 작업을 위한 함수를 생성합니다. 이 함수는 LEDBrightnessTask라고 합니다.
void LEDBrightnessTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
uint16_t brightness = potValue;
ledcWrite(LED_PIN, brightness);
Serial.printf("LEDBrightnessTask: Set brightness %u\n", brightness);
}
}
}
우리는 큐에 있는 값들을 받을 potValue라는 로컬 변수를 생성합니다.
uint16_t potValue;
큐에서 값 가져오기
큐에서 값을 가져오려면 xQueueReceive() 함수를 사용할 수 있습니다. 큐 핸들을 인자로 전달하고, 값을 저장할 변수의 포인터를 지정하며, 항목이 사용 가능해질 때까지 대기할지(portMAX_DELAY) 아니면 즉시 반환할지(0 전달)를 지정합니다.
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
LED 밝기 제어
12비트 해상도로 PWM을 제어하고 있으며 PWM의 기본 해상도(12비트)를 사용하기 때문에, 밝기는 potValue와 동일하게 됩니다.
uint16_t brightness = potValue;
마지막으로, 우리는 다음을 사용할 수 있습니다. ledcWrite()LED의 밝기를 조절하는 기능입니다.
ledcWrite(LED_PIN, brightness);
setup()
setup() 에서 시리얼 모니터를 115200의 전송 속도로 초기화합니다.
Serial.begin(115200);
PWM 속성을 설정합니다.
ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
Queue 대기열 생성
xQueueCreate() 함수를 사용하여 실제 큐를 생성합니다. 이 함수는 인자로 큐의 크기와 각 항목의 크기(바이트 단위)를 받습니다. uint16_t 변수를 저장할 예정이므로 uint16_t의 크기인 2바이트(16비트)를 전달합니다.
// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
작업 생성
그런 다음, 전위차계를 제어하고 LED 밝기를 제어하는 작업을 만듭니다. 두 작업 모두 코어 1에 할당합니다.
// Create tasks
xTaskCreatePinnedToCore(
potTask,
"potTask",
3000, // Task stack
NULL,
1,
NULL,
1 // Core 1
);
xTaskCreatePinnedToCore(
LEDBrightnessTask,
"LEDBrightnessTask",
3000,
NULL,
1,
NULL,
1
);
이전 튜토리얼에서 작업을 만드는 방법을 다루었습니다. FreeRTOS(Arduino IDE)를 사용한 ESP32 – 시작하기: 작업 만들기
loop()
loop() 에서는 FreeRTOS가 작업을 처리하므로 비어 있습니다.
void loop() {
// Empty
}
데모
보드에 코드를 업로드하세요. ESP32가 코드 실행을 시작하도록 보드에 내장된 RST 버튼을 누르세요.
그런 다음, 통신 속도를 115200으로 설정하여 직렬 모니터를 엽니다. 비슷한 결과가 나올 것입니다.

대기열을 사용한 ESP32 freertos 작업 - 직렬 모니터 데모
전위차계를 돌려서 LED 밝기가 그에 따라 증가하거나 감소하는 것을 확인하세요.

ESP32 포텐셜미터로 LED 밝기 제어
예제 2: FreeRTOS 대기열 - 세 가지 작업 간 데이터 공유
여러 FreeRTOS 작업 간에 데이터를 공유할 수 있습니다. 이전 예제에서는 한 작업에서 다른 작업으로 데이터를 전달했지만, 원하는 만큼 많은 작업에 데이터를 전달할 수 있습니다.
이 예제는 이전 프로젝트를 수정하여 세 번째 작업을 추가한 것입니다. 이 세 번째 작업은 대기열에서 전위차계 값을 검색하여 직렬 모니터에 타임스탬프와 함께 값을 표시하는 역할을 합니다. 또한 다른 작업에서 모든 직렬 출력을 제거했습니다.
이는 여러 작업 간에 데이터를 공유하기 위해 큐를 사용하는 방법을 보여주는 간단한 예입니다.
여러 작업이 동일한 데이터를 수신하도록 하려면 동일한 값을 가진 두 개(또는 그 이상)의 개별 큐에 데이터를 입력해야 합니다. 그러면 각 작업이 자체 큐에서 데이터를 가져옵니다. FreeRTOS에서는 큐 항목을 한 번만 제거할 수 있기 때문에 하나의 작업만 하나의 큐에서 각 값을 수신할 수 있습니다.
회로 배선
이전 예제와 동일한 회로를 LED와 전위차계로 유지합니다.
코드: FreeRTOS 대기열 – 세 가지 작업
다음 코드를 Arduino IDE에 복사합니다.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-freertos-queues-inter-task-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15
#define LED_PIN 2
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
#define QUEUE_SIZE 5
QueueHandle_t potQueue = NULL;
QueueHandle_t sMonitorQueue = NULL;
void SensorTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
xQueueSend(sMonitorQueue, &potValue, portMAX_DELAY); // Send to queue
vTaskDelay(300 / portTICK_PERIOD_MS); // 300ms
}
}
void LEDBrightnessTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
uint16_t brightness = potValue;
ledcWrite(LED_PIN, brightness);
}
}
}
void SerialLoggerTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(sMonitorQueue, &potValue, portMAX_DELAY)) {
Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());
}
}
}
void setup() {
Serial.begin(115200);
// Setup PWM for LED
ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
// Create queue (5 items, each uint16_t)
sMonitorQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (sMonitorQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
// Create tasks with one parameter per line
xTaskCreatePinnedToCore(
SensorTask, // Task function
"SensorTask", // Task name
3000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
LEDBrightnessTask, // Task function
"LEDBrightnessTask", // Task name
3000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
SerialLoggerTask, // Task function
"SerialLoggerTask", // Task name
3000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
}
void loop() {
// Empty
}
코드는 어떻게 작동하나요?
이 코드는 이전 예제와 매우 유사하지만, 대기열과 작업이 추가로 추가되었습니다.
기본적으로, 우리는 sMonitorQueue라는 또 다른 큐를 생성합니다.
QueueHandle_t potQueue = NULL;
QueueHandle_t sMonitorQueue = NULL;
SensorTask()에서 우리는 두 큐 모두에 전위차계 값을 입력했습니다.
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
xQueueSend(sMonitorQueue, &potValue, portMAX_DELAY); // Send to queue
추가 작업인 SerialLoggerTask()를 추가합니다.
void SerialLoggerTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(sMonitorQueue, &potValue, portMAX_DELAY)) {
Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());
}
}
}
이 작업은 sMonitorQueue에서 potValue를 가져옵니다.
if (xQueueReceive(sMonitorQueue, &potValue, portMAX_DELAY)) {
그런 다음 타임스탬프와 함께 직렬 모니터에 표시합니다.
Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());
또한 이 새 작업을 setup()에서 생성해야 합니다.
xTaskCreatePinnedToCore(
SerialLoggerTask, // Task function
"SerialLoggerTask", // Task name
3000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
그리고 대기열도 추가돼요.
// Create queue (5 items, each uint16_t)
sMonitorQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (sMonitorQueue== NULL) {
Serial.println("Failed to create queue!");
while (1);
}
데모
결과는 이전 예제와 유사하지만 이제 다음이 있습니다.시리얼로거 작업직렬 모니터에 값과 타임스탬프를 표시합니다.

직렬 모니터에 데이터를 인쇄하는 freertos 작업이 포함된 Esp32 - 데모
이 간단한 예는 여러 개의 대기열을 사용하여 원하는 만큼 많은 작업에 데이터를 전달할 수 있음을 보여줍니다.
예제 3: 화면에 센서 데이터 표시(데이터 구조 공유)

OLED 디스플레이의 BME280 센서에서 ESP32 디스플레이 센서 판독값
이 예제에서는 FreeRTOS 작업을 사용하여 BME280의 센서 판독값을 OLED 디스플레이에 표시합니다. 이 과정은 두 가지 주요 작업으로 나뉩니다.
센서 작업 :
BME280 센서에서 온도, 습도 및 압력을 읽습니다.
센서 데이터를 대기열로 보냅니다.
디스플레이 작업
큐에서 센서 데이터를 수신합니다.
OLED 화면에 판독값을 표시합니다.
이 예제의 목적은 여러 변수(온도, 습도, 압력)를 포함하는 데이터 구조를 작업 간에 공유하는 방법을 보여주는 것입니다.
필요한 부품
이 예에서는 다음 부품이 필요합니다.
귀하가 선택한 ESP32 보드 - 최고의 ESP32 개발 보드를 읽어보세요
BME280 센서
SSD1306 OLED 디스플레이
브레드보드
점퍼 와이어
회로 배선
BME280과 OLED 디스플레이를 ESP32 기본 I2C 핀에 연결합니다.

OLED 디스플레이와 BME280을 ESP32 기본 I2C 핀에 연결한 ESP32 회로
ESP32 I2C 통신: 핀 설정, 다중 버스 인터페이스 및 주변 장치(Arduino IDE) 도 읽어보시기 바랍니다 .
| BME280 | ESP32 |
| VIN | 3V3 |
| GND | GND |
| SCL | GPIO 22 |
| SDA | GPIO 21 |
| OLED Display | ESP32 |
| VIN | 3V3 |
| GND | GND |
| SCL | GPIO 22 |
| SDA | GPIO 21 |
코드 - FreeRTOS 대기열을 사용하여 화면에 센서 데이터 표시
다음 코드를 Arduino IDE에 복사합니다.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-freertos-queues-inter-task-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDRESS 0x3C
#define QUEUE_SIZE 5
Adafruit_BME280 bme;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
QueueHandle_t sensorQueue = NULL;
typedef struct {
float temperature;
float humidity;
float pressure;
} SensorData;
void SensorTask(void *parameter) {
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
for (;;) {
SensorData data;
data.temperature = bme.readTemperature();
data.humidity = bme.readHumidity();
data.pressure = bme.readPressure() / 100.0F;
xQueueSend(sensorQueue, &data, portMAX_DELAY);
Serial.print("SensorTask: Sent Temp=");
Serial.print(data.temperature, 1);
Serial.print("°C, Hum=");
Serial.print(data.humidity, 1);
Serial.print("%, Pres=");
Serial.print(data.pressure, 1);
Serial.println("hPa");
vTaskDelay(2000 / portTICK_PERIOD_MS); // 2s
}
}
void DisplayTask(void *parameter) {
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("OLED init failed!");
while (1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
for (;;) {
SensorData data;
if (xQueueReceive(sensorQueue, &data, portMAX_DELAY)) {
// Display on OLED
display.clearDisplay();
display.setCursor(0, 0);
display.print("Temperature: ");
display.print(data.temperature, 1);
display.print(" ");
display.cp437(true);
display.write(167);
display.println("C");
display.setCursor(0, 20);
display.print("Humidity: ");
display.print(data.humidity, 1);
display.println(" %");
display.setCursor(0, 40);
display.print("Pressure: ");
display.print(data.pressure, 1);
display.println(" hPa");
display.display();
}
}
}
void setup() {
Serial.begin(115200);
// Starts I2C on the board default's I2C pins
Wire.begin();
// Create queue
sensorQueue = xQueueCreate(QUEUE_SIZE, sizeof(SensorData));
if (sensorQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
// Create tasks
xTaskCreatePinnedToCore(
SensorTask, // Task function
"SensorTask", // Task name
4000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
DisplayTask, // Task function
"DisplayTask", // Task name
4000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
}
void loop() {
// Empty
}
코드는 어떻게 작동하나요?
이 시점에서는 대부분의 코드에 익숙해졌을 것입니다.
여러 변수를 큐에 전송하기 위해 온도, 습도, 기압을 포함하는 구조체를 생성합니다. 이러한 구조체를센서데이터.
혹은 JSON 객체를 사용할 수도 있지만, 이 시나리오에서는 구조체를 사용하는 것이 더 쉽다고 생각합니다.
typedef struct {
float temperature;
float humidity;
float pressure;
} SensorData;
SensorTask 함수는 현재 센서 측정값을 가져와 큐로 전송하는 역할을 담당합니다. 우리는 data라는 변수를 생성하는데, 이는 SensorData 유형의 데이터 구조체입니다(이전에 생성한 바로 그 구조체입니다).
SensorData data;
그런 다음 값을 추가합니다.데이터구조는 다음과 같습니다.
data.temperature = bme.readTemperature();
data.humidity = bme.readHumidity();
data.pressure = bme.readPressure() / 100.0F;
이제 최신 센서 측정값으로 데이터 구조를 채웠으므로, 이전 예제에서와 마찬가지로 xQueueSend() 함수를 사용하여 이를 큐로 보낼 수 있습니다.
xQueueSend(sensorQueue, &data, portMAX_DELAY);
DisplayTask 함수에서 큐에서 데이터를 가져와 OLED 화면에 표시합니다.
void DisplayTask(void *parameter) {
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("OLED init failed!");
while (1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
for (;;) {
SensorData data;
if (xQueueReceive(sensorQueue, &data, portMAX_DELAY)) {
// Display on OLED
display.clearDisplay();
display.setCursor(0, 0);
display.print("Temperature: ");
display.print(data.temperature, 1);
display.print(" ");
display.cp437(true);
display.write(167);
display.println("C");
display.setCursor(0, 20);
display.print("Humidity: ");
display.print(data.humidity, 1);
display.println(" %");
display.setCursor(0, 40);
display.print("Pressure: ");
display.print(data.pressure, 1);
display.println(" hPa");
display.display();
}
}
}
이전 예와 마찬가지로,설정(), 대기열과 작업을 생성하고 이를 ESP32 코어에 할당합니다.
void setup() {
Serial.begin(115200);
// Starts I2C on the board default's I2C pins
Wire.begin();
// Create queue
sensorQueue = xQueueCreate(QUEUE_SIZE, sizeof(SensorData));
if (sensorQueue == NULL) {
Serial.println("Failed to create queue!");
while (1);
}
// Create tasks
xTaskCreatePinnedToCore(
SensorTask, // Task function
"SensorTask", // Task name
4000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
DisplayTask, // Task function
"DisplayTask", // Task name
4000, // Stack size (bytes)
NULL, // Task parameters
1, // Priority
NULL, // Task handle
1 // Core ID
);
}
loop() 함수는 FreeRTOS가 작업을 처리하므로 비어 있습니다.
void loop() {
// Empty
}
데모
이전 코드를 ESP32 보드에 업로드하세요. 업로드 후, 보드에 내장된 RST 버튼을 눌러 보드가 코드 실행을 시작하도록 하세요.
115200의 전송 속도로 직렬 모니터를 엽니다. 2초마다 업데이트된 센서 판독값이 표시됩니다.

2초마다 직렬 모니터에 표시되는 ESP32 센서 작업(FreeRTOS)
새로운 판독값이 대기열에 전송됨에 따라 화면도 같은 속도로 업데이트됩니다.

FreeRTOS 대기열을 사용하여 ESP32에서 BME280 센서 판독값을 OLED 디스플레이에 표시
마무리하기
이 튜토리얼에서는 FreeRTOS 큐를 사용하여 한 작업에서 다른 작업으로 안전하게 데이터를 전달하는 방법을 배웠습니다. 큐를 사용하여 작업 간 통신을 얼마나 쉽게 수행할 수 있는지 보여주는 세 가지 예제를 살펴보았습니다. 이러한 기본적인 예제를 이해한 후에는 변수 값에 동시에 접근하는 것이 프로그램에서 매우 중요할 수 있는 더 복잡한 프로젝트에 이 개념을 적용해 보겠습니다.
'ESP32' 카테고리의 다른 글
| ESP32 MicroPython 개발 참고 문서 (0) | 2025.10.19 |
|---|---|
| ESP32 DevkitC V4 USB C 타입 32E 모듈 적용 개발보드 (0) | 2025.10.19 |
| FreeRTOS를 사용한 ESP32: 세마포어 시작하기 (0) | 2025.10.14 |
| FPV 카메라가 장착된 ESP32 RC 잠수함 만들기(ESP-DIVE) (0) | 2025.10.14 |
| ESP32 듀얼 코어를 사용하는 방법 Arduino IDE (0) | 2025.10.13 |
| FreeRTOS를 사용한 ESP32 – 시작하기 Arduino IDE Create Tasks (0) | 2025.10.13 |
| ESP32 SPI 마스터-슬레이브 통신 (1) | 2025.10.01 |
| RTSP를 이용한 Arduino ESP32 오디오 스트리밍 (0) | 2025.10.01 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
캐어랩