USB-C Power Delivery for a non-phone product
A product that needs 12 or 20 volts and has no battery used to mean a barrel jack and a wall wart with a fixed output printed on the label. Now the mechanical drawing says USB-C, the first prototype board comes up, and someone plugs it into a charger and probes the input rail. It reads 5 volts. Nothing is broken. Nothing has negotiated yet, and until it does, USB-C behaves exactly like the dumb 5-volt connector it’s replacing.
That gap between “it’s a USB-C connector” and “it delivers the voltage the load needs” is where most of the design work lives — and where the questions keep landing, most recently in a Hacker News thread on a small open-source PD sink library and a run of ST community forum posts from engineers whose sink negotiates a contract, then drops it, or never gets past a hard reset. The connector is a commodity. The negotiation is not.
What has to happen before the voltage changes
A USB-C source always powers up at 5 volts and stays there until something asks for more. The sink identifies itself by pulling each CC line down through roughly 5.1 kΩ (Rd); the source detects that, applies VBUS, and — if it supports Power Delivery — starts a state machine over the CC line, coded at 300 kbps. The source sends a Source_Capabilities message listing the power data objects (PDOs) it offers: a set of fixed voltages, and on newer chargers a PPS or AVS range adjustable in fine steps. The sink picks one, sends a Request naming that PDO and the current it needs, and only after the source answers Accept and then PS_RDY does the rail actually change.
Any of that can fail — a malformed message, a timeout, a current the source won’t grant — and the result is a Hard_Reset back to default 5 volts until the sink tries again. None of this is instant; the handshake takes tens to low hundreds of milliseconds even when it goes cleanly. A design that assumes the requested voltage is present the moment VBUS appears will misbehave in exactly the way people are currently debugging in public.
Three ways to build the sink
There are three real options, and the right one depends on how much the voltage needs to change after boot, not on how the rest of the board is built.
| Approach | Firmware effort | Good for |
|---|---|---|
| Standalone PD controller (e.g. TI TPS25730, Cypress/Infineon CYPD3177, STMicro STUSB4500) | Low — config via NVM/straps, or a thin I2C layer to a host MCU | Fixed or small set of requested voltages, minimal host involvement |
| MCU with an integrated PD peripheral (e.g. STM32G0/G4/U5 UCPD) plus a CC protection IC | High — a full PD stack runs on the application MCU | Devices that already need an MCU for the main load and want tight integration |
| Fixed-voltage trigger/decoy IC (e.g. CH224, IP2721-class parts) | Near zero | One known voltage, no renegotiation, no PPS |
A standalone controller is usually the right default: the protocol logic and timing live in silicon that’s already been through interoperability testing against real chargers, and the host side is a handful of register reads. An MCU-integrated peripheral earns its place when the product already needs an application processor and the extra BOM line isn’t worth avoiding — but budget real firmware time for it; several ST forum threads this month trace back to CC pull-down configuration or interrupt setup, not the protocol itself. A trigger chip is the right call when the product genuinely only ever needs one voltage and never renegotiates.
Worth naming a fourth option so it can be ruled out: bit-banging PD on a spare GPIO with a transistor and no dedicated PHY. It’s a legitimate way to strip a BOM line on a hobbyist board, and it works — a recent Hackaday writeup did exactly this. But the CC timing is tight, there’s no built-in over-voltage or dead-battery protection, and correctness depends entirely on code that hasn’t seen the range of chargers a shipping product will meet. Fine for one board on a bench. The wrong foundation for a charger you’ve never tested.
The bootstrap problem for a device with no battery
Phones and laptops run their own housekeeping from a battery while PD negotiates in the background. A mains-powered instrument or tool usually can’t — there’s no charge reservoir to run the MCU and PD controller on while the rail comes up. (A product that does carry a rechargeable cell doesn’t have this problem, but it inherits a charger-input design of its own.) That’s the specific problem the recent HN discussion around PD sink libraries kept circling: a device that needs 12 volts or higher and has nothing else to run on can’t do anything until negotiation finishes, and negotiation itself needs power.
Two patterns work. Keep a small always-on section — the PD controller, a supervisor, a status LED — running directly off the default 5-volt rail, and gate the main load behind a “contract accepted” signal. Or run a small boost or buck-boost stage from 5 volts long enough to bootstrap the system, then hand off once the negotiated rail is stable. Either way, the product needs a defined, visible state for “still negotiating” rather than silence — an LED that’s dark during a two-hundred-millisecond negotiation window reads, to a customer, as a dead board.
What breaks once it leaves the bench
A sink that works against a bench power supply and one charger has not been tested against the environment it will actually ship into.
Multi-port chargers and power banks renegotiate their internal power budget when a second port is plugged or unplugged, which can drop and re-establish VBUS on ports that were already running. A sink has to treat a Hard_Reset as routine, not exceptional, and recover without browning out downstream regulators or latching a fault. Not every charger with a USB-C connector speaks standard PD at all — many phone-fast-charging bricks default to a proprietary handshake first and only fall back to PD or plain 5 volts if the sink doesn’t participate in it. A sink should request only from the standard PDO list and treat “the requested voltage isn’t offered” as a normal branch, not an error path added after the first field return. Cables matter too: current above 3 A requires a 5 A-rated cable with an e-marker chip, and a cheap unmarked cable makes the source cap current regardless of what the sink asked for — worth a line in the product’s documentation, not just the schematic.
If the load needs a continuously variable voltage rather than fixed steps, PPS or the newer AVS range is the right tool, but the contract has to be refreshed every few seconds or the source lets it lapse and falls back to a fixed PDO. A profile that assumes a PPS voltage stays put indefinitely will drift back to 5 volts mid-cycle for no reason visible on a scope without already knowing to look for it.
Where EU rules are pushing this now
The EU’s common-charger rules under the Radio Equipment Directive already require USB-C on phones, tablets, and cameras, and reach laptops on April 28, 2026, with mandatory PD support up to 240 W. Its actual scope is narrower than most engineers assume: it covers radio equipment — devices with wireless capability — not electronics generally. A mains-powered instrument or test fixture with no radio isn’t compelled to carry USB-C. But market pressure runs ahead of the regulation anyway: engineering leads increasingly ask for USB-C on products that were never in scope, simply because it’s what every cable in the building already is.
That’s usually the actual decision hiding behind “just put USB-C on it” — not a compliance question, but a power-architecture one, and it’s cheaper to answer correctly at the schematic stage than after the enclosure is tooled around a barrel jack.
Send us the load’s voltage and current profile, whether the product has any energy storage of its own, and how it needs to behave in the first half-second after it’s plugged in, and we’ll tell you whether a standalone controller, an MCU peripheral, or a trigger IC is the right fit before the CC pull-downs get laid out.
Adding USB-C power to a product that is not a phone? We design the PD sink circuit and the firmware that negotiates it.
Tools for this topic
- ESP32 browser flasherFlash a .bin over USB from the browser. It reads the chip and the file first and refuses firmware built for a different chip, revision or flash size.
- STM32 browser flasherFlash a .dfu, .hex or .bin over USB DFU from the browser. It reads the chip’s flash map and identity first and refuses firmware whose bytes or vector table do not belong on it. Never touches option bytes.
- Browser serial terminalConnect a USB board and read or send serial text and hex from the browser. Baud rate and format, DTR/RTS reset profiles for ESP32 and Arduino, timestamps and a saved log. Nothing leaves your computer.
Related notes
- I2C bus stuck low after a resetWhy an I2C sensor holds SDA low after the MCU resets, how the nine-clock bus clear works, and the board and firmware changes that prevent a hang.
- First power-up of a new prototype board, step by stepA bring-up order that finds shorts, bad rails and dead debug links before they cost you a board: what to check on a current-limited bench supply, in what…
- What test points and programming pads a board needsWhich signals to bring out for programming, debug and production test, so a board can be flashed and checked on a fixture without a respin.