Skip to content

Progress Log: マルチボード対応設計 - 接続形式別アーキテクチャ検討

Task Description

kurikintonsのマルチボード対応について、以下の3つの接続形式に対応可能な統一アーキテクチャについて検討しました。

  1. 1:1接続 - 1台のPC ← → 1台のOSECHI(現在の基本形態)
    • USB接続、単一のシリアルポート
  2. N:N接続 - N台のPC ← → N台のOSECHI(複数台のパソコン/OSECHIのセット同時計測)
    • 各PCが独立して単一ボードを管理
    • 1:1をスケールしたシンプルな構成
  3. 1:N接続 - 1台のPC ← → N台のOSECHI(1台で複数ボードを同時管理/計測)
    • USBハブ+複数シリアルポート接続
    • 複数ボードのデータを統合、時刻同期、相関分析

現状分析

既存実装の確認(v1.9.5時点)

  • ✅ シリアル通信: SSV/TSV/CSV/JSONLのフォーマット対応(v1.7.0以降)
  • ✅ タイムスタンプ: RTC機能(unix_timestamp)実装済み(v1.9.4)
  • ✅ WiFi機能: AP/STAモード基盤整備済み(v1.8.0)
  • ✅ コマンド処理: テキスト/バイナリプロトコル対応(v1.8.6)
  • ⏳ マルチスレッド化: デュアルコア分離予定(v2.0.0前半)

REFACTORING_ROADMAP での計画

  • Phase 1(v2.0.0中盤): USB接続ベースの複数台管理
    • ボード識別システム(デバイスID、シリアル番号)
    • データ統合フォーマット
    • タイムスタンプ同期(相対時刻)
    • PC側: Python + PySerial での統合スクリプト
  • Phase 2/3(v2.0.0後半以降): WiFi経由の同期通信
    • WiFi安定化検証が前提
    • ネットワーク同期機構(NTP等)
    • 中央サーバーへの集約

検討項目(決定が必要な設計ポイント)

1. デバイス識別方式

複数ボードを区別するための識別子の設計を検討した

案A: MAC アドレス(自動取得)

  • 利点:ESP32の固有番号、手動設定不要、業界標準
  • 欠点:英数字(e.g., A4:CF:12:AB:CD:EF)、人間が覚えにくい
  • 実装:
    • WiFi.macAddress() で自動取得可能
    • ファイル名などに使用する場合、コロンなしのlowercaseにする

案B: シリアルナンバー(手動割り当て)

  • 利点:短くて覚えやすい(e.g., OSECHI-001, OSECHI-002
  • 欠点:手動設定が必要、初期化フロー設計が必要
  • 実装:
    • SET_DEVICE_ID / GET_DEVICE_IDコマンドで操作
    • LittleFS(v2.0.0計画)または EEPROM に保存予定

案C: 両方実装(オプション)

  • MAC + シリアル名の併用
  • ユーザーが選択可能

2. タイムスタンプ同期戦略

複数ボードの検出イベント時刻の同期の設定を検討した

案A: 各ボードが独立RTC使用

  • 相対時刻での同期(PC側で時刻合わせ)
  • 利点:シンプル、ボード独立、PC側で調整可能
  • 欠点:長期運用時にドリフト発生可能
  • 用途:短期実験、開発テスト向け

案B: NTP(Network Time Protocol)連携

  • WiFi経由で外部タイムサーバーと同期
  • 利点:全ボード共通の絶対時刻保証
  • 欠点:WiFi環境が必須、外部依存
  • 用途:長期測定、本格運用向け

案C: PC側基準クロック(SET_TIME コマンド)

  • PCから各ボードにunix_timestampSET_TIMEで配信
  • 利点:PC側で一元管理、簡単
  • 欠点:PC ⇔ ボード間通信遅延の影響
  • 用途:1:N接続で有効

3. 実装ロードマップの優先度

推奨案(USBケーブル接続ベース):

  1. v2.0.0前半: マルチスレッド化(デュアルコア分離)✅ 既計画
  2. v2.0.0中盤 - Phase 1(USB/シリアル基盤)優先実装:
    • N:N接続対応(1:1をスケール)
    • 1:N接続対応(単一PCで複数ボード管理)
    • ボード識別システム(MACアドレス + device_id)
    • PC側統合スクリプト(Python + PySerial)
  3. v2.0.0後半:
    • LittleFS導入(device_id永続化)
    • WiFi安定化検証(Phase 2へ向けた準備)
  4. v2.1.0以降 - Phase 2(WiFi対応):
    • WiFi経由での複数ボード管理
    • NTP連携またはGNSSモジュール対応
    • 中央サーバーへの集約

設計方針

以下の方針で設計を進めます。

1. デバイス識別方式 → 案C(両方実装)

  • MACアドレス:自動取得、デバイス追跡用
  • シリアルナンバー:ユーザー設定可能、人間にわかりやすい識別
  • 実装:LittleFS(v2.0.0導入予定)またはEEPROMに保存
  • 出力形式:JSONレスポンスに両者を含める
{"device_id":"OSECHI-001", "mac_address":"A4:CF:12:AB:CD:EF", ...}

2. タイムスタンプ同期戦略 → 案A(相対時刻)+ 案B(GNSS将来対応)

  • 基本実装(Phase 1):各ボードが独立RTC使用

    • 相対時刻での同期(PC側で時刻合わせ)
    • v1.9.4で既実装のunix_timestampを活用
    • 短期実験、開発テスト向け
  • 将来対応(v2.1.0以降):GNSSモジュール(GPS等)追加検討

    • 絶対時刻取得が可能に
    • 外部依存性低減(WiFi不要)
    • 長期測定、本格運用向け

3. 実装スコープ → Phase 1(物理ケーブル/USB接続)優先

  • WiFi対応は「様子見」、基本は物理ケーブル(シリアル通信)
  • Phase 1(v2.0.0中盤):USB/シリアル基盤

    • ボード識別システム(MACアドレス + シリアルナンバー)
    • PC側統合スクリプト(Python + PySerial)
    • データ統合フォーマット(JSONL活用)
  • Phase 2/3(v2.1.0以降、WiFi安定化後):WiFi対応検討

    • NTP連携またはGNSSモジュール連携
    • ネットワーク同期機構
    • 中央サーバーへの集約

4. WiFi安定化 → 現在は様子見、後続フェーズで判断

  • v1.8.0での基盤実装は継続維持
  • 長期運用テストはPhase 1完了後に実施
  • Phase 3実装前に24時間以上の安定性テスト

責任分界の検討

kurikintonsと外部DAQツール(haniwers)Python DAQツール間での責任分界を検討した。

kurikintonsの責任

各OSECHIボード個別の機能

  • ✅ 宇宙線検出:物理的な検出、信号処理
  • ✅ センサー読み込み:BME280、ADC、GNSS(将来)など
  • ✅ デバイス識別:MACアドレス自動取得、シリアルナンバー管理
  • ✅ タイムスタンプ記録:RTC(unix_timestamp)、検出タイミング
  • ✅ シリアル出力:JSONL形式での検出イベント送信
  • ✅ コマンド処理:テキスト/バイナリプロトコル
  • ✅ LED制御、検出イベント管理
  • 検討中: ボード間の同期信号(将来、マスター/スレーブ方式の検討)

haniwersの責任

複数ボード統合・管理・解析

  • 複数シリアルポート監視(USBハブ経由)
  • 各ポートからのJSONL受信・パース
  • タイムスタンプ同期(各ボードのunix_timestamp比較・補正)
  • データロギング(CSV、JSON、HDF5等)
  • リアルタイム可視化(matplotlibplotlyなど)
  • マルチボード統計(検出率、相関分析等)
  • ファイル管理(出力ディレクトリ構成)
  • 設定管理(ボード初期化、パラメーター一括設定)

具体的な作業分担例

1:1接続(PC ← → OSECHI)

graph TB
    subgraph OSECHI["OSECHI"]
        direction LR
        detect["検出"]
        sensor["センサー読込"]
        output["JSONL送信"]
        detect --> sensor --> output
    end

    subgraph PC["PC-DAQ"]
        direction LR
        recv["受信"]
        log["ログ出力"]
        viz["可視化"]
        recv --> log --> viz
    end

    output -->|serial| recv

    style OSECHI fill:#e1f5ff
    style PC fill:#f3e5f5

N:N接続(PC ← → OSECHI ×N)

graph LR
    subgraph Setup1["セットアップ1"]
        direction LR
        OSECHI1["OSECHI-1"]
        OSECHI1 -->|serial| PCA["PC1-DAQ"]
    end

    subgraph Setup2["セットアップ2"]
        direction LR
        OSECHI2["OSECHI-2"]
        OSECHI2 -->|serial| PCB["PC2-DAQ"]
    end

    subgraph Setup3["セットアップ3"]
        direction LR
        OSECHI3["OSECHI-3"]
        OSECHI3 -->|serial| PCC["PC3-DAQ"]
    end

    style OSECHI1 fill:#e1f5ff
    style OSECHI2 fill:#e1f5ff
    style OSECHI3 fill:#e1f5ff
    style PCA fill:#f3e5f5
    style PCB fill:#f3e5f5
    style PCC fill:#f3e5f5

1:N接続(PC ← → OSECHI×N)

graph LR
    OSECHI1["OSECHI-1"]
    OSECHI2["OSECHI-2"]
    OSECHI3["OSECHI-3"]

    OSECHI1 -->|COM1| HUB["USB Hub"]
    OSECHI2 -->|COM2| HUB
    OSECHI3 -->|COM3| HUB

    HUB -->|serial| PC["PC-DAQ"]

    subgraph PC_Functions["PC"]
        monitor["複数ポート同時監視"]
        sync["タイムスタンプ同期"]
        stats["マルチボード統計"]
        agg["統合ログ"]
        monitor -.-> sync -.-> stats -.-> agg
    end

    style OSECHI1 fill:#e1f5ff
    style OSECHI2 fill:#e1f5ff
    style OSECHI3 fill:#e1f5ff
    style PC fill:#f3e5f5
    style PC_Functions fill:#f3e5f5

各ボードはまったく独立:

  • 各ボード内部の検出・計測は独立
  • PC側で時刻合わせ、相関分析を実施
  • ボード間通信なし(物理ケーブル接続のみ)

デバイス識別システム

MACアドレス取得方式の検討

案1(ファームウェア側)

kurikintonsからGET_MAC_ADDRESSコマンドでMACアドレスを返す

  • 利点:ファームウェアですでに取得可能、クエリで即座に確認可能
  • 欠点:毎回シリアル通信が必要、若干の遅延
  • 実装:WiFi.macAddress()をコマンドハンドラーに追加

案2(DAQ側)

esptoolで直接ファームウェアイメージから読み取り

  • 利点:ファームウェア変更不要、ボード識別が初期化フロー完全に外部で管理可能
  • 欠点:esptoolの追加依存、USB接続時のみ動作(シリアル接続時は不可)
  • 実装:Python DAQツールにesptool統合

推奨案:案1 + 案2の併用

  • 初期化フロー(セットアップ時):DAQ側でesptoolで一括取得、ボードマッピング表作成
  • ランタイム確認kurikintonsからGET_MAC_ADDRESSコマンドで確認
  • 利点:柔軟性が高い、メンテナンス容易

詳細設計

ファームウェア

  • GET_MAC_ADDRESSコマンド追加(テキストプロトコル)
  • JSONL出力にはシリアルナンバー(device_id)のみ含める
  • MACアドレスはオンデマンド取得(コマンド経由)

DAQ側

  • 初期化時にesptoolで各ボードのMACアドレスを取得
  • ボードマッピングテーブル作成(COM1 → MAC → OSECHI-001)
  • 出力ファイルにボード情報(MAC、device_id、COMポート等)を記録
  • ランタイムで必要ならGET_MAC_ADDRESSでクロスチェック

タイムスタンプ同期

  • ファームウェア:各ボード独立RTC、unix_timestamp出力
  • DAQ側:受信時刻との比較で同期確認、補正(1:N接続での時刻オフセット計算)

将来(GNSS対応時)

  • ファームウェア:GPS/NTP連携(v2.1.0以降、オプション)
  • DAQ側:外部タイムソース検証(GNSS信号確認時のみ使用)

シリアルナンバー管理の実装方針

  • ファームウェア:コマンドを追加
    • GET_DEVICE_ID - 現在のシリアルナンバーを返す
    • SET_DEVICE_ID <name> - シリアルナンバーを設定(例:SET_DEVICE_ID OSECHI-001
    • 出力フォーマット:STATUSコマンドのレスポンスにdevice_idを追加する
    • メモリ管理:
    • 現在:セッション中メモリに保持(電源切ると初期化)
    • 将来:LittleFS/EEPROMに永続保存(v2.0.0以降の検討事項)
  • DAQ側
    • 設定ファイル(TOML)からボード設定を読み込む
    • セットアップ時
    • esptoolで各ボードのMACアドレスを自動取得(セットアップ時)
    • 各ボードにSET_DEVICE_IDコマンドを送信して初期化
    • ランタイム:
    • セッション開始時にSTATUSコマンドで確認
    • 保存先のファイルを変える時はセッションを変更

利点

  • ファームウェアシンプル:RAMのみ使用、複雑な永続化ロジック不要
  • Python側で管理:設定ファイル一元管理、複数ボード一括初期化可能
  • デバイス交換対応:esptool + SET_DEVICE_IDで新ボードを素早く識別・設定可能
  • 将来拡張性:LittleFS導入時に永続化を追加可能

ファイル保存の仕様(Python DAQ側)

ディレクトリ構造

current_workspace/
└── 20251129_a4cf12abcdef/         # yyyymmdd_mac_address(小文字)
    ├── cosmic_2025-11-29_10h30m45s_a4cf12abcdef.jsonl
    ├── cosmic_2025-11-29_10h45m12s_a4cf12abcdef.jsonl
    └── metadata_device_a4cf12abcdef.jsonl
└── 20251129_b5d023bcdef0/         # 別ボードの例
    ├── cosmic_2025-11-29_11h00m00s_b5d023bcdef0.jsonl
    └── metadata_device_b5d023bcdef0.jsonl

ファイル命名規則

データファイル

cosmic_yyyy-mm-dd_HHhMMmSSs_{mac_address}.jsonl

  • 例: cosmic_2025-11-29_10h30m45s_a4cf12abcdef.jsonl
  • タイムスタンプを先に配置(時系列でソート)
  • MACアドレス(小文字、コロン除去)をファイル名に含める(一意性が高い)
  • 1セッション(コマンド実行~終了)ごとに新規ファイル作成
  • JSONL形式(各行が検出イベント)

メタデータファイル

  • metadata_session_{mac_address}.jsonl
  • metadata_device_{mac_address}.jsonl
  • metadata_environment_{mac_address}.jsonl
  • 各メタデータファイルにもMACアドレス(小文字)を含める
  • 複数ボードのメタデータを一括処理する際に識別容易

ボード識別(boardid)

採用方式: MACアドレス(小文字、コロン除去)

  • 形式: a4cf12abcdef(小文字12文字、コロン除去)
  • ディレクトリ: 20251129_a4cf12abcdef/
  • ファイル名: cosmic_2025-11-29_10h30m45s_a4cf12abcdef.jsonl
  • メタデータ: metadata_device_a4cf12abcdef.jsonl

選定理由:

  • 一意性: ESP32の固有番号で重複なし
  • 自動取得: esptoolで自動取得可能、手動設定不要
  • ファイル名に含める: ボードを人間が確実に識別可能
  • 小文字統一: ファイルシステムの互換性向上

セッション管理

  1. コマンド実行時にcurrent workspaceを基準に起動
  2. ボード設定ファイルから各MACアドレスを取得
  3. 各MACアドレスについてサブディレクトリを作成(yyyymmdd_mac_address 形式)
  4. データファイルを時系列+MACアドレス付きで該当ディレクトリに保存(cosmic_yyyy-mm-dd_HHhMMmSSs_mac_address.jsonl
  5. セッション終了時にメタデータを記録(MACアドレス付きファイル名)

複数ボード同時記録例(1:N接続)

current_workspace/
├── config.json
├── 20251129_a4cf12abcdef/
│   ├── cosmic_2025-11-29_10h30m45s_a4cf12abcdef.jsonl (Board-A data)
│   ├── cosmic_2025-11-29_10h31m02s_a4cf12abcdef.jsonl (Board-A data)
│   ├── metadata_device_a4cf12abcdef.jsonl
│   └── metadata_session_a4cf12abcdef.jsonl
├── 20251129_b5d023bcdef0/
│   ├── cosmic_2025-11-29_10h30m45s_b5d023bcdef0.jsonl (Board-B data)
│   ├── cosmic_2025-11-29_10h31m15s_b5d023bcdef0.jsonl (Board-B data)
│   ├── metadata_device_b5d023bcdef0.jsonl
│   └── metadata_session_b5d023bcdef0.jsonl
└── 20251129_c6e134cdef01/
    ├── cosmic_2025-11-29_10h30m50s_c6e134cdef01.jsonl (Board-C data)
    ├── cosmic_2025-11-29_10h31m20s_c6e134cdef01.jsonl (Board-C data)
    ├── metadata_device_c6e134cdef01.jsonl
    └── metadata_session_c6e134cdef01.jsonl

メタデータ記録戦略

記録タイミング:

  • セッション開始時: STATUSコマンド実行直後にメタデータを記録
  • セッション終了時: メタデータは保存不可(呼び出し時点で終了)、開始時点の情報のみ

ファイル分割方式(推奨):

各メタデータ種類ごとに別ファイルを作成して、JSONL行の形式を統一。解析時にpandas/polarsで一括読み込み可能:

yyyymmdd_mac_address/
├── cosmic_yyyy-mm-dd_HHhMMmSSs_mac_address.jsonl  # 検出イベント(複数セッション、時系列ソート)
├── metadata_session_mac_address.jsonl              # セッション開始情報
├── metadata_device_mac_address.jsonl               # ボード情報
└── metadata_environment_mac_address.jsonl          # 環境情報

各メタデータファイルの仕様:

  1. metadata_session_{mac_address}.jsonl(セッション開始時に記録):
{"record_type":"session_start", "timestamp":"2025-11-29T10:30:45.123Z", "data_file":"cosmic_2025-11-29_10h30m45s_a4cf12abcdef.jsonl"}
  • 各行の形式統一:全てrecord_typetimestampdata_fileを含む
  • MACアドレスを含めてファイル識別容易

  • metadata_device_{mac_address}.jsonl(STATUSコマンド結果から):

{"record_type":"device_status", "device_id":"OSECHI-001", "mac_address":"a4:cf:12:ab:cd:ef", "com_port":"COM3"}
  • デバイス識別情報を統一形式で記録

  • metadata_environment_{mac_address}.jsonl(ツール側の環境情報):

{"record_type":"environment", "python_version":"3.11.0", "tool_version":"1.0.0", "stream_format":"JSONL"}
  • ツール環境を統一形式で記録

利点:

  • 形式統一: 各ファイル内で行の形式が一定(pandas/polarsで直接読み込み可能)
  • スケーラビリティ: 複数セッション、複数ボードのデータを追記形式で保存
  • 解析容易性: ファイルごとにDataFrameに変換して処理可能
df_cosmic = pd.read_json('cosmic_2025-11-29_10h30m45s.jsonl', lines=True)
df_device = pd.read_json('metadata_device.jsonl', lines=True)
df_session = pd.read_json('metadata_session.jsonl', lines=True)
  • record_type保持: ファイル分割後もrecord_typeフィールドで追加情報を記録
  • 統合解析容易: sessionファイルでdata_fileを取得 → 対応する検出データを読み込み → deviceファイルで補足情報を参照

デメリット回避:

  • ファイル数が増えてもメタデータファイルは小さい(セッション開始時のみ追記)
  • cosmos_*.jsonlは検出データのみで行の形式が統一

次のステップ

  1. ファームウェア実装仕様の詳細化(v2.0.0中盤向け)

    • GET_DEVICE_IDSET_DEVICE_IDコマンド実装
    • STATUSレスポンスにdevice_id追加
    • JSONL出力にdevice_idフィールド追加
    • メモリ管理:静的バッファーまたは動的割り当て
  2. Python DAQツール仕様の策定

    • 設定ファイルスキーマ定義(JSON)
    • esptool統合方法
    • 複数シリアルポート同時管理
    • タイムスタンプ同期アルゴリズム
    • データ保存形式
  3. Phase 1実装順序

    • ファームウェア優先(デバイスID、JSONL拡張)
    • Python側の整備
    • 統合テスト(1:1 → 1:N)
  4. 将来的な検討事項

    • LittleFS導入(v2.0.0後半)
    • device_idの永続化
    • GNSS対応(v2.1.0向け)

リソース

  • REFACTORING_ROADMAP.md セクション 2「マルチボード対応」
  • 既実装: RTC(v1.9.4)、WiFi基盤(v1.8.0)、シリアルフォーマット統一(v1.7.0)