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 -r requirements.txt
pip install -e .

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 examples/read_sensors.py — 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

  • examples/quickstart.py — 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).
  • examples/read_sensors.py — a polling/averaging loop, refreshing in place, for whichever device family is connected (same PMD2/PMD2X-first-then-PMD-USB detection as quickstart.py).

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.3

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.3
File Size Uploaded Provenance Actions
pmd_pycore-0.2.3.tar.gz 15.3 kB With provenance Details for pmd_pycore-0.2.3.tar.gz

Built distribution (wheel)

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

Total release size: 28.5 kB

Release files / pmd_pycore-0.2.3.tar.gz

Download URL pmd_pycore-0.2.3.tar.gz
Size 15.3 kB
Tags Source
SHA-256 checksum
How to use checksums
795b3ce64aa7e02a138bbaaa0c6c66a2f93f1583d158827736e95888b7803b0d
BLAKE2b-256 checksum
How to use checksums
6ddf716184507b084eeca9398375541c325959a53433d43079f582e9f60ba203
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.3-py3-none-any.whl

Download URL pmd_pycore-0.2.3-py3-none-any.whl
Size 13.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dfc2ecdd52cf2db8ab6851b367d3eb865cd82df03785311847978e14888fb076
BLAKE2b-256 checksum
How to use checksums
c92847c61ac9634b561307ed9ee1c241d90e8216c00218657315a1e4a7c8a5c0
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.3

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.3.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