v1.6.0 (2025-11-02)#

このリリースは、ESP32フラッシュ診断機能の追加と、CLIオプションの一貫性向上に焦点を当てた重要なアップデートです。 OSECHI検出器のESP32マイコンへのファームウェア書き込みトラブルシューティングを大幅に改善し、開発者とユーザーの両方にとってより使いやすいシステムを実現しました。

✨ v1.6.0 での改善点#

このマイルストーンは、以下を実現します。

  • ESP32フラッシュ診断機能: esptoolを統合した包括的な診断ツール

  • 自動異常検出: フラッシュチップ通信失敗の自動検出と対処法提案

  • CLIオプション統合: 重複オプション削除と一貫性のある設計

  • ボーレート設定改善: 標準値(115200)とセンシブルなデフォルト値の設定

  • CI/CD最適化: ローカル専用テストのスキップによる実行時間短縮

🎯 User Story 1 - ESP32フラッシュ診断機能#

新機能: port diagnose コマンド#

# ESP32フラッシュチップの包括的な診断
haniwers-v1 port diagnose /dev/ttyUSB0

# フラッシュ情報のみ表示
haniwers-v1 port diagnose /dev/ttyUSB0 --flash-id

# チップ情報のみ表示
haniwers-v1 port diagnose /dev/ttyUSB0 --chip-id

# カスタムボーレート指定
haniwers-v1 port diagnose COM3 --baudrate 9600

esptool統合の特徴#

  • Pythonモジュールとして統合: サブプロセス不要で高速・安定動作

    import esptool
    
    # ESPチップに接続
    esp = esptool.detect_chip(port=device, baud=baudrate, connect_attempts=3)
    
    # スタブフラッシャーをアップロード(高速・安定化)
    esp = esp.run_stub()
    
    # フラッシュ情報を取得
    esptool.flash_id(esp)
    
  • 自動異常検出: ff/ffff 検出時に電源状態を診断

    ✗ Flash chip communication failed
    
    Possible causes:
      - OSECHI power switch is OFF
      - Device not powered
      - Hardware connection issue
    
    Flash voltage is 1.8V (should be 3.3V)
      - Check OSECHI power switch
      - GPIO12 may be floating
    
  • 選択的情報表示: 必要な情報のみを表示

    • --flash-id: Manufacturer, Device, Size, Voltage

    • --chip-id: Chip type, Features, Crystal, MAC address

    • --summary: eFuse summary(今後実装予定)

FlashInfo データクラス#

from haniwers.v1.cli.port import FlashInfo

# フラッシュ情報を型安全に管理
info = FlashInfo(
    manufacturer="20",      # XMC製造ID
    device="4017",          # デバイスID
    flash_size="8MB",       # フラッシュサイズ
    flash_voltage="3.3V",   # フラッシュ電圧
    chip_type="ESP32-D0WD-V3",
    crystal="40MHz",
    mac_address="24:6f:28:xx:xx:xx"
)

# ヘルスチェック
if info.is_healthy():
    print("✓ Flash chip communication successful")
else:
    print(info.get_diagnosis())

🎯 User Story 2 - CLIオプションの統合と一貫性向上#

SuppressOptions の統合#

  • 重複削除: --suppress-threshold を ThresholdOptions に統合

    # v1.5.0以前: 3箇所で定義
    # - options.py: SuppressOptions
    # - options.py: ThresholdOptions
    # - 各CLIコマンド: 個別定義
    
    # v1.6.0: 単一箇所で定義
    @dataclass
    class ThresholdOptions:
        """Threshold management options."""
        suppress_threshold: int = typer.Option(
            None,
            help="Suppress events below this threshold (1-1023)",
        )
    
  • 一貫性の向上: すべてのコマンドで同じオプション名・説明を使用

Baudrate の標準化#

  • 標準値の採用: 115200 bps をデフォルトに設定

    # v1.5.0以前
    baudrate: int = 9600  # 非標準値
    
    # v1.6.0
    baudrate: int = 115200  # OSECHI標準値
    
  • 妥当性検証: min=1200 で異常な低速値を防止

DeviceOptions の一貫性#

  • 中央集権化: すべてのコマンドで DeviceOptions を使用

    # 統一されたデバイスオプション
    from haniwers.v1.cli.options import DeviceOptions
    
    @app.command()
    def test(
        device: str = typer.Argument(...),
        device_opts: DeviceOptions = typer.Option(...),
    ):
        # device_opts.baudrate, device_opts.timeout が利用可能
    

🎯 User Story 3 - CI/CD最適化とテスト改善#

ローカル専用テストのスキップ#

  • .gitlab-ci.yml 修正: pytest-full ジョブで -m "not local" を追加

    # v1.5.0以前
    pytest-full:
      script:
        - poetry run pytest tests/v1/ -m "" --cov  # すべて実行
    
    # v1.6.0
    pytest-full:
      script:
        - poetry run pytest tests/v1/ -m "not local" --cov  # local除外
    
  • 実行時間短縮: 時間がかかる・環境依存のテストをスキップ

    • ハードウェア依存テスト

    • 統計的テスト(確率的に失敗する可能性)

    • ベンチマークテスト

テストマーカーの活用#

# ローカル環境でのみ実行すべきテスト
@pytest.mark.local
def test_missing_port_error():
    """Test error when port is not specified."""
    # 環境依存のConfigLoader動作をテスト

🧪 テスト強化#

ユニットテストの追加#

テストスイート

状態

詳細

FlashInfo dataclass

✅ 100%

正常時・異常時の診断メッセージ

port diagnose コマンド

✅ 統合テスト

esptool実行フロー検証

v1 ユニットテスト全体

✅ パス

346 passed, 9 skipped

コード品質

✅ Pass

ruff format, pre-commit OK

テストカバレッジ

✅ 54%

port.py: 58%(診断機能含む)

新規テストファイル#

  • tests/v1/unit/cli/port/test_flash_info.py: FlashInfo診断ロジックのテスト

    def test_flash_info_healthy():
        """Test diagnosis message for healthy flash chip."""
        info = FlashInfo(
            manufacturer="20",
            device="4017",
            flash_size="8MB",
            flash_voltage="3.3V",
        )
        assert info.is_healthy() is True
    
    def test_flash_info_unhealthy():
        """Test diagnosis message for unhealthy flash chip."""
        info = FlashInfo(
            manufacturer="ff",
            device="ffff",
            flash_size="Unknown",
            flash_voltage="1.8V",
        )
        assert info.is_healthy() is False
        assert "OSECHI power switch is OFF" in info.get_diagnosis()
    

🔧 内部実装の改善#

Dependencies#

  • esptool >= 4.8.1: ESP32フラッシュ診断

    [tool.poetry.dependencies]
    esptool = ">=4.8.1"
    

進捗ログの充実#

  • トラブルシューティング記録: docs/progress/entries/2025-11-02-esp32-flash-troubleshooting.md

    • 根本原因分析: OSECHI電源スイッチOFF → GPIO12フローティング

    • ESP32フラッシュ電圧判定メカニズムの詳細解説

    • Arduino IDE設定の確認結果(QIO/DIO両対応)

    • GPIO12とフラッシュ電圧の関係

    • 診断コマンドの使用例

    • 今後の予防策チェックリスト

コード品質の向上#

  • 統一されたオプション定義: DRY原則に準拠

  • 型安全性の向上: dataclass活用とOptional型の適切な使用

  • ドキュメンテーション強化: docstringの充実とコード内コメント

📊 主要な統計#

変更内容#

  • 新規ファイル: 2ファイル

    • tests/v1/unit/cli/port/test_flash_info.py (46 lines)

    • docs/progress/entries/2025-11-02-esp32-flash-troubleshooting.md (210 lines)

  • 変更ファイル: 3ファイル

    • pyproject.toml (+1 dependency)

    • poetry.lock (+esptool and dependencies)

    • src/haniwers/v1/cli/port.py (+185 lines)

    • .gitlab-ci.yml (pytest-full修正)

  • コミット数: 3 commits

    • feat(port): add ESP32 flash diagnostics with esptool integration

    • ci: skip local-marked tests in pytest-full job

    • bump: version 1.5.0 1.6.0

🛠️ トラブルシューティング実例#

問題: ESP32ファームウェア書き込み失敗#

TypeError: argument of type 'NoneType' is not iterable
WARNING: Failed to communicate with the flash chip
Packet content transfer stopped

診断プロセス#

# 1. フラッシュ情報取得
$ haniwers-v1 port diagnose /dev/cu.usbserial-110

# 異常値検出
Manufacturer: ff
Device: ffff
Detected flash size: Unknown
Flash voltage: 1.8V

✗ Flash chip communication failed
Possible causes:
  - OSECHI power switch is OFF   原因特定

解決#

# OSECHIスイッチをONにする
$ haniwers-v1 port diagnose /dev/cu.usbserial-110

# 正常値確認
Manufacturer: 20 (XMC)
Device: 4017
Detected flash size: 8MB
Flash voltage: 3.3V   正常化

✓ Flash chip communication successful

学習事項#

  • GPIO12の役割: ESP32起動時にフラッシュ電圧(1.8V/3.3V)を判定

  • 電源の重要性: OSECHI電源OFFではGPIO12がフローティング状態

  • 診断の有効性: ff/ffff は通信失敗を示す明確な指標

📦 インストール#

pip install haniwers==1.6.0

または最新版:

pip install --upgrade haniwers

🔗 関連リンク#


リリース担当: shotakaha リリース日: 2025-11-02 バージョン: 1.6.0 (MINOR update) 実装期間: 1日 (2025-11-02)