PPK2 MCP Server
# PPK2 MCP Server
Nordic Semiconductor **Power Profiler Kit II (PPK2)** 的 Model Context Protocol (MCP) 服务器。
让 LLM 直接完成功耗剖析:控制硬件采集电流(500 nA–2000 mA @ 100 kSa/s + 8 路逻辑口)、
给 DUT 供电(源表 0.8–5 V),采集数据落盘 npz,再做离线分析——统计、功耗分段、
电池续航估算,回答"睡眠电流多少 / 活动占空比多少 / 一节 CR2032 能跑多久"。
- 开发方式:**DDD**(四层 + 依赖倒置),见 `docs/PLAN.md`
- 协议逆向与真机验证笔记:`docs/NOTES.md`(真机 18 项自检 0 FAIL)
- **19 个 MCP 工具**:连接/配置/采集落盘/离线分析
- **采样数据只落盘**(npz),工具返回摘要 + 包络 + 路径,绝不内联数组
- 协议层自研(规避 GPLv2 的 ppk2-api),对照 Nordic 官方开源 app 逐条验证,
增加了官方 app 同款的**模 64 计数器丢样检测**(IRNAS 库没有)
## 快速开始
```bash
cd /Users/andylos/mcp_gen/PPK2
python3 -m venv .venv
.venv/bin/pip install -e . pytest
.venv/bin/python -m pytest tests/ # 56 个测试:mock 设备全栈 + MCP 协议级
.venv/bin/python scripts/probe_device.py # 真机端到端自检(18 项)
PPK2_MOCK=1 .venv/bin/ppk2-mcp # 无硬件演示(mock 设备)
```
PPK2 经 USB CDC 串口连接(免驱);插入后绿灯亮即可,无需其他配置。
macOS 上会出现两个 `cu.usbmodem*` 口,服务器自动探测 0x19 应答识别命令口。
## 配置(环境变量)
| 变量 | 默认 | 说明 |
|---|---|---|
| `PPK2_PORT` | 自动发现 | 指定串口(跳过探测) |
| `PPK2_DATA_DIR` | `./data` | 采集 npz 保存目录 |
| `PPK2_CAPTURE_MAX_S` | `60` | `capture` 工具时长上限(长采集用 start/stop) |
| `PPK2_TIMEOUT` | `15` | 普通操作超时(秒) |
| `PPK2_MOCK` | 关 | `1` = 内存模拟设备(无硬件演示/CI) |
## 接入 MCP 客户端
ZCode(`.zcode/settings.json`)或 Claude Desktop:
```json
{
"mcpServers": {
"ppk2": {
"command": "/Users/andylos/mcp_gen/PPK2/.venv/bin/python",
"args": ["-m", "ppk2_mcp.server"],
"cwd": "/Users/andylos/mcp_gen/PPK2",
"env": {
"PPK2_DATA_DIR": "/Users/andylos/mcp_gen/PPK2/data"
}
}
}
}
```
## 工具总览(19 个)
| 分组 | 工具 |
|---|---|
| 系统/连接 (5) | `list_devices` `connect` `disconnect` `get_status` `raw_command`(逃生舱;RESET 需 confirm) |
| 配置 (5) | `set_mode`(AMPERE/SOURCE) `set_source_voltage`(800-5000mV) `set_dut_power` `set_resistor_range`(锁定/恢复自动) `set_spike_filtering` |
| 采集 (4) | `capture`(定长→落盘→摘要+包络) `start_capture`/`stop_capture`(长采集) `get_capture_status` |
| 离线分析 (5) | `analyze_capture`(统计+子窗+包络) `detect_activity_segments`(功耗分段/占空比) `estimate_battery_life`(续航) `read_logic_channels`(逻辑口 stats/edges) `list_captures` |
## 典型工作流
```
list_devices -> connect -> set_mode(AMPERE) # 或 SOURCE + set_source_voltage(3300)
capture(5.0) # 采集 5s,返回摘要+1000 点包络+路径
analyze_capture("cap_x", start_s=1, end_s=2) # 子窗统计(µC 电量 / µJ 能量)
detect_activity_segments("cap_x", threshold_ua=50, min_duration_s=0.01)
estimate_battery_life(capture_id="cap_x", capacities_mah=[120, 240])
read_logic_channels("cap_x", channel=0, mode="edges") # 逻辑口与电流同步采样
```
DUT 供电流程:SOURCE 模式下 `set_source_voltage(3300)` → `set_dut_power(true)`
→ `capture(...)` → `set_dut_power(false)`。图表语义与官方 app 一致:SPM 模式
显示的仍是负载电流,电压即设定值。
## 架构(DDD)
```
interfaces(server.py 组合根,19 工具)
└── application(PPK2Service 用例编排,不变式校验)
└── domain(值对象/换算器+尖峰滤波状态机/分析服务纯函数/Port 接口)
└── infrastructure 实现 Port:串口协议+设备网关 / npz 仓库 / mock 设备
```
依赖方向自外向内;domain 零 IO 零框架。测试金字塔:领域纯函数 → 协议编解码
(真机元数据 fixture)→ mock 设备全栈用例 → FastMCP 协议级内存会话。
## 已知边界
- 设备未校准(`Calibrated: 0`)时按官方缺省系数换算,工具响应带告警;
校准请在官方 nRF Connect Power Profiler 中完成
- 模式切换/DUT 上电后设备有预热延迟,首采样本数可能偏低——响应带告警,建议重采
- `raw_command` 发 0x20(RESET)会触发 USB 重枚举,服务器自动清理会话,
需重新 connect
- 长采集(start/stop)无时长上限,但 npz 体积 ≈500 KB/s,注意磁盘
TDQS
Scored across 19 tools
Each tool targets a distinct action or resource: discovery vs. connection, device configuration setters, capture lifecycle, and post-capture analysis. The only adjacent pair is capture vs. start/stop_capture, but the blocking <=60s vs. background long-capture distinction is explicit, so misselection is unlikely.
Names are uniformly snake_case and almost all follow verb_noun or verb_object patterns (list_captures, set_mode, get_capture_status). Minor deviations like connect, disconnect, capture, and raw_command are conventional and readable rather than inconsistent.
19 tools is on the higher side but reasonable for a full-featured PPK2 client spanning device discovery, configuration, capture, analysis, and logic channels. No obvious redundant tool exists, though the capture lifecycle could be seen as slightly expansive.
The surface covers the core lifecycle: discover, connect, configure, capture (short and long), monitor, stop, analyze, segment, estimate battery, and read logic channels. Minor gaps like deleting/exporting captures, explicit sample-rate control, or calibration writes are mostly addressable via raw_command.