# 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` コマンド

```bash
# 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モジュールとして統合**: サブプロセス不要で高速・安定動作
  ```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 データクラス

```python
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 に統合
  ```python
  # 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 をデフォルトに設定
  ```python
  # v1.5.0以前
  baudrate: int = 9600  # 非標準値

  # v1.6.0
  baudrate: int = 115200  # OSECHI標準値
  ```

- **妥当性検証**: min=1200 で異常な低速値を防止

### DeviceOptions の一貫性

- **中央集権化**: すべてのコマンドで `DeviceOptions` を使用
  ```python
  # 統一されたデバイスオプション
  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"` を追加
  ```yaml
  # 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除外
  ```

- **実行時間短縮**: 時間がかかる・環境依存のテストをスキップ
  - ハードウェア依存テスト
  - 統計的テスト（確率的に失敗する可能性）
  - ベンチマークテスト

### テストマーカーの活用

```python
# ローカル環境でのみ実行すべきテスト
@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診断ロジックのテスト
  ```python
  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フラッシュ診断
  ```toml
  [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
```

### 診断プロセス

```bash
# 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  ← 原因特定
```

### 解決

```bash
# 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` は通信失敗を示す明確な指標

## 📦 インストール

```bash
pip install haniwers==1.6.0
```

または最新版：

```bash
pip install --upgrade haniwers
```

## 🔗 関連リンク

- [完全な変更ログ](../../CHANGELOG.md#160-2025-11-02)
- [ESP32トラブルシューティングガイド](../progress/entries/2025-11-02-esp32-flash-troubleshooting.md)
- [実装の詳細](../../src/haniwers/v1/cli/port.py)
- [ドキュメント](https://qumasan.gitlab.io/haniwers/docs/)
- [Issues](https://gitlab.com/qumasan/haniwers/-/issues)
- [リリースタグ](https://gitlab.com/qumasan/haniwers/-/tags/1.6.0)

---

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