Skip to content

進捗ログ:複数レイヤーでのバッファリング設計

タスク説明

Kurikintonsファームウェアのアーキテクチャを分析し、パフォーマンス向上・ブロッキング削減・リアルタイム精度改善が期待できるバッファリング層を特定。包括的なバッファリング戦略を設計した。

目標

  1. ブロッキングポイントの排除 → シリアル送信・ネットワークI/O
  2. タイムスタンプ精度の維持 → マイクロ秒精度の検出時刻
  3. ジッター削減 → 検出から出力までのレイテンシ変動を最小化
  4. スケーラビリティ → 高頻度検出でもバッファオーバーフロー対応
  5. 診断機能 → バッファー統計・オーバーフロー追跡

前提知識

バッファリング

バッファー(buffer)は「一時的なデータ保管庫」のようなものです。 ディスクではなく、メモリ上に確保されるため、高速にアクセスできます。

バッファリングの効果

  1. ブロッキング削減:
  2. USB送信には時間がかかる処理(100ms程度)
  3. バッファーを利用すると、データをリアルタイム取得しながら、データ送信できます
  4. スループット向上: 並行処理で全体を高速化
  5. 例)センサー読取(1ms)と送信(100ms)を同時処理
  6. タイムスタンプ精度: イベント発生時刻を確定できる
  7. 例)イベント発生 → 即座に時刻記録 → 後で送信(送信遅延に影響されない)

バッファーオーバーフロー

バッファーが満杯になることを「バッファーオーバーフロー」と呼びます。 オーバーフローしたデータをどのように処理するかは、設計時に検討が必要です。

バッファ: [イベント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. 検出直後(1-2ms)にタイムスタンプ記録 ← ✅ ここで時刻確定
  2. 検出データをキューに追加(非ブロッキング)
  3. シリアル送信は別のループ処理で非同期実行
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

レイヤー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 導入(パーサーは共通化しない)

  1. 両プロトコルで独立した FreeRTOS Queue を導入
  2. text_command_queuexQueueCreate(10, sizeof(text_command_t))
  3. binary_command_queuexQueueCreate(10, sizeof(binary_command_t))

  4. キュー管理を統一

// 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;
}
  1. メリット

  2. リングバッファーのバグリスク低減

  3. 割り込ットセーフな処理
  4. 将来的な共通化の基盤

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(現状維持の最適化)

理由:

  1. テキストとバイナリは本質的に異なる(テキスト vs バイナリパーサー)
  2. 両プロトコルは相互排他(ENABLE_TEXT_PROTOCOL で一方のみ有効)
  3. コマンド実行ロジックも異なる(テキスト: ディスパッチテーブル、バイナリ: 固定形式)
  4. 無理な抽象化でコード複雑化のリスク

実装順序:

  • v1.9.0: 各プロトコル独立でFreeRTOS Queue導入 ← マイナーな改善
  • v1.9.1: 検出データバッファリング(新規キュー) ← 最優先(効果大)
  • v1.9.2: WiFiバッファリング(条件付き)