この記事でわかること
- 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);
}
動きのポイント
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 記事シリーズ
