1
1

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】Bufferとは?MessageBufferとの違いと最小サンプル

Posted at

この記事でわかること

  • StreamBuffer(ストリームバッファ)で何ができるか
  • MessageBufferとの違い(境界あり/なし)
  • 最小サンプル(ESP32 ESP-IDFで動く)
  • 主要API(Create/Send/Receive/BytesAvailable)の役割

こんな方におすすめ

  • StreamBufferの機能を知りたい
  • UART受信(バイト列)を別タスクに渡したい
  • 「受信割り込み→処理タスク」構成をシンプルにしたい

1. StreamBufferとは

FreeRTOSの StreamBuffer は、タスク間で “バイト列の流れ” を安全に受け渡しする仕組みです。

送信側:任意の長さのバイト列を流し込む

受信側:欲しい分だけ取り出す(読み出しサイズは自由)

ポイントは 「メッセージ境界が無い」 こと。
つまり HELLO と WORLD を送っても、受信側は HELLOWO のように まとめて読めたり、分割して読めたり します。

2. MessageBufferとの違い

一言で言うと:
MessageBuffer:メッセージ単位(境界あり)
StreamBuffer:バイトストリーム(境界なし)

以下の使い分けがされるそうです。

  • フレーム境界が決まっている(JSON 1件、固定ヘッダ付き、行単位など)
    → MessageBuffer

  • UART受信のように“連続するバイト列” を貯めたい
    → StreamBuffer

3. 典型ユースケース

ESP32だと、UART受信は「割り込み/ドライバ側 → アプリ処理タスク」に流すのが基本です。
この“中継”に StreamBuffer がハマります。
受信側(ISRや受信タスク):入ってきたバイト列を Send
処理側(パーサ/プロトコルタスク):必要な分だけ Receive
ポーリングで受信を監視するより、CPU効率も見通しも良くなります。

4. 【最小サンプル】StreamBufferでバイト列を流す(ESP32 + ESP-IDF)

やること:
ProducerTask:文字列をバイト列として流し込む
ConsumerTask:一定サイズで受信して表示する
StreamBufferは '\0' 不要です。
今回は表示のために受信側で終端を付けます。

#include <cstdio>
#include <cstring>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h"

static StreamBufferHandle_t sbuf;

static void ProducerTask(void *pv)
{
    const char *msg = "HELLO_STREAM_BUFFER:";
    for (;;) {
        // バイト列を“流す”(境界なし)
        xStreamBufferSend(sbuf, msg, strlen(msg), portMAX_DELAY);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

static void ConsumerTask(void *pv)
{
    uint8_t rx[16];  // あえて小さくして「分割受信」を見せる
    for (;;) {
        // 欲しい分だけ受け取る(最大rxサイズ)
        size_t n = xStreamBufferReceive(sbuf, rx, sizeof(rx), portMAX_DELAY);

        if (n > 0) {
            // 表示しやすいように一時的に終端を付ける
            char s[17];
            size_t m = (n < 16) ? n : 16;
            memcpy(s, rx, m);
            s[m] = '\0';
            printf("[RX] %s (n=%u)\n", s, (unsigned)n);
        }
    }
}

extern "C" void app_main(void)
{
    // 容量は“バイト数”。UART受信のバッファサイズ感で決める
    sbuf = xStreamBufferCreate(128, 1);  // 第2引数=trigger level(何バイト貯まったら受信側を起こすか)
    if (sbuf == NULL) {
        printf("xStreamBufferCreate failed\n");
        return;
    }

    xTaskCreate(ProducerTask, "ProducerTask", 2048, NULL, 5, NULL);
    xTaskCreate(ConsumerTask, "ConsumerTask", 2048, NULL, 5, NULL);
}

動きのポイント

  • Producerは HELLO_STREAM_BUFFER: を連続で流し込む
  • Consumerは 16バイトずつ取るので、ログが 分割されて出る
    image.png

5. 主要API

StreamBufferで最初に押さえるのは、作る・送る・受け取るの3つだけです。
余裕があれば BytesAvailable(今たまっている量の確認) もデバッグに便利です。

xStreamBufferCreate(作る)

  • StreamBuffer を作成してハンドルを返します(失敗時は NULL)
sbuf = xStreamBufferCreate(128, 1);

xStreamBufferSend(送る)

  • バイト列を StreamBuffer に書き込みます
size_t sent = xStreamBufferSend(sbuf, data, len, portMAX_DELAY);

xStreamBufferReceive(受け取る)

  • StreamBuffer からバイト列を読み出します
size_t n = xStreamBufferReceive(sbuf, rx, sizeof(rx), portMAX_DELAY);

xStreamBufferBytesAvailable(現在たまっている量)

  • 今バッファに何バイト溜まっているかを確認できます(デバッグ/監視に便利)
size_t avail = xStreamBufferBytesAvailable(sbuf);

6. 注意点

  • 原則 1 writer / 1 reader 前提(境界がないため、受信は 分割も結合も起こる
  • プロトコル境界(改行・ヘッダ・長さ)は自前で扱う
  • バッファサイズは「最大バースト量+余裕」で決める(UARTが一気に来る/処理が詰まる瞬間を想定)

7. まとめ

  • StreamBufferは バイト列ストリーム を渡す仕組み(境界なし)
  • MessageBufferは 境界あり。用途が違う
  • StreamBufferで最初に押さえるのは、作る・送る・受け取るの3つ
  • バッファサイズは「最大バースト量+余裕」で決める

FreeRTOS × ESP32 記事シリーズ

1
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?