進捗ログ:複数レイヤーでのバッファリング設計¶
タスク説明¶
Kurikintonsファームウェアのアーキテクチャを分析し、パフォーマンス向上・ブロッキング削減・リアルタイム精度改善が期待できるバッファリング層を特定。包括的なバッファリング戦略を設計した。
目標¶
- ブロッキングポイントの排除 → シリアル送信・ネットワークI/O
- タイムスタンプ精度の維持 → マイクロ秒精度の検出時刻
- ジッター削減 → 検出から出力までのレイテンシ変動を最小化
- スケーラビリティ → 高頻度検出でもバッファオーバーフロー対応
- 診断機能 → バッファー統計・オーバーフロー追跡
前提知識¶
バッファリング¶
バッファー(buffer)は「一時的なデータ保管庫」のようなものです。 ディスクではなく、メモリ上に確保されるため、高速にアクセスできます。
バッファリングの効果¶
- ブロッキング削減:
- USB送信には時間がかかる処理(100ms程度)
- バッファーを利用すると、データをリアルタイム取得しながら、データ送信できます
- スループット向上: 並行処理で全体を高速化
- 例)センサー読取(1ms)と送信(100ms)を同時処理
- タイムスタンプ精度: イベント発生時刻を確定できる
- 例)イベント発生 → 即座に時刻記録 → 後で送信(送信遅延に影響されない)
バッファーオーバーフロー¶
バッファーが満杯になることを「バッファーオーバーフロー」と呼びます。 オーバーフローしたデータをどのように処理するかは、設計時に検討が必要です。
バッファ: [イベント1][イベント2][イベント3] (満杯)
新しいイベント4が来た → どうする?
- 破棄:現在保存されているデータを優先し、イベント4を破棄する
- スライディング:古いイベント(イベント1)を削除し、イベント4を追加する
- エラーレスポンス:オーバーフローしたことをユーザーに通知
リングバッファーの原理¶
リングバッファーは、効率的にバッファリングするためのテクニックです。 バッファリングは、あくまで「一時的な保存」であって、永続的な保存ではありません。 バッファーから取り出したデータは、すぐに削除するのが一般的です。 リングバッファーは、名前のとおりバッファー用の配列を「円形」にみたて、 データの出し入れをぐるぐると回すことです。
初期状態:
head=0, tail=0
[ ][ ][ ][ ]
データ追加 (A, B, C):
head=0, tail=3
[A ][B ][C ][ ]
さらにD追加(循環):
head=0, tail=0 (% 4 で循環)
[D ][B ][C ][A ] ← tailが一周して head に追いつく
head=1で読取、tail=1で追加:
[D ][E ][C ][A ] ← 最も効率的
利点:
- メモリを再利用(配列サイズ固定)
- 削除処理不要(ポインターだけ進める)
kurikintonsの現状:
kurikintonsでも、リングバッファーを利用しています。
// 手動管理(複雑・読みにくい)
queue_tail = (queue_tail + 1) % TEXT_QUEUE_SIZE;
queue_count++;
FreeRTOS Queue¶
FreeRTOS QueueはArduinoに組み込まれている標準的なライブラリです。 試験的に導入した独自のリングバッファーでコマンド処理がきちんと動作することを確認できたため、 このライブラリへの移行を検討しています。 標準的なツールの導入により、堅牢性・可読性・保守性を向上させます。
特徴:
- スレッド安全(複数タスク間でも大丈夫)
- タイムアウト対応
- ブロッキング/ノンブロッキング選択可能
- 割り込みハンドラーからも使用可能
使用例:
// 従来(手動リングバッファー)
if (queue_count >= SIZE) return false;
queue[tail] = item;
tail = (tail + 1) % SIZE;
queue_count++;
// FreeRTOS Queue(簡潔・安全)
xQueueSend(queue_handle, &item, 0); // 0=タイムアウトなし
Kurikintonsでの利点:
- コマンド処理を堅牢化
- 将来的なマルチタスク対応に備える
- コード意図が明確(バッファー管理ロジックが隠蔽)
FreeRTOS Queueの基本的な使い方¶
ライブラリの読み込み¶
Arduino(ESP32)環境では、FreeRTOSはすでに組み込まれているため、明示的にインクルードする必要はありません。 ただし、型定義や関数を使う場合は以下のように記述します。
#include <freertos/queue.h>
ただし、通常はArduino SDKに含まれているため、以下の書き方でも動作します。
// Arduino環境ではこちらが一般的
#include <Arduino.h> // 既に含まれている
// FreeRTOS関数は直接使用可能
// xQueueCreate(), xQueueSend(), xQueueReceive() など
グローバルキューの宣言¶
// ヘッダーファイル(例:protocol_queue.h)
static QueueHandle_t command_queue;
// 実装ファイル(例:protocol_queue.cpp)
#define COMMAND_QUEUE_SIZE 10
void queue_init() {
// データ型のサイズとキュー容量を指定
command_queue = xQueueCreate(
COMMAND_QUEUE_SIZE, // キュー要素数
sizeof(command_t) // 1要素のバイトサイズ
);
if (command_queue == NULL) {
// キュー作成失敗(メモリ不足)
Serial.println("ERROR: Queue creation failed");
}
}
satic QueueHandle_tでリングバッファー用のメモリをグローバルに確保xQueueCreate関数でバッファーを初期化
キューイング(送信)¶
// データを送信(ノンブロッキング)
command_t cmd;
cmd.type = CMD_SET_THRESHOLD;
cmd.channel = 1;
cmd.value = 1234;
if (xQueueSend(command_queue, &cmd, 0) == pdTRUE) {
// 送信成功
Serial.println("Command queued");
} else {
// キューが満杯(オーバーフロー)
stats.queue_overflows++;
Serial.println("ERROR: Queue full, command dropped");
}
xQueueSend関数でキュー(バッファーにデータを追加)
デキューイング(受信)¶
command_t received_cmd;
// キューから取得(ノンブロッキング)
if (xQueueReceive(command_queue, &received_cmd, 0) == pdTRUE) {
// データ取得成功
handle_command(&received_cmd);
} else {
// キューが空(データなし)
// 何もしない
}
// または、ブロッキング待機(タイムアウト指定)
if (xQueueReceive(command_queue, &received_cmd, pdMS_TO_TICKS(100)) == pdTRUE) {
// 100ms以内にデータが来たら処理
handle_command(&received_cmd);
}
xQueueReceive関数でデキュー(バッファーからデータを取り出す)
よく使うパラメーター¶
| 関数 | パラメーター | 説明 |
|---|---|---|
xQueueCreate() |
(size, item_size) | キュー作成(size=要素数、item_size=1要素のバイトサイズ) |
xQueueSend() |
(queue, item, timeout) | キューに送信(0=タイムアウトなし) |
xQueueReceive() |
(queue, item, timeout) | キューから受信 |
uxQueueMessagesWaiting() |
(queue) | キュー内の要素数を返す |
xQueueReset() |
(queue) | キューをリセット(すべての要素を破棄) |
タイムアウト指定の例¶
// 0ms(ノンブロッキング、すぐ返す)
xQueueSend(queue, &data, 0);
// 100ms 待機(pdMS_TO_TICKS で ms→ tick に変換)
xQueueSend(queue, &data, pdMS_TO_TICKS(100));
// portMAX_DELAY(無限待機、ブロッキング)
xQueueSend(queue, &data, portMAX_DELAY);
実装例(Kurikintons向け)¶
ヘッダーファイル(text_protocol.h):
#define TEXT_COMMAND_QUEUE_SIZE 10
typedef struct {
uint8_t cmd_type;
int16_t param1;
int16_t param2;
} text_command_t;
void text_protocol_init();
bool text_queue_send(const text_command_t* cmd);
bool text_queue_receive(text_command_t* cmd);
uint32_t text_queue_count();
実装ファイル(text_protocol.cpp):
static QueueHandle_t text_command_queue = NULL;
void text_protocol_init() {
text_command_queue = xQueueCreate(
TEXT_COMMAND_QUEUE_SIZE,
sizeof(text_command_t)
);
if (text_command_queue == NULL) {
Serial.println("ERROR: Text queue creation failed");
}
}
bool text_queue_send(const text_command_t* cmd) {
if (text_command_queue == NULL) return false;
BaseType_t result = xQueueSend(text_command_queue, cmd, 0);
if (result != pdTRUE) {
// オーバーフロー処理
return false;
}
return true;
}
bool text_queue_receive(text_command_t* cmd) {
if (text_command_queue == NULL) return false;
BaseType_t result = xQueueReceive(text_command_queue, cmd, 0);
return (result == pdTRUE);
}
uint32_t text_queue_count() {
if (text_command_queue == NULL) return 0;
return uxQueueMessagesWaiting(text_command_queue);
}
利点(手動リングバッファーとの比較)¶
| 項目 | 手動リングバッファー | FreeRTOS Queue |
|---|---|---|
| 意図の明確性 | 低(queue_tail = (tail+1) % SIZE) |
高(xQueueSend() で意図が明確) |
| スレッド安全性 | 考慮が複雑 | ✅ 割り込みハンドラーからも安全 |
| メモリ管理 | 手動 | 自動(ライブラリ管理) |
| デバッグの容易さ | 難しい | 容易(キュー統計利用可) |
| メモリオーバーヘッド | 最小限 | +~160bytes |
結果¶
✅ 必須層 (Mandatory Layers)¶
レイヤー1:コマンド処理キュー(Protocol Command Queues)¶
- 対象: テキスト・バイナリープロトコルのコマンド受信
- 現在: カスタムリングバッファー(10要素)
- 課題:
- 手動インデックス管理(可読性低い)
queue_tail = (queue_tail + 1) % SIZEの複雑さ- 拡張時の信頼性懸念
改善案:
// 改善前:インデックス手動管理
if (queue_count >= TEXT_QUEUE_SIZE) return false;
text_command_queue[queue_tail] = cmd;
queue_tail = (queue_tail + 1) % TEXT_QUEUE_SIZE;
queue_count++;
// 改善後:FreeRTOS API(意図が明確)
return xQueueSend(text_command_queue, &cmd, 0) == pdTRUE;
- 導入方法:
ENABLE_FREERTOS_QUEUEフラグで段階的移行 - 見積: 2-3時間
- メモリ増加: +160 bytes RAM
レイヤー2:検出データ用バッファー(Detection Event Buffering)【最重要】¶
- 対象: 宇宙線検出イベントの即座なバッファー化
- 現在: 検出直後にシリアル送信(同期的・ブロッキング)
課題:
- シリアル送信が100-200ms程度のブロッキング
- ブロッキング中に検出タイミングの精度が失われる(+/-200msジッター)
- 高頻度検出時にイベント喪失のリスク
改善案:
- 検出直後(1-2ms)にタイムスタンプ記録 ← ✅ ここで時刻確定
- 検出データをキューに追加(非ブロッキング)
- シリアル送信は別のループ処理で非同期実行
loop():
├─ process_protocol_commands() [~1ms、非ブロッキング]
├─ wifi_update() [<10ms、非ブロッキング]
├─ detection_process() [~5ms、高速キューイング]
│ └─ detection_buffer_queue() [1-2ms] ✅ タイムスタンプ記録
│ → sensor_data_t + timestamp をキューに追加
│
└─ detection_buffer_dequeue_and_send()[~100-200ms、ブロッキング許容]
→ キューから1イベント取出
→ send_sensor_data() でシリアル出力
タイムスタンプ精度保証:
イベント発生: T=0ms
├─ cosmic_detector_read() [T+0-1ms]
├─ センサー読取 [T+1-4ms]
├─ buffer_queue()で記録 [T+4-5ms] ✅ タイムスタンプ確定!
│ → このタイムスタンプがイベントの確定時刻として記録
└─ send_sensor_data() [T+100-300ms] ← 送信遅延はタイムスタンプに影響しない(現在は受信側のPCでタイムスタンプをつけている)
メリット:
- 検出タイミング精度: +/-200ms → +/-1-2ms(100倍改善)
- シリアルブロッキング削減
-
高頻度検出対応
-
バッファサイズ: 20イベント(約2.3KB)
- 見積: 3-4時間
- メモリ増加: +500-600 bytes RAM
🔄 推奨層 (Recommended Layers)¶
レイヤー3:WiFi出力バッファー(WiFi Transmission Buffer)¶
- 対象: WiFi経由でのセンサーデータ送信
- 現在: v1.8.0で基本機能実装、ブロッキング送信と推定
課題:
- WiFi送信がシリアルより遅い(1秒以上の遅延可能性)
- 高頻度検出時にWiFi送信でメインループブロック
- イベント喪失リスク
改善案:
- 検出データを別キューに追加(WiFi用)
- WiFiバッファーから非同期で送信
-
シリアルとWiFiで独立したバッファリング
-
優先度: 🟡 中(WiFi機能ユーザーのみに関連)
- 見積: 2-3時間
- メモリ増加: +300-400 bytes RAM
- 依存: レイヤー2完了後
レイヤー4:WebSocketリアルタイムバッファー(WebSocket Real-time Stream)¶
- 対象: WiFi AP モードのWebサーバー経由でのリアルタイムストリーミング
- 現在: v1.8.0で基本的なWeb UI実装(静的ページ)
改善案:
- WebSocket接続ごとに送信バッファーを管理
- 複数クライアント対応(最大N接続)
-
イベントをJSON形式でストリーミング
-
優先度: 🟢 低(可視化・監視用、コア機能ではない)
- 見積: 4-5時間
- メモリ増加: +1-2KB RAM(クライアントごとに~200bytes)
- 依存: レイヤー2 + WiFi機能
- 将来: v2.0.0の「Webダッシュボード」へ向けた基盤
🔴 オプション層 (Optional Layers)¶
レイヤー5:ローカルストレージバッファー(Flash/LittleFS Logging)¶
- 対象: ESP32内蔵Flashへのイベントログ記録
- 用途:
- 通信障害時のログバックアップ
- 長期統計分析
-
オフライン動作時のデータ保存
-
優先度: 🟢 低(ストレージ容量・耐久性考慮が必要)
- 見積: 3-4時間
- メモリ増加: +100-200 bytes RAM
- 将来: v2.0.0の「データロギング」で検討
学習・知見¶
1. バッファリング層の優先順位は機能重要度に基づく¶
| 低コスト | 中コスト | 高コスト
------|------------|------------|----------
高効果 | レイヤー2 | レイヤー3 | レイヤー4
| (検出) | (WiFi) | (WebSocket)
------|------------|------------|----------
中効果 | レイヤー1 | レイヤー5 | -
| (Protocol) | (Storage) |
------|------------|------------|----------
- レイヤー1: 小さな改善(可読性)だが必須
- レイヤー2: 高い効果(精度100倍)で実装コスト中程度 ← 優先実装
- レイヤー3: レイヤー2の自然な拡張
- レイヤー4・5: 将来の基盤だが優先度低
2. タイムスタンプ戦略は層ごとに異なる¶
- レイヤー1: コマンド受信
- → キューイング時刻は不要(処理順序が重要)
- レイヤー2: 検出イベント【重要】
- → キューイング時刻が重要!
- → 検出時点で確定させる(送信遅延の影響を排除)
- →
uint64_t timestamp_usで1-2ms精度を保証 - レイヤー3: WiFi送信
- → レイヤー2のタイムスタンプを継承
- → WiFi送信遅延は記録済みのタイムスタンプに影響しない
- レイヤー4: WebSocket
- → クライアント単位のタイムスタンプ(イベント受信時刻)は別追跡
3. メモリ効率とバッファサイズのトレードオフ¶
| 層 | イベント数 | 1イベント | 合計 | 用途 |
|---|---|---|---|---|
| L1 | 10 | ~50b | 500b | コマンド |
| L2 | 20 | ~130b | 2.6KB | 検出 |
| L3 | 10 | ~150b | 1.5KB | WiFi |
| L4 | 5/クライアント | ~100b | 500b/クライアント | WS |
制約: ESP32 RAM 320KB中、現在使用率7.3% (23.4KB)
- L2追加後: ~7.8%(許容範囲内)
- L3追加後: ~8.2%(許容範囲内)
- L4×3クライアント: ~8.7%(許容範囲内)
全層実装してもRAM使用率 ~9% で安全。
4. バッファオーバーフロー時の動作戦略¶
// 戦略A: サイレント破棄(推奨、L2)
if (xQueueSend(queue, &item, 0) != pdTRUE) {
stats.overflows++; // 統計記録のみ
// イベント破棄(新しいイベント優先)
}
// 戦略B: 古いイベント破棄(L3、将来)
// → WiFiバッファでは古いデータを捨てる選択肢有
if (is_queue_full) {
xQueueReceive(queue, &old_item, 0); // 古い方を削除
xQueueSend(queue, &new_item, 0);
}
// 戦略C: エラー応答(L1)
if (xQueueSend(queue, &cmd, 0) != pdTRUE) {
send_error_response("Queue full"); // クライアント通知
}
次のステップ¶
実装ロードマップ(v1.9.0-v1.9.2)¶
v1.9.0: Protocol Queue FreeRTOS化¶
-
ENABLE_FREERTOS_QUEUEフラグを config.h に追加 - text_protocol.cpp, binary_protocol.cpp に条件分岐実装
- 両実装で同一動作確認(テスト)
- コミット:
refactor(protocol): add FreeRTOS queue implementation
v1.9.1: 検出データバッファリング【優先】¶
- detection_buffer.h/cpp モジュール作成
- main.cpp の detection_process/detection_send 分離
- テキストコマンド
GET_BUFFER_STATS追加(診断用) - ハードウェアテスト(タイムスタンプ精度確認)
- コミット:
feat(detection): add sensor data buffering
v1.9.2: WiFi バッファリング(条件付き)¶
- ENABLE_WIFI=1 の場合のみ WiFi バッファリング有効化
- detection_buffer.h の拡張
- WiFi送信非同期処理実装
- コミット:
feat(wifi): add WiFi transmission buffering
v2.0.0: WebSocket リアルタイムストリーム(将来計画)¶
- マルチクライアント対応
- リアルタイムデータ可視化基盤
設計決定(確定版)¶
✅ 確定事項¶
1. 検出データバッファサイズ (レイヤー2)¶
- バッファサイズ: 100イベント(ビルド時変更可能)
- ビルドフラグ:
-DDETECTION_BUFFER_SIZE=100でカスタマイズ可能 - メモリ: 100 × ~130bytes = ~13KB(現在RAM使用率で許容範囲)
- 根拠:
- 実測値(20イベント/秒)の5倍余裕を持たせる
- 高頻度検出時のバッファオーバーフロー対応
- ビルドフラグで環境ごとに調整可能
2. コマンド受信バッファサイズ (レイヤー1)¶
- バッファサイズ: 10コマンド(現在の仕様を継続)
- 共通仕様: テキストプロトコルとバイナリプロトコルで統一
- 実装: FreeRTOS Queueに統一予定(v1.9.0)
- 根拠:
- 実績: 現在のリングバッファーで十分動作確認済み
- コマンド処理は高速(送信ブロッキング時間内に処理可能)
3. タイムスタンプ戦略 (レイヤー2)¶
- 使用フラグ:
ENABLE_TIMESTAMP(既存フラグを継続活用) - 記録項目:
uptime_ms:millis()で取得(ブート時からの経過時間)duration_us:micros()で取得(直前イベントとの時間差)- 精度保証:
- ESP32タイマー解像度: 4us(仕様)
- 実装: イベント検出直後にマイクロ秒単位で記録
- 送信遅延(100-200ms)はタイムスタンプに影響しない
- 将来対応:
- RTC(リアルタイムクロック)タイムスタンプも同フラグで実装予定
- 既存の
ENABLE_TIMESTAMPフラグを拡張する設計
4. 待機事項(後回し)¶
- WiFiバッファーの設計詳細(レイヤー3、v1.9.2以降)
- クライアント最大数の決定
- バッファサイズ(イベント数)の決定
-
接続管理(自動切断タイムアウト)
-
WebSocket サポート(レイヤー4、v2.0.0計画)
- ESP32-WebSocketライブラリの選定
- メモリ制約下での複数クライアント対応可否
推奨事項(まとめ)¶
| 優先度 | 層 | 実装 | 効果 |
|---|---|---|---|
| 🔴 高 | L2 | v1.9.1 | タイムスタンプ精度100倍改善 |
| 🔴 高 | L1 | v1.9.0 | コード可読性向上 |
| 🟡 中 | L3 | v1.9.2 | WiFiブロッキング削減 |
| 🟢 低 | L4 | v2.0.0 | リアルタイム可視化基盤 |
| 🟢 低 | L5 | 将来 | データロギング |
プロトコルコマンド分析と共通化検討¶
現在のコマンド体系¶
テキストプロトコル(ENABLE_TEXT_PROTOCOL=1)¶
コマンド構造:
- 形式: テキスト形式(
"COMMAND arg1 arg2\n") - 解析:
text_parse()でコマンド名と引数を分割 - キュー:
text_command_t型で10要素のリングバッファー
主なコマンド例:
HELP # ヘルプ表示
STATUS # システム状態表示
GET_VERSION # バージョン取得
SET_POLL_COUNT <count> # 検出ポーリング数設定
GET_THRESHOLD <ch> # 閾値取得
SET_THRESHOLD <ch> <val> # 閾値設定
LED <ch|ALL> <ON|OFF> # LED制御
DEBUG <0|1> # デバッグモード設定
RESET # システムリセット
データ構造:
typedef struct {
char command_name[32]; // コマンド名("SET_THRESHOLD"など)
uint8_t arg_count; // 引数数(0-2)
char args[2][16]; // 引数文字列配列
char raw_buffer[64]; // 完全なコマンド行
} text_command_t;
バイナリプロトコル(ENABLE_TEXT_PROTOCOL=0)¶
コマンド構造:
- 形式: 3バイト形式(
[channel][data1][data2]\n) - 解析: バイナリ直接・構造化、テキスト変換なし
- キュー:
binary_command_t型で10要素のリングバッファー
コマンドフォーマット:
channel: DAC チャンネル(0-2)data1: 閾値エンコード上位ビットdata2: 閾値エンコード下位ビット\n: 行終端(同期用)
データ構造:
typedef struct {
uint8_t channel; // DAC チャンネル(1-3)
uint8_t data1; // エンコード済みデータ1
uint8_t data2; // エンコード済みデータ2
} binary_command_t;
共通化の可能性分析¶
✅ 共通化できる部分¶
| 項目 | 詳細 |
|---|---|
| キュー管理 | 両プロトコルとも 10 要素リングバッファー |
| エラーレスポンス | 両方とも JSONL 形式で応答 |
| タイムアウト処理 | 500ms タイムアウト(テキスト)と行終端待機(バイナリ) |
| オーバーフロー処理 | キュー満杯時の対応ロジック |
❌ 共通化困難な部分¶
| 項目 | テキスト | バイナリ | 理由 |
|---|---|---|---|
| 入力フォーマット | 可変長テキスト行 | 固定 3 バイト | 本質的に異なる |
| パーサー | text_parse() |
直接バイナリ | テキスト vs バイナリ処理 |
| コマンド表現 | 文字列("SET_THRESHOLD") | バイナリ構造体 | データ型が異なる |
| ハンドラーインターフェイス | CommandHandler 関数ポインター |
直接検証/実行 | 異なるシグネチャ |
| 引数解析 | char args[2][16](文字列) |
uint8_t data1/2(バイト) |
型が異なる |
推奨される共通化戦略¶
戦略1: 抽象レイヤー導入(推奨)¶
目標: キュー処理を共通化しつつ、パーサーは独立させる
// 共通コマンド型(タグ付きユニオン)
typedef enum {
CMD_TYPE_TEXT, // テキストコマンド
CMD_TYPE_BINARY, // バイナリコマンド
} command_type_t;
typedef struct {
command_type_t type;
union {
text_command_t text_cmd; // テキスト用
binary_command_t binary_cmd; // バイナリ用
} payload;
} universal_command_t;
// 共通キュー関数
bool command_queue_send(const universal_command_t* cmd);
bool command_queue_receive(universal_command_t* cmd);
メリット:
- キュー管理ロジックが統一される
- 両プロトコルで同一のバッファリング戦略を使用
- FreeRTOS Queueへの移行時に共通の型を使用可能
デメリット:
- タグ付きユニオンはメモリ効率が若干落ちる
- ハンドラー側で型チェックが必要
戦略2: 現状維持の最適化¶
概要: プロトコルごとに独立したキュー、FreeRTOS Queueで最適化
// テキストプロトコル
static QueueHandle_t text_command_queue;
xQueueCreate(10, sizeof(text_command_t));
// バイナリプロトコル
static QueueHandle_t binary_command_queue;
xQueueCreate(10, sizeof(binary_command_t));
// メイン処理で両方を処理
text_execute(); // テキスト用
binary_execute(); // バイナリ用
メリット:
- 変更最小限(現在の構造を維持)
- パーサーと実行ロジックが独立
- メモリ効率が最良
デメリット:
- キュー管理コードが重複
v1.9.0-v1.9.1 推奨実装フロー¶
v1.9.0: FreeRTOS Queue 導入(パーサーは共通化しない)¶
- 両プロトコルで独立した FreeRTOS Queue を導入
text_command_queue→xQueueCreate(10, sizeof(text_command_t))-
binary_command_queue→xQueueCreate(10, sizeof(binary_command_t)) -
キュー管理を統一
// text_protocol.cpp
static QueueHandle_t text_command_queue;
bool text_queue(text_command_t cmd) {
return xQueueSend(text_command_queue, &cmd, 0) == pdTRUE;
}
// binary_protocol.cpp
static QueueHandle_t binary_command_queue;
bool binary_queue(binary_command_t cmd) {
return xQueueSend(binary_command_queue, &cmd, 0) == pdTRUE;
}
-
メリット
-
リングバッファーのバグリスク低減
- 割り込ットセーフな処理
- 将来的な共通化の基盤
v1.9.1: 検出データバッファリング【優先】¶
検出データ用の新しいバッファーをFreeRTOS Queueで追加
// detection_buffer.h
typedef struct {
uint8_t signal1, signal2, signal3;
uint16_t adc_value;
float temp, pressure, humidity;
uint32_t uptime_ms;
uint64_t duration_us;
uint64_t timestamp_us; // イベント検出時刻(重要)
} detection_event_t;
static QueueHandle_t detection_queue;
xQueueCreate(100, sizeof(detection_event_t)); // 100 イベント
結論¶
推奨戦略: 戦略2(現状維持の最適化)
理由:
- テキストとバイナリは本質的に異なる(テキスト vs バイナリパーサー)
- 両プロトコルは相互排他(ENABLE_TEXT_PROTOCOL で一方のみ有効)
- コマンド実行ロジックも異なる(テキスト: ディスパッチテーブル、バイナリ: 固定形式)
- 無理な抽象化でコード複雑化のリスク
実装順序:
- v1.9.0: 各プロトコル独立でFreeRTOS Queue導入 ← マイナーな改善
- v1.9.1: 検出データバッファリング(新規キュー) ← 最優先(効果大)
- v1.9.2: WiFiバッファリング(条件付き)