Metadata-Version: 2.4
Name: axxpd
Version: 1.0.0
Summary: Python library for controlling AxxPD USB-C PD 3.1 EPR sink via SCPI
Author-email: "Axel Johansson (AxxAxx)" <axel.johansson@duck.com>
License-Expression: GPL-3.0-only
Project-URL: Homepage, https://github.com/AxxAxx/AxxPD
Project-URL: Issues, https://github.com/AxxAxx/AxxPD/issues
Keywords: USB-C,USB-PD,power-delivery,EPR,PPS,SCPI,pyserial,bench-supply,test-equipment,instrumentation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyserial>=3.5
Dynamic: license-file

# axxpd

Python library for controlling an [AxxPD](https://github.com/AxxAxx/AxxPD) USB-C PD 3.1 EPR power source over its SCPI interface.

AxxPD is a programmable USB-C Power Delivery tool: it draws negotiated power from a USB-C PD charger (it is a PD *sink*) and re-exposes it as a controllable bench-supply output, from 3.3 V up to 48 V (up to 240 W), with voltage/current/power measurement, protection, and automation over a USB CDC serial port. This library wraps that SCPI command set as a Python API and auto-discovers the device's serial port.

## Installation

```
pip install axxpd
```

Requires Python 3.8+ and [pyserial](https://pypi.org/project/pyserial/) (installed automatically).

## Before you start

- AxxPD is a **sink** — plug a USB-C PD charger or power bank into its input.
- The output is **off at boot**; call `output_on()` to enable it.
- Voltages above 20 V (the EPR range, up to 48 V / 240 W) require both the charger and the cable to support EPR — use a **240 W e-marked USB-C cable**. Without one you're capped around 20 V.
- The voltages you can actually reach depend on what the charger advertises; AxxPD requests the closest match and reports the contract it got.

⚠️ **Safety:** the output can reach **48 V at up to 240 W**. Current-limit or disconnect your load before enabling the output, and set `set_ocp()` appropriately.

## Quick start

```python
from axxpd import AxxPD, AxxPDError

try:
    pd = AxxPD()                  # auto-detects the AxxPD serial port
except AxxPDError:
    pd = AxxPD("COM10")           # ...or pass the port explicitly

with pd:
    print(pd.idn())               # device identity
    pd.set_voltage(12.0, current=2.0)   # 12 V, 2 A current limit
    pd.output_on()

    m = pd.measure()              # dict: voltage, current, power, energy_wh,
                                  # charge_ah, temp_die, temp_ntc
    print(f"{m['voltage']:.3f} V, {m['current']:.3f} A")

    for sample in pd.stream(duration=10):   # ~20 Hz telemetry
        print(sample)

    pd.output_off()
```

All values are SI: volts, amps, watts, energy in Wh, charge in Ah, temperature in °C (or °F if the device's Fahrenheit setting is on).

## Connecting

Auto-detect picks the **STMicroelectronics Virtual COM Port** (VID:PID `0483:5740`) — not the ST-Link debug port. If no device is found, `AxxPD()` raises `AxxPDError`; pass the port explicitly instead. Ports look like `COM10` on Windows, `/dev/ttyACM0` on Linux, or `/dev/cu.usbmodem*` on macOS. To list candidates:

```python
from serial.tools import list_ports
for p in list_ports.comports():
    print(p.device, p.description)
```

All library errors (connection failure, unparseable device response) raise `AxxPDError`, so wrap calls in `try/except AxxPDError` for robust automation.

## A few terms

A PD charger advertises one or more **PDOs** (selectable voltage/current offerings); the sink negotiates one, forming a **contract**. **PPS** and **AVS** are adjustable-voltage modes; **EPR** (Extended Power Range) unlocks voltages above 20 V, up to 48 V / 240 W.

## API overview

- **Identity / system:** `idn()`, `reset()`, `reboot()`, `selftest()`
- **Output:** `output_on()`, `output_off()`, `output_state()`
- **Set point:** `set_voltage(v, current=None)`, `set_pps(v, current=None)`, `set_avs(v)`, `set_pdo(index)`, `set_mode(mode)`
- **PD info:** `list_pdos()`, `list_all_pdos()`, `contract()`, `enter_epr()`, `exit_epr()`
- **Measurement:** `measure()`, `measure_voltage()`, `measure_current()`, `measure_power()`, `measure_temperature()`, `measure_energy()`
- **Protection:** `set_ocp(amps)`, `set_ovp(volts)`, `protection_status()`, `protection_clear()`
- **Sequencing:** `seq_add(v, time_ms)`, `seq_clear()`, `seq_list()`, `seq_run()`, `seq_stop()`
- **Streaming:** `stream(duration=None, callback=None)` — yields telemetry dicts at ~20 Hz (omit `duration` to stream until interrupted)
- **Errors:** every library error raises `AxxPDError`

`stream()` skips malformed frames silently, so the sample count over a fixed duration isn't guaranteed. `set_voltage()` does not raise if the source can't satisfy the request — it falls back to the nearest PDO, so confirm with `measure()` or `contract()`.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| `AxxPDError` on connect / wrong port | Device not enumerated, or the ST-Link port was picked — pass the port explicitly |
| Reads 0 V | Output not enabled (`output_on()`), no PD source connected, or no contract — check `contract()` and `output_state()` |
| Can't reach voltages above 20 V | No EPR — use a 240 W e-marked cable and a charger that advertises EPR |

For the full SCPI command reference and device documentation, see the [project repository](https://github.com/AxxAxx/AxxPD).

## License

GPL-3.0-only. Copyright Axel Johansson.
