Skip to main content

ppk2-python

Python library and CLI for the Nordic Power Profiler Kit II (PPK2).

Features

  • Device control — source/ampere meter, voltage, DUT power, 100kHz measurement
  • File I/O — save/load .ppk2 files (nRF Connect Power Profiler compatible) — see file format
  • Daemon server — keeps the PPK2 open so multiple consumers can share one device. Required for source meter workflows on macOS — see daemon guide
  • Event annotation — overlay DUT firmware trace events (Chrome JSON scopes) onto PPK2 digital channels D0–D7 for visual correlation against the power trace
  • Reporting — markdown tables, interactive HTML charts, GitHub Actions annotations
  • Synthetic profiles — build realistic profiles programmatically with a phase/ramp builder
  • AI integration — generate, analyze, and validate profiles using Claude — see AI integration
  • Desktop automation — open .ppk2 files in nRF Connect via Playwright — see desktop
  • GitHub Action — power profiling reports in CI workflows — see GitHub Action
  • Firmware management — query, check against upstream, and flash PPK2 firmware over USB — see firmware

Status

CI

Live example reports are regenerated on every build.

Installation

pip install ppk2-python              # core library
pip install ppk2-python[report]      # + plotly HTML charts
pip install ppk2-python[desktop]     # + Playwright for nRF Connect automation
pip install ppk2-python[ai]          # + Anthropic SDK for Claude integration
pip install ppk2-python[all]         # everything

Quick Start

List connected devices

ppk2 list

Take a measurement (ampere meter mode — DUT has its own supply)

A one-shot CLI command works fine in ampere meter mode because the PPK2 only measures; nothing about closing the serial port disturbs the DUT.

ppk2 measure 5.0 -o capture.ppk2
ppk2 info capture.ppk2
ppk2 report capture.ppk2 --html report.html

Or from Python:

from ppk2 import PPK2Device, save_ppk2

with PPK2Device.open() as ppk:
    result = ppk.measure(duration_s=5.0)
    print(f"Mean: {result.mean_ua:.1f} uA")
    save_ppk2(result, "capture.ppk2")

Source meter mode — use the daemon

In source meter mode the PPK2 itself supplies the DUT rail, and closing the serial port causes the rail to collapse (DTR drop on close → DUT power off). A sequence of one-shot CLI calls — voltage, power on, measure — would reset the DUT between every command. Hold the port open with the daemon instead:

from pathlib import Path
from ppk2.daemon import start_daemon
from ppk2.client import DaemonClient

serial, _ = start_daemon()                   # auto-detects a single PPK2
sock = Path(f"~/.local/state/ppk2/{serial}.sock").expanduser()

with DaemonClient(sock, serial) as client:
    client.use_source_meter()
    client.set_source_voltage(3700)
    client.toggle_dut_power(True)
    result = client.measure(duration_s=5.0)
    print(f"Mean: {result.mean_ua:.1f} uA")

DaemonClient exposes the same surface as PPK2Device, so application code can switch between direct and daemon-backed access without other changes.

Multiple PPK2s

Daemons are keyed by serial number. Spin one up per device, then address each by serial:

from ppk2.transport import list_ppk2_devices
from ppk2.daemon import start_daemon
from ppk2.client import connect_to_daemon

for dev in list_ppk2_devices():
    start_daemon(port=dev.port)

with connect_to_daemon(serial="C9F6358A") as a, \
     connect_to_daemon(serial="AB123456") as b:
    print(a.measure(2.0).mean_ua, b.measure(2.0).mean_ua)

See the daemon guide for the full lifecycle (startup, shutdown, multi-device state directory, and the underlying limitations).

Load and inspect a .ppk2 file

from ppk2 import load_ppk2

result = load_ppk2("capture.ppk2")
print(f"Samples: {result.sample_count:,}")
print(f"Mean: {result.mean_ua:.1f} uA, Peak: {result.max_ua:.1f} uA")

Build a synthetic profile

from ppk2 import ProfileBuilder, save_ppk2

profile = (
    ProfileBuilder(seed=42)
    .phase("deep_sleep", current_ua=3.5, duration_s=5.0, noise_ua=0.5)
    .ramp("wakeup", start_ua=3.5, end_ua=15_000, duration_s=0.05)
    .phase("radio_tx", current_ua=45_000, duration_s=0.2, noise_ua=2000)
    .ramp("shutdown", start_ua=45_000, end_ua=3.5, duration_s=0.01)
    .phase("deep_sleep", current_ua=3.5, duration_s=5.0, noise_ua=0.5)
    .build()
)
save_ppk2(profile, "synthetic.ppk2")

Annotate a capture with DUT trace events

If your firmware emits Chrome JSON scope events over a serial link ({"ph":"B"/"E","ts":...,"name":"..."}), overlay them as D0–D7 logic channels on the PPK2 capture:

from ppk2.events import parse_serial_events
from ppk2.ppk2file import save_ppk2

mapper = parse_serial_events(dut_serial_output, {"gps": 0, "radio": 1})
mapper.apply(measurement)
save_ppk2(measurement, "annotated.ppk2",
          events=mapper.to_scopes(measurement))

The HTML report renders embedded scopes as a "Named scopes" table without needing a sidecar legend. Events outside the capture window are clamped and a warning is logged. See examples/capture_with_events.py for a runnable walkthrough including the daemon-client / serial-reader pattern for real hardware.

CLI

ppk2 list                                   List connected PPK2 devices
ppk2 power on|off [--port ...]              Toggle DUT power
ppk2 mode source|ampere [--port ...]        Set measurement mode
ppk2 voltage 3300 [--port ...]              Set source voltage (mV)
ppk2 measure 5.0 [-o capture.ppk2]          Measure for N seconds
ppk2 info <file.ppk2>                       Show file statistics
ppk2 report <files...> --html report.html   Generate reports
ppk2 open <file.ppk2>                       Open in nRF Connect
ppk2 generate "<description>" -o out.ppk2   Generate from text (requires AI)
ppk2 analyze <file.ppk2>                    Analyze with Claude (requires AI)
ppk2 validate <file.ppk2> --spec "<spec>"   Validate against spec (requires AI)
ppk2 merge <trace.json> <capture.ppk2>      Merge Chrome trace with PPK2 power
ppk2 firmware [info|check|upgrade|...]      Query or upgrade PPK2 firmware

The single-command CLI shapes (ppk2 power on, ppk2 voltage 3300, etc.) work directly in ampere meter mode. In source meter mode, drive the PPK2 through the daemon so the rail isn't reset between commands.

A few worked examples

# HTML report with pass/fail thresholds
ppk2 report deep_sleep.ppk2 gps_fix.ppk2 \
    --thresholds '{"deep_sleep": 10, "gps_fix": 50000}' \
    --html report.html

# Generate a synthetic profile from natural language
ppk2 generate "BLE beacon: 3uA deep sleep, wakes every 2s to TX at 8mA for 5ms" \
    -o beacon.ppk2

# Open in nRF Connect for visual comparison
ppk2 open beacon.ppk2

# Validate against a specification
ppk2 validate recording.ppk2 \
    --spec "Deep sleep should be 3-5uA. GPS acquisition under 50mA for max 60s."

Development

git clone https://github.com/m-mcgowan/ppk2-python.git
cd ppk2-python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v

Metadata

Release files for ppk2-python 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ppk2-python 0.1.0
File Size Uploaded Provenance Actions
ppk2_python-0.1.0.tar.gz 155.1 kB With provenance Details for ppk2_python-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ppk2-python 0.1.0
File Interpreter ABI Platform Actions
ppk2_python-0.1.0-py3-none-any.whl Python 3 none any Details for ppk2_python-0.1.0-py3-none-any.whl

Total release size: 243.6 kB

Release files / ppk2_python-0.1.0.tar.gz

Download URL ppk2_python-0.1.0.tar.gz
Size 155.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d267cc6f3bda1e86794b820ecfa8218571ed9944e71eebef45bbf4f6a5c6311b
BLAKE2b-256 checksum
How to use checksums
3ac28d4df855794263c55f91c7eb1c989cbc8c8ee9924d7a0a4f268ea9ccc5b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / ppk2_python-0.1.0-py3-none-any.whl

Download URL ppk2_python-0.1.0-py3-none-any.whl
Size 88.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f4c57f34b0f3d9fe36af9e48213a268d2da38fda302ab1ac84e02c77edbc4944
BLAKE2b-256 checksum
How to use checksums
d549b12012da9eea4a3bba443f4c42d1371ef80b0986113dcfced11f9908b48b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Security for ppk2-python 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ppk2_python-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page