1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【FreeRTOS × ESP32】Queueとは?グローバル変数との違い・実装例・設計指針を解説

Last updated at Posted at 2025-12-07

この記事でわかること

  • FreeRTOS の Queue が何か理解できる
  • グローバル変数ではなぜ危険かがわかる
  • ESP32で動くqueueを使った実用コードが手に入る

目次

  1. FreeRTOS Queueとは — FreeRTOS における「タスク間通信手段」
  2. FreeRTOS Queueとグローバル変数の違い
  3. 【実装】 ESP32で動かす FreeRTOS Queue 利用の Lチカ実装例
  4. グローバル変数を使用した場合の問題点
  5. FreeRTOS Queue を使うべき場面と設計指針
  6. FreeRTOS Queue が溢れたとき・データ取りこぼし
  7. まとめ

1. FreeRTOS Queueとは — FreeRTOS における「タスク間通信手段」

FreeRTOS の Queue(キュー) は、タスク間でデータを受け渡しするための仕組みです。
一言でいうと、

タスク間で値を安全に保管できる“箱(メッセージバッファ)”

です。

FreeRTOS Queue はどんな箱?

  • Queue は RAM 上に一定サイズのメモリ領域を確保
  • send すると、値は コピーされて Queue 内に格納
  • receive すると、Queue の 先頭から順番に取り出される
  • 複数件保持可能(上限は作成時に決める)
  • FIFO(先入れ先出し)
  • FreeRTOS が内部で排他制御してくれる

📦 イメージ:

[ TaskA ] --send-->  [ Queueの箱 ] --receive--> [ TaskB ]

Queue の箱には、

┌─────────────┐
│ 500 ms       │
├─────────────┤
│ 200 ms       │
├─────────────┤
│ 500 ms       │
└─────────────┘

のように 複数データを順番に保持できます

ここが グローバル変数とはまったく違う点です。


FreeRTOS Queue が保持できる容量はこう決まる

Queue を作成する関数:

xQueueCreate( queueLength, itemSize );
引数 説明
queueLength Queue 内に保持できる“件数”
itemSize 1件あたりのデータサイズ(例:sizeof(uint32_t)

例:

xQueueCreate(3, sizeof(uint32_t));

uint32_t を最大 3 件まで保持できる箱

📦 内部イメージ:

[500][200][500]   ← 最大3件まで

FreeRTOS Queue の超大事なポイント(初心者が落としがちな部分)

Queueは「変数の参照を共有する」のではなく、「値をコピーして内部に保持する」

つまり、

  • send した瞬間、値は Queue 内に保存される
  • send 元の変数が上書きされても Queue 内のデータは上書きされない
  • FIFO順で確実に取り出せる
  • 読み取りタイミングに左右されない

この「コピー保持」という仕組みが、
安定したタスク間通信を可能にしています。


2. FreeRTOS Queueとグローバル変数の違い

グローバル変数

  • 状態(最後の1つ)しか持てない
  • 上書きされると過去の値は消失
  • 読み取りタイミングが悪いと 古い値を参照する可能性
  • 排他制御を自分で実装する必要がある

例:

blinkInterval = 500
blinkInterval = 200
blinkInterval = 500

最終的には 500 しか残らない
途中の 200 が存在した事実は “消える”。


Queue

[500][200][500]

というように 送信された順番で蓄積される

  • 履歴を保持できる
  • 読み取りのタイミングがズレても “取りこぼしがない”
  • FreeRTOS が排他制御を内部実装
  • 最新値だけ扱いたい場合は xQueueOverwrite() が便利

Queue = イベント共有
グローバル変数 = 状態共有


3. 【実装】 ESP32で動かす FreeRTOS Queue 利用の Lチカ実装例

以下は Arduino + ESP32(DevKitC)で動くコードです:

  • TaskA:500ms ⇔ 200msを切り替え、Queue に送信
  • TaskB:Queue を受信して点滅周期を変更
  • Queue 長 = 1(最新値だけ保持)
  • xQueueOverwrite() により「最新値を保存し続ける」
#include <Arduino.h>

QueueHandle_t blinkQueue;
const int LED_PIN = 16; //タスクの記事同様のピンを使用

void TaskA(void *pvParameters) {
  uint32_t interval = 500;
  for (;;) {
    interval = (interval == 500) ? 200 : 500;
    xQueueOverwrite(blinkQueue, &interval);
    vTaskDelay(pdMS_TO_TICKS(2000));
  }
}

void TaskB(void *pvParameters) {
  uint32_t currentInterval = 500;
  pinMode(LED_PIN, OUTPUT);

  for (;;) {
    if (xQueueReceive(blinkQueue, &currentInterval, 0) == pdPASS) {
      // 最新設定を反映
    }

    digitalWrite(LED_PIN, HIGH);
    vTaskDelay(pdMS_TO_TICKS(currentInterval));
    digitalWrite(LED_PIN, LOW);
    vTaskDelay(pdMS_TO_TICKS(currentInterval));
  }
}

void setup() {
  blinkQueue = xQueueCreate(1, sizeof(uint32_t));  // 最新値だけ保持
  if (!blinkQueue) while (1) delay(1000);

  xTaskCreate(TaskA, "TaskA", 2048, NULL, 1, NULL);
  xTaskCreate(TaskB, "TaskB", 2048, NULL, 1, NULL);
}

void loop() {
  vTaskDelay(portMAX_DELAY);  // CPU占有を避ける
}

4. グローバル変数を使用した場合の問題点

今回の Lチカ(更新が 2秒間隔、点滅が 200~500ms)では、
グローバル変数でも一見正しく動きます

理由:

  • TaskB の delay が終わる頃には、最新値を読むことができる
  • “タイミングがゆるい” と、問題が発生しにくい

しかし本当に危ないのは:

タスクBが delay 中に A が何度も更新しても、途中の値がすべて無視されること

例:更新が高速だと…

500 → 200 → 500 → 200 → …

TaskB は delay中なので、

次に読むときには「最後の値しか残らない」
= 途中のイベントは 完全に消える

これは LED なら許されても、

  • センサーイベント
  • エラー通知
  • コマンド処理
  • ログ

などだと 致命的


5. FreeRTOS Queue を使うべき場面と設計指針

要件 設計
最新値だけほしい Queue長=1 + xQueueOverwrite()
履歴も処理したい Queue長>1 + xQueueSend()
ロスOK xQueueSend(..., 0)
ロス禁止 xQueueSend(..., portMAX_DELAY)
複数イベント/通知/コマンド 迷わず Queue

Queue の強みは 設計の自由度が高いこと


6. FreeRTOS Queue が溢れたとき・データ取りこぼし

xQueueSend(..., 0)

  • 満杯なら送信失敗
  • ロスが発生する(捨てられる)

xQueueSend(..., portMAX_DELAY)

  • 空きができるまで待機
  • ロスしない

xQueueOverwrite(queue, &data)

  • Queue長=1なら最適
  • 常に最新値を保持する
  • 古い値は自動的に消える(というか上書き)

FreeRTOS は勝手に古い値を捨てたりしない
どう扱うかは設計者が選ぶ


7. まとめ

今回はqueueとグローバル変数について比較し、
queueの有効性について紹介しました。

  • グローバル変数は状態共有
    → 最新値1つしか見えない
    → 途中イベントが消える

  • Queueはイベント共有
    → 履歴を扱える
    → 読み取りタイミングに影響しない
    → FreeRTOS が排他制御

  • 現実の組み込み開発では Queue が必須
    LED点滅ならたまたま許されるが、
    イベント通知・ログ・エラー処理ではグローバル変数は破綻する。

今後も、タスク間通信について投稿していきます。
次は FreeRTOS の セマフォ(Mutex / バイナリセマフォ)の使いどころと、
Queue との 明確な使い分けを解説します。

FreeRTOS × ESP32 記事シリーズ

Queueについて詳細にまとめていますので良ければご覧ください

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?