この記事でわかること
- MessageBufferの機能内容(何ができるか)
- Queueとの違い(固定長 vs 可変長)
- 最小構成のサンプル(ESP32 ESP-IDFで動く)
今までArduinoのフレームワークを使用していましたが、今後はESP-IDFを使用したコードで実装例を示します。
ESP-IDFの環境構築について知りたい方がいれば、まとめますのでコメントよろしくお願いいたします。
1. MessageBufferの機能内容(何ができる?)
FreeRTOS の MessageBuffer(メッセージバッファ) は、
タスク間で 可変長のデータ(バイト列) を送受信できる仕組みです。
Queueは「固定サイズのデータ(例:uint32_tや構造体)」を渡すのが得意ですが、
MessageBufferは 文字列・JSON・可変長フレーム のように、長さが毎回変わるデータを扱うのに向いています。
できることをざっくり言うと:
- 送信:可変長データ(バイト列)をバッファに入れる
- 受信:1件(1メッセージ)ずつ取り出す(境界がある)
ができます。
2. Queueとの違い(初心者向けに一言で)
違いを一言でまとめるとこうです。
- Queue:固定長の“箱”を順番に渡す
- MessageBuffer:可変長の“メッセージ”を1件ずつ渡す
Queueだと困りやすいケース
- 文字列(ログ)を渡したい
- JSONを渡したい
- 通信フレーム(長さ可変)を渡したい
Queueでやろうとすると、
「最大長のバッファ構造体を作る」「無駄なコピーが増える」などで面倒になります。
MessageBufferなら、メッセージ長を意識せず送って、受信側でまとめて受け取れるので実装がシンプルになります。
3. MessageBufferのイメージ(1件ずつ受け取れる)
MessageBufferは、単なるバイトの流れではなく “メッセージ境界” があるのが特徴です。
[ "HELLO" ][ "JSON:{...}" ][ "OK" ]
受信側は1件ずつ取り出せます。
4. 【実装】最小サンプル(ESP32 + ESP-IDFで動く)
やること
- SenderTask:可変長メッセージ(文字列)を送信
- ReceiverTask:受信して printf() で出力
MessageBuffer自体は '\0' を必要としません(長さ付きバイト列として扱うため)。
ただ今回は printf("%s") で出力したいので、送信側で '\0' まで含めて送っています。
#include <cstdio>
#include <cstring>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h"
// MessageBuffer全体の容量(この中に複数メッセージが格納される)
static MessageBufferHandle_t mbuf;
void SenderTask(void *pvParameters) {
const char *msg1 = "HELLO";
const char *msg2 = "JSON:{\"temp\":25.3}";
for (;;) {
// 文字列末尾の '\0' も含めて送る(受信側でprintfしやすい)
xMessageBufferSend(mbuf, msg1, strlen(msg1) + 1, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(500));
xMessageBufferSend(mbuf, msg2, strlen(msg2) + 1, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1500));
}
}
void ReceiverTask(void *pvParameters) {
char rx[64];
for (;;) {
// 受信バッファサイズを超えるメッセージは受け取れないので注意
size_t n = xMessageBufferReceive(mbuf, rx, sizeof(rx), portMAX_DELAY);
if (n > 0) {
printf("[RX] %s (len=%u)\n", rx, (unsigned)n);
}
else {
printf("MessageBufferReceiveError\n");
vTaskDelay(pdMS_TO_TICKS(100)); // ログ連打防止
}
}
}
extern "C" void app_main(void)
{
// MessageBuffer全体のバッファサイズ(この中に可変長メッセージが詰まる)
mbuf = xMessageBufferCreate(128);
if(mbuf == NULL){
printf("MessageBufferCreateError\n");
return;
}
xTaskCreate(SenderTask, "SenderTask", 2048, NULL, 1, NULL);
xTaskCreate(ReceiverTask, "ReceiverTask", 2048, NULL, 1, NULL);
}
5. 主要関数(役割だけ押さえる)
xMessageBufferCreate(作る)
mbuf = xMessageBufferCreate(128);
//MessageBufferを作る(128バイト分の領域を確保)
xMessageBufferSend(送る)
xMessageBufferSend(mbuf, data, len, portMAX_DELAY);
// data(バイト列)を len バイト送る
// 送れたら len が返り、満杯などで送れなければ 0 が返る
xMessageBufferReceive(受け取る)
n = xMessageBufferReceive(mbuf, rx, sizeof(rx), portMAX_DELAY);
//1件のメッセージを受け取る(rxにコピーされる)
6. 使いどころ
ログ文字列を別タスクに渡してまとめて出力
JSONやHTTPの一部など可変長データの受け渡し
通信フレーム(長さが決まっていないデータ)の受け渡し
7. 注意点(初心者が詰まりやすい)
受信バッファが小さい → xMessageBufferReceive() が 0 を返す
※そのメッセージはバッファに残るので、次の受信も同じメッセージで失敗し続ける
→ rx を十分大きくする(または最大長を設計で決める)
MessageBufferの容量が小さいとすぐ満杯になる
→ xMessageBufferCreate() のサイズは余裕を持たせる
可変長は便利だが、最大サイズの設計は必要
→ “無限に長いデータ”は扱わない(必ず上限を決める)
原則は 1 writer / 1 reader 前提
→ MessageBuffer(StreamBuffer含む)は、基本的に 送信者1・受信者1 を想定しています。
8. まとめ
- MessageBufferは 可変長メッセージ をタスク間で渡せる仕組み
- Queueが苦手な 文字列/JSON/可変長フレーム に強い
- 原則は 1 writer / 1 reader 前提
次は、バイト列を“流す”用途に強い StreamBuffer を紹介します(UART受信などで便利です)。
FreeRTOS × ESP32 記事シリーズ