Skip to content

Progress Log:データ送受信プロトコル概要

タスク説明

既存のデータi送受信プロトコル(バイナリープロトコルとテキストプロトコル)を分析し、現在の実装を理解してプロトコル拡張の準備を進めました。

以下の内容について整理しました。

  1. 現在使用されているデータ形式
  2. プロトコルの選択とコンパイル方法
  3. 検出イベント時に送信されるセンサーデータ
  4. 相互排他性の制約

現在のプロトコルアーキテクチャ

kurikintonsは、相互排他的な2つのシリアルプロトコルを実装しています。 それぞれ バイナリープロトコルテキストプロトコルと 呼びます。

どちらのプロトコルを利用するかは、ビルド時のフラグ(ENABLE_TEXT_COMMANDS)で選択できます。 現在、 本番環境ではバイナリープロトコル、 開発環境ではテキストプロトコル、 がデフォルトになっています。

1. バイナリープロトコル

  • 目的: バイナリコマンドでDAC閾値を直接制御
  • フラグ: ENABLE_TEXT_COMMANDS=0
  • デフォルト: 本番環境

コマンド形式

  • 形式:<ch> <val1> <val2>
  • バイト数:1コマンド3バイト
  • 詳細
  • byte[0]:チャンネルID(1-3)
  • byte[1]:第1データバイト
  • byte[2]:第2データバイト
  • 特殊ケース
  • val1 == 0xFFの場合、エラー応答として「dame」を送信

実装

  • 受信: serial_read_command()
  • 送信: serial_send_response()
  • レスポンス形式: バイナリエコー(ch、val1、val2)の後、DAC実行
  • 安定性機能
  • バッファー同期による部分コマンド受信防止
  • 不完全コマンドのタイムアウト処理
  • 堅牢受信のための再試行メカニズム
  • コマンド処理後の入力バッファフラッシュ

重要:PCソフトウェア側でのエンコーディング処理が必要

バイナリープロトコルでは、ESP32が受け取ったval1val2のバイナリー形式のデータをそのままDACに送信します。 そのため、PCソフトウェア側であらかじめエンコード処理を実施してから、設定したい値をバイナリー値で送信する必要があります。

エンコーディング式

12ビット(0-4095)の閾値thresholdをバイナリープロトコルに変換式です。

  1. ch = 1 (or 2 or 3): そのまま
  2. byte1 = 0x10 + (threshold >> 6): 閾値の上位6ビットを取得し、プロトコルヘッダー(0x10)を追加
  3. byte2 = (threshold << 2) & 0xFF: 閾値の下位6ビットを取得し、8ビット(0xFF)マスクを適用

計算例:

  • threshold = 1234byte1=0x23byte2=0x4A
  • threshold = 0byte1=0x10byte2=0x00
  • threshold = 4095byte1=0x3Fbyte2=0xFC

2. テキストプロトコル

  • 目的: 人間が読めるテキスト形式でDAC閾値を制御
  • フラグ: ENABLE_TEXT_COMMANDS=1
  • デフォルト: 開発環境(将来的にバイナリープロトコルを置き換え)

コマンド形式

  • 形式:COMMAND arg1 arg2
  • 終端記号:改行(\n または \r\n
  • タイムアウト:500ms(シリアルモニター互換)
  • キュー:最大10項目(満杯時はエラー)

利用可能なコマンド

  • [H] HELP: 利用可能なコマンドをすべて表示
  • [S] STATUS: システム構成とファームウェアバージョンを表示
  • [V] GET_VERSION: ファームウェアバージョンを表示
  • [C] SET_POLL_COUNT <count>: ポーリング回数を設定(1-1000、デフォルト100)
  • [G] GET_THRESHOLD <ch>: 指定したチャンネルのスレッショルド値を取得
  • [T] SET_THRESHOLD <ch> <val>: 指定したチャンネルにスレッショルド値を設定(0-4095)
  • [I] SET_INTERVAL <ms>: デバッグモードでの測定間隔をミリ秒で設定
  • [L] LED <ch|ALL> <ON|OFF>: LEDを手動で制御
  • [R] RESET: デフォルトの設定にリセット

ユーザーの利便性を考えて、コマンド名は大文字と小文字を区別しないようにしました。 また、それぞれのコマンドは短い1文字エイリアスをサポートしています。

実装

  • 関数:process_text_commands()process_queued_text_command()
  • キュー処理:コマンドをFIFOにキュー、1ループに1個処理
  • バイナリープロトコル関数:ビルドから完全に除外される
  • レスポンス形式: 改行で終わる人間が読める形式

PCソフトウェア側でのエンコーディング処理が不要

テキストプロトコルでは、kurikintonsの内部でエンコードしてからDAC制御を実行します。 SET_THRESHOLDコマンドで12ビット閾値値(0-4095)をそのまま指定してOKです。

エンコーディング関数

  • ファイル: src/spi_control.cpp
  • 関数:encode_dac_threshold()
  • (当たり前ですが)エンコーディング式はバイナリープロトコルと同じです

センサーデータ出力(両プロトコル共通)

宇宙線検出イベント発生時(detection.detected == true)にデータを送信します。 データにはBME280センサーのデータも追加されます。

出力形式はスペース区切り値(改行で終了)です。

signal1 signal2 signal3 adcValue temperature pressure humidity

100 85 92 2048 25.35 1013.25 45.67

データ型と範囲:

フィールド 範囲 ソース
signal1 int 0-100 GPIO12検出カウント(100ポーリング)
signal2 int 0-100 GPIO19検出カウント(100ポーリング)
signal3 int 0-100 GPIO27検出カウント(100ポーリング)
adcValue int 0-4095 GPIO32からADC(signal1 > 0時のみ)
temperature float 可変 BME280センサー(℃)
pressure float 可変 BME280センサー(Pa)
humidity float 可変 BME280センサー(%)

条件付きロジック:

  • signal1 == 0 の場合、ADC値は強制的に0に設定(ハードウェア制約)
  • センサー読み取りは検出イベント時のみ(イベント駆動)
  • 出力は検出時のみ、周期的出力なし

コンパイル時の相互排他性

どうして2つのプロトコルが存在するのか?

2つのプロトコルが存在するのは開発経緯による理由です。 kurikintonsのプロトタイプ実装では、バイナリープロトコルのみでした。 そこに、ユーザーの利便性を考えてテキストプロトコルを追加しました。

当初は2つのプロトコルを共存させようとしましたが、 改行処理がうまくいかなかったため、ビルドフラグを追加し、 独立したプロトコルとして実装することにしました。

ビルド方法

  • 開発プロファイル(task dev:build):ENABLE_TEXT_COMMANDS=1(テキストプロトコル)
  • 本番プロファイル(task prod:build):ENABLE_TEXT_COMMANDS=0(バイナリープロトコル)
  • ビルドフラグ:platformio.ini-DENABLE_TEXT_COMMANDS=0/1 に設定

シリアル通信定数

  • ボーレート: 115200 bps(すべてのビルドで固定)
  • 最大テキストコマンド長: 64バイト
  • 最大コマンド引数数: 1コマンド2個
  • コマンドキュー容量: 最大10項目
  • コマンドタイムアウト: 500ms(部分コマンド受信)
  • 検出サイクルのポーリング数: 100(DETECT_POLL_COUNT で設定可能)

Learnings

  1. 相互排他性が強い: プロトコルはコンパイル時に完全に分離される。無効なプロトコルの関数はバイナリから完全に除外される。メモリ効率は良いが、プロトコル切り替えには再コンパイルが必要。

  2. データ出力はイベント駆動: センサーデータ送信は宇宙線検出時のみ。周期的なハートビートやステータス出力はない。これは周期ベンチテストモードとは異なる。

  3. ADCフィルタリングロジックはハードウェア依存: ADCがsignal1 > 0時のみ有効なのは、元のハードウェア設計がADC入力をチャンネル1に結合しているため。この制約は adc_read_with_channel_filter() 関数で実装されている。

  4. テキストコマンドキューは非同期: コマンドは10項目FIFOにキューイングされ、1ループに1個処理される。キューが満杯の場合、受信コマンドは拒否される。これはシリアルI/Oのブロッキングを防止する。

  5. バッファー管理が重要: バイナリープロトコルには部分/破損コマンド処理のための広範なバッファー同期とタイムアウト処理がある。 テキストコマンドはシリアルモニター互換性のために500msタイムアウトを使用する。


次のステップ

  1. プロトコル拡張の検討:
  2. センサーデータ出力にタイムスタンプを追加(時間解像度向上)
  3. メッセージ型インジケーター追加(例:D: 検出、E: エラー)
  4. バイナリプロトコルにチェックサム/CRC追加(エラー検出)
  5. プロトコルヘッダーにバージョン番号追加(前方互換性)

  6. テスト・検証:

  7. 両プロトコルのテストベクトル文書化
  8. PC側パーサーのリファレンス実装
  9. 本番ビルドで両プロトコルが正しく動作することを検証

  10. 今後の開発:

  11. 相互排他性が限定的になった場合、実行時に両プロトコルをサポートするハイブリッドプロトコルを検討
  12. 各プロトコルのパフォーマンス特性を文書化
  13. ストリーミング検出モード検討(送信あたり複数イベント)