ESP32(ESP-IDF)でFreeRTOSを使うと、EventGroupで“複数条件待ち”を実装する場面がすぐ出ます。
例えば「Wi-Fi接続完了」+「センサー初期化完了」の両方が揃ったら送信開始、のようなケースです。
この“待ち合わせ”を シンプルに書ける仕組みが、FreeRTOSの EventGroup(イベントグループ)です。EventGroupは、複数のフラグ(ビット)をまとめて管理し、AND待ち/OR待ちができます。
Contents
この記事でわかること
- EventGroup(イベントグループ)で何ができるか
- 「複数条件待ち(AND/OR)」をどう書くか
- 最小サンプル(ESP32 ESP-IDF で動く)
- 主要API(Create / Set / Wait / Clear)の役割と引数の意味
こんな方におすすめ
- Wi-FiやMQTTやセンサーなど、複数の準備完了を待ってから処理したい
- グローバル変数のフラグ監視(ポーリング)が増えて、見通しが悪い
- EventGroupの
xEventGroupWaitBits()の引数がよく分からない
EventGroupとは(何ができる?)
EventGroupは、複数のビット(フラグ)を1つのグループとして扱い、
- 条件が成立したらビットを立てる(SET)
- 必要なビットが揃うまで待つ(WAIT:AND/OR)
を実現する仕組みです。
EventGroupを使うメリット
メリット1:複数条件待ちがスッキリ書ける
条件が増えるほど「フラグ変数が散らかる」「チェックが増える」問題が出ます。EventGroupはビットで条件を集約でき、待ち条件が明確になります。
メリット2:待機中はCPUを使わない(ポーリング不要)
定期チェック(ポーリング)ではなく、条件が揃うまでタスクをブロックできます。
【最小サンプル】2条件が揃うまで待つ(ESP32 + ESP-IDF)
やりたいこと
WifiTask:Wi-Fi準備完了を想定してビットを立てるSensorTask:センサー準備完了を想定してビットを立てるMainTask:2つのビットが揃うまで待つ(AND待ち)→ 揃ったら処理開始
※この例は「起動シーケンス」として一度だけ待つ例です。周期処理で毎回待つ場合は、後述の「イベントとして使う(xClearOnExit=pdTRUE)」を使います。
#include <cstdio>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
static EventGroupHandle_t eg;
/* 「どのビットが何を意味するか」は設計で決める */
#define WIFI_READY_BIT (1U << 0)
#define SENSOR_READY_BIT (1U << 1)
static void WifiTask(void *pvParameters)
{
vTaskDelay(pdMS_TO_TICKS(3000)); // 3秒後にWi-Fi接続できた想定
xEventGroupSetBits(eg, WIFI_READY_BIT); // Wi-Fi準備完了を通知
printf("[Wi-Fi] Connected!\n");
for (;;) vTaskDelay(pdMS_TO_TICKS(1000));
}
static void SensorTask(void *pvParameters)
{
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒後にセンサー準備できた想定
xEventGroupSetBits(eg, SENSOR_READY_BIT); // センサー準備完了を通知
printf("[Sensor] Ready!\n");
for (;;) vTaskDelay(pdMS_TO_TICKS(1000));
}
static void MainTask(void *pvParameters)
{
/* AND待ち:両方揃うまでブロック */
xEventGroupWaitBits(
eg,
WIFI_READY_BIT | SENSOR_READY_BIT, // 待つビット
pdFALSE, // 成立してもビットを消さない(状態として保持)
pdTRUE, // 全ビット待ち(AND)
portMAX_DELAY // 無期限待ち
);
printf("[Main] Wi-Fi & Sensor OK -> Start!\n");
for (;;) vTaskDelay(pdMS_TO_TICKS(1000));
}
extern "C" void app_main(void)
{
eg = xEventGroupCreate();
if (eg == NULL) {
printf("xEventGroupCreate failed\n");
return;
}
xTaskCreate(WifiTask, "WifiTask", 2048, NULL, 5, NULL);
xTaskCreate(SensorTask, "SensorTask", 2048, NULL, 5, NULL);
xTaskCreate(MainTask, "MainTask", 2048, NULL, 5, NULL);
}
この形にすると「Wi-Fiもセンサーも揃ったら開始」が、xEventGroupWaitBits() 1発で書けます。
ログからもセンサーとWi-FiのタスクでEventGroupのビットがセットされたあと、メインタスクが動いていることが確認できます。

主要API(役割・引数・戻り値を最短で理解)
ここでは、EventGroupで最初に覚えるべきAPIだけに絞って整理します。
xEventGroupCreate(EventGroupを作る)
- 役割:EventGroup を作成して、ハンドルを返す
- 引数
- なし
- 戻り値
- 成功:
EventGroupHandle_t(作成したEventGroupのハンドル) - 失敗:
NULL(メモリ不足など)
- 成功:
xEventGroupSetBits(ビットを立てる)
- 役割:指定したビットを 1(セット) にする(状態成立/イベント発生を通知)
- 引数
- 第1引数:対象の EventGroup ハンドル(
EventGroupHandle_t) - 第2引数:セットしたいビット(
EventBits_t)- 例:
WIFI_READY_BIT - 複数指定:
WIFI_READY_BIT | SENSOR_READY_BIT
- 例:
- 第1引数:対象の EventGroup ハンドル(
- 戻り値
EventBits_t:Set後の EventGroup のビット状態(実装上の戻り値)
xEventGroupWaitBits(待つ:AND/OR、クリア有無、タイムアウト)
- 役割:指定したビット条件が成立するまで待つ(タスクをブロック)
- 引数
- 第1引数:対象の EventGroup ハンドル(
EventGroupHandle_t) - 第2引数:待ちたいビット(
EventBits_t)- 例:
WIFI_READY_BIT | SENSOR_READY_BIT
- 例:
- 第3引数:
xClearOnExit(条件成立後にビットを消すか)pdFALSE:消さない(状態フラグとして保持したい)pdTRUE:消す(1回きりのイベントとして扱いたい)
- 第4引数:
xWaitForAllBits(AND待ち / OR待ち)pdTRUE:AND待ち(全部揃うまで待つ)pdFALSE:OR待ち(どれか1つで起床)
- 第5引数:待ち時間(Tick)
0:待たない(条件未成立ならすぐ戻る)portMAX_DELAY:ずっと待つ(ブロック)pdMS_TO_TICKS(100):最大100ms待つ、など
- 第1引数:対象の EventGroup ハンドル(
- 戻り値
EventBits_t:起床時点のビット状態(戻り値に「どのビットが立っていたか」が入る)- OR待ちのときは
if (ret & WIFI_READY_BIT)のように判定できる - タイムアウト時は、待っていたビットが揃っていない状態で返る(
0とは限らない)
- OR待ちのときは
上記の実装例を下記に修正しOR待ち(xWaitForAllBits=pdFALSE)にすると、最初に成立したビットでMainTaskが起床します。
今回はSensorが先にreadyになるため、[Main] Sensor ready のみが出力されます。
ただし WifiTask 自体の [Wi-Fi] Connected! は遅れて出ます。
Wi-Fi側の [Main] Wi-Fi ready も出したい場合は、もう一度 xEventGroupWaitBits() を呼ぶ(ループする)必要があります。
// OR待ち:どちらかが揃えば起床
//ret には「起床した瞬間に立っていたビット」が入ります。
EventBits_t ret = xEventGroupWaitBits(
eg,
WIFI_READY_BIT | SENSOR_READY_BIT,
pdFALSE, // 条件成立後もビット保持
pdFALSE, // OR待ち
portMAX_DELAY
);
if (ret & WIFI_READY_BIT) printf("[Main] Wi-Fi ready\n");
if (ret & SENSOR_READY_BIT) printf("[Main] Sensor ready\n");
xEventGroupClearBits(ビットを下げる)
- 役割:指定したビットを 0(クリア) に戻す(状態リセット/次サイクル準備)
- 引数
- 第1引数:対象の EventGroup ハンドル(
EventGroupHandle_t) - 第2引数:クリアしたいビット(
EventBits_t)- 例:
WIFI_READY_BIT - 複数指定:
WIFI_READY_BIT | SENSOR_READY_BIT
- 例:
- 第1引数:対象の EventGroup ハンドル(
- 戻り値
EventBits_t:Clear前のビット状態(実装上の戻り値)
(補足)FromISR系(割り込みから使う場合)
xEventGroupSetBitsFromISR(ISRからビットを立てる)
- 役割:割り込み(ISR)から安全にビットをセットする
- 引数
- 第1引数:対象の EventGroup ハンドル
- 第2引数:セットしたいビット
- 第3引数:
pxHigherPriorityTaskWoken(高優先度タスクが起きたか返る)
- 戻り値
BaseType_t:成功/失敗(実装依存)
- 注意
- ISR内で重い処理はしない。ビットを立ててタスクに任せるのが基本。
AND待ち・OR待ちの使い分け(設計のコツ)
EventGroupの設計で迷いやすいのが、「ビットを状態として扱うか/イベントとして扱うか」です。
- 状態(readyフラグ)として使う:
xClearOnExit = pdFALSE(保持)- 例:Wi-Fi接続済み、初期化完了など
- イベント(1回きりの合図)として使う:
xClearOnExit = pdTRUE(自動で消す)- 例:ボタン押下、1回だけの開始合図など
まとめ
- EventGroupは、ビット(フラグ)を束ねて複数条件の待ち合わせ(AND/OR)ができる
- ポーリングせずに、条件が揃うまでタスクをブロックできる
- 最初は
Create / SetBits / WaitBits / ClearBitsの4つを押さえると実装が進む
次回はMessageBufferについて記載します。

コメント