Skip to main content

pmd-pycore

Python interface for ElmorLabs PMD power measurement devices (PMD2, PMD2X, PMD-USB).

Supported devices

PMD-USB (discontinued) PMD2 (discontinued) PMD2X (current)
4 channels (PCIE1, PCIE2, EPS1, EPS2) over a CH340 USB-serial bridge. No ATX24/motherboard rail, no 12VHPWR. 2x EPS, 3x PCIe, 1x ATX24, 1x 12VHPWR connector. STM32 USB-serial interface. Superseded by PMD2X. Same rail set as PMD2, plus per-wire 12VHPWR monitoring and connector thermistors.

PMD-USB was superseded by PMD2, which was in turn superseded by PMD2X -- the only one of the three still sold. All three measure voltage/current/power on CPU, GPU, and (PMD2/PMD2X only) motherboard power cables, with an OLED display and USB connection to a PC for logging.

Pre-requisites

  • Python >= 3.11, with pyserial (and numpy for the example scripts)
  • PMD-USB uses a CH340 USB-serial chip. Linux has an in-kernel driver for it (binds automatically, nothing to install). Windows does not ship one -- if the device shows up in Device Manager but never gets a COM port, install the CH340/CH341 driver from the chip manufacturer's site (wch-ic.com, search "CH340"/"CH341") and replug. PMD2/PMD2X use an STM32 VCP instead and don't need a separate driver on either OS.

Install

pip install pmd-pycore

Or from a checkout:

pip install -r requirements.txt
pip install -e .

This also installs the example scripts (see Examples below) as part of the package, runnable with python -m pmd_pycore.examples.quickstart.

Usage

PMD2 / PMD2X

from pmd_pycore.core import (
    get_pmd2_ports,
    read_pmd2_welcome_string,
    read_pmd2_device,
    read_pmd2_uid,
    read_pmd2_sensors,
    translate_pmd2_sensor_struct,
)
import serial

port = get_pmd2_ports()[0]["port"]
ser = serial.Serial(port, 115200, timeout=1, rtscts=False, dsrdtr=False)

device_info = read_pmd2_device(ser)
sensors = read_pmd2_sensors(ser, device_info["ProductId"])
readings = translate_pmd2_sensor_struct(sensors, device_info["ProductId"])
print(readings["12V"])  # {'voltage': 12.0, 'current': 1.0, 'power': 12.0}

Currently implemented are the device's read-only commands: CMD_WELCOME (read_pmd2_welcome_string), CMD_READ_VENDOR_DATA (read_pmd2_device), CMD_READ_UID (read_pmd2_uid), and CMD_READ_SENSOR_VALUES (read_pmd2_sensors). Writing/action commands (continuous TX, config read/write, reset, bootloader) are not implemented.

See python -m pmd_pycore.examples.read_sensors (below) — a polling/averaging loop that mirrors the original sample script.

PMD-USB

PMD-USB is a different device family -- a CH340 USB-serial bridge rather than PMD2/PMD2X's STM32 VCP, with its own command set and a simpler sensor set (PCIE1, PCIE2, EPS1, EPS2 only; no ATX24 rails, no 12VHPWR). There's no firmware source for it, so this was ported from and verified against the reference implementations linked below plus a real device.

from pmd_pycore.core import get_pmdusb_ports, read_pmdusb_id, read_pmdusb_sensors, translate_pmdusb_sensors
import serial

port = get_pmdusb_ports()[0]["port"]
ser = serial.Serial(port, 115200, timeout=1)

device_info = read_pmdusb_id(ser)
sensors = read_pmdusb_sensors(ser)
readings = translate_pmdusb_sensors(sensors)
print(readings["EPS1"])  # {'voltage': 12.0, 'current': 1.0, 'power': 12.0}

Implemented: CMD_WELCOME (read_pmdusb_welcome_string), CMD_READ_ID (read_pmdusb_id), and CMD_READ_SENSOR_VALUES (read_pmdusb_sensors/translate_pmdusb_sensors). This command already returns calibrated readings -- confirmed against the official PMD-USB GUI app's source, which uses it directly with no client-side calibration math -- so CMD_READ_ADC's raw path isn't needed. translate_pmdusb_aggregate_power adds GPU/CPU/Total power (PCIE1+PCIE2 / EPS1+EPS2 / sum), computed client-side the same way the official app does; PMD-USB's firmware doesn't compute these on-device the way PMD2/PMD2X's does.

Note: PMD-USB has no per-unit UID/serial number anywhere -- not in the UART protocol (no command for it), and not at the USB level (the CH340 chip's serial number field reads empty on the device tested here). If you have more than one, there's no way to tell them apart besides which COM port they land on.

See KNOWN_LIMITATIONS.md for what's deliberately out of scope and why.

Examples

Both examples ship as part of the installed package -- run them with python -m after pip install pmd-pycore, no checkout needed.

  • python -m pmd_pycore.examples.quickstart — connect to the first device found, trying PMD2/PMD2X first and falling back to PMD-USB, print its identity, and read sensor values once (for PMD2/PMD2X: per-rail readings, firmware-computed aggregate power, chip health, and PMD2X's 12VHPWR connector temperatures).
  • python -m pmd_pycore.examples.read_sensors — a polling/averaging loop, refreshing in place, for whichever device family is connected (same PMD2/PMD2X-first-then-PMD-USB detection as quickstart).

Status

This repository is under active development at ElmorLabs-P1/pmd-pycore. It provides an installable pmd_pycore package (serial discovery, UART command helpers, sensor structures, and readable-unit translation for the supported devices), a test suite, and CI for tests and tagged releases.

Tagged releases build an sdist/wheel and attach them to a GitHub Release. A PyPI-publish job is wired into the release workflow but not yet active -- it needs PyPI Trusted Publishing configured for this repo first (no credentials are stored in GitHub either way). Until that's set up, install from the GitHub Release assets or pip install -e . from a checkout.

  • PMDLogger — C# reference implementation for PMD-USB (there's no public PMD-USB firmware source); PMD_USB_Device.cs has the command set and sensor scaling used here.
  • PMD-USB — the official PMD-USB GUI app; FormPMD.cs confirmed CMD_READ_SENSOR_VALUES is already calibrated and has the CMD_READ_CONFIG struct layout.
  • pmd-usb-serial-interface-fast / elmorlabs-pmd-usb-serial-interface — Python sample scripts for PMD-USB, cross-checked against PMDLogger and a real device.

Metadata

Release files for pmd-pycore 0.2.5

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

Source distribution (sdist)

Source distribution for pmd-pycore 0.2.5
File Size Uploaded Provenance Actions
pmd_pycore-0.2.5.tar.gz 19.7 kB With provenance Details for pmd_pycore-0.2.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pmd-pycore 0.2.5
File Interpreter ABI Platform Actions
pmd_pycore-0.2.5-py3-none-any.whl Python 3 none any Details for pmd_pycore-0.2.5-py3-none-any.whl

Total release size: 38.6 kB

Release files / pmd_pycore-0.2.5.tar.gz

Download URL pmd_pycore-0.2.5.tar.gz
Size 19.7 kB
Tags Source
SHA-256 checksum
How to use checksums
501a73822e9b75e9a90e9cbb83d440e1c3c594a01148b597f7d562524c67e8eb
BLAKE2b-256 checksum
How to use checksums
faaf2449754a4dfd6b7470edd2b61aaa92f107099c06bdb3cb1701dbaff2b42c
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 / pmd_pycore-0.2.5-py3-none-any.whl

Download URL pmd_pycore-0.2.5-py3-none-any.whl
Size 18.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4952d4cac7fb88ac2fe3a9837d61450820054295a9a81b3ab23500bd5cd94671
BLAKE2b-256 checksum
How to use checksums
b2c6d928103b90b9e57ec166fda25d1e1f0b66c1b86dcf1cc8d4669349db5e4e
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 pmd-pycore 0.2.5

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 pmd_pycore-0.2.5.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.

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