USB-C Attach: Why One Plug Orientation Can Lose VBUS
Start with a USB-C sink receptacle that attaches in both orientations. Remove just one of its two pull-down paths and flip the same cable at the sink end. Watch which Configuration Channel pin is active and whether the power bus still turns on.
Evidence scope. This is a proposed isolated bench fixture. The complete analyzer was tested on synthetic data. The experiment observes settled direct-current (DC) voltages on this setup. It does not measure attach timing or current capability. The fixture neither requests USB Power Delivery (USB PD) nor exchanges data.
The cable selects a path
USB stands for Universal Serial Bus. A USB Type-C source provides power and a sink receives it. The connector’s Configuration Channel (CC) lets a source detect a sink. The source presents a pull-up, called Rp. The sink presents a pull-down to ground, called Rd. The resulting voltage on the connected path lets a source recognize the termination and then enable VBUS, the bus supply rail.
A receptacle has separate contacts CC1 at A5 and CC2 at B5. A standard Type-C cable carries one through CC wire. Keeping one plug fixed and rotating the other changes which receptacle CC pin that wire reaches. Each sink contact therefore gets its own Rd. The two contacts must remain separate. The other plug contact can have a cable-side function such as VCONN, the power used by supported cable electronics. The idle or open-contact reading alone does not establish which end-to-end CC path is connected.
The USB Implementers Forum (USB-IF) Release 2.5, Table 3-4, identifies both receptacle contacts. Figure 4-22 and its accompanying sequence show the single path and source attach behavior. Table 4-28 gives 5.1 kΩ as nominal for the resistive sink termination. The meter readings here are for the local fixture and do not decode current advertisements.
Build one reversible fault fixture
- Use a Pololu #3411 usb07b USB 2.0 Type-C receptacle breakout. The Pololu schematic shows a separate 5.11 kΩ resistor from CC1 (R1) and CC2 (R2) to ground. The manufacturer documents removing these resistors to make your own CC connections. Use the board’s CC 1, CC 2, VBUS and GND markings. The numbered row in the schematic is VBUS 1, CC1 4, CC2 5 and GND 8. Use the markings and continuity, since left/right changes with the side you view. The duplicate power and ground holes share their respective nets.
- Fit two 5.1 kΩ ±1% resistors and a 100 kΩ bleeder, for example Vishay MRS25 metal-film resistors. A bleeder is a weak path that drains residual charge when the supply is removed. Add one removable link in series with each 5.1 kΩ branch. The links change the connection to the resistors. They do not join the CC pins together.
- Use one genuine Apple USB-C Power Adapter A2305 with its housing intact and one fixed StarTech USB2CC1M 1 m C-to-C cable. Keep the adapter’s mains side enclosed and untouched. Mark the fixture-end plug on one face, leave the source-end plug inserted in the same orientation, and never swap ends. No hubs, adapters, inline testers, computers or added loads belong in this setup.
- Use three battery-powered Fluke 87V MAX meters in DC volts with leads in COM and the volts input. Fluke gives about 10 MΩ for these voltage ranges. Use insulated clips on the breakout or connected test wires, never probes inside the receptacle. Keep all meter COM leads at the same fixture GND. The meters remain connected in the same way for all captures.

Disconnect the cable completely before modifying the board. Under magnification, identify each fitted resistor by continuity from its pad to the corresponding marked CC hole and to GND. Remove the two resistors without cutting traces or levering against the connector. A qualified assembler can prepare the board if you lack the tools for surface-mount rework. Stop if the board differs from the documentation or if you cannot identify the pads.
With all meters and the cable disconnected, verify that CC1 and CC2 are each open to GND on the bare modified board and separate from each other and from VBUS. Inspect for solder bridges. After fitting the external parts, test each branch with its link removed and fitted. Expect open with a link removed and about 5.1 kΩ to GND when fitted. With both fitted, resistance between CC1 and CC2 is about 10.2 kΩ through their two ground paths. Verify about 100 kΩ from VBUS to GND and stop if any expected path is shorted.
The Fluke input adds a weak parallel path. One 10 MΩ meter changes a 5.1 kΩ branch to about 5.097 kΩ. That is about a 0.051% change. A meter on a deliberately open branch still loads it weakly, so record its voltage without classifying it as a valid Rd. The VBUS bleeder draws about 50 µA at 5 V. No payload or current measurement is attached.
Record both orientations, then restore the control
Keep the same cable, source, breakout, meter clips and ground throughout. Call the marked plug face up A and the flipped orientation B. Those labels do not tell you which CC path is active.
| Capture | CC1 link | CC2 link | Expected VBUS |
|---|---|---|---|
no_rd | Open | Open | Off in A and B |
dual_rd | Fitted | Fitted | Present in A and B |
cc2_open | Fitted | Open | Present only on mapped CC1 |
restored | Fitted | Fitted | Present in A and B |
- Install the meter clips and secure the test wires with the fixture unplugged. Use the table’s first link state. Connect the marked sink plug, wait at least two seconds and record CC1, CC2 and VBUS in volts once the displays settle. Disconnect the sink end, wait at least two seconds and verify that fixture VBUS has returned near zero before reinsertion. Take three separate insertions for each orientation, giving six triples per link state.
- For every link change, unplug the sink cable and verify zero before touching the links. Stop if VBUS is above 5.5 V, if the source energizes VBUS in the
no_rdcontrol, or if the setup behaves unexpectedly. Investigate the source and wiring while disconnected before continuing. - In the
dual_rdcontrol, one CC voltage should be higher than the other and the pattern should swap in the other orientation. Record which mark maps to each CC pin. A mark that gives ambiguous or inconsistent readings provides no basis for interpreting the fault. - Open CC2, repeat both orientations and compare with the mapping. Keep the external CC1 branch in place. Restore CC2 and repeat both orientations. The restored control helps catch a disturbed cable, clip or ground that happened to coincide with opening the link.

For background on cable construction, see the Tricky Circuit cable overview. This fixture establishes an orientation-dependent attachment pattern with one cable. It does not identify the wiring inside its plugs.
Analyze the four configurations
Use Python 3.9 or later. Put the three settled voltage readings for each insertion on one CSV row. The worksheet uses configuration,orientation,repeat,cc1_v,cc2_v,vbus_v. Fill all 24 rows. Readings must be numeric volts. Preserve the actual marks and never infer a missing measurement from the example.
python3 cc_orientation.py --kind synthetic synthetic-captures.csv
python3 cc_orientation.py --kind observed my-captures.csv
The analyzer calls a group off when all three VBUS readings are from −0.10 to 0.20 V. It calls VBUS present when all three are from 4.75 to 5.50 V with a span of at most 0.20 V. Everything between or unstable is inconclusive. In the dual and restored controls, a loaded CC candidate must stay from 0.20 to 2.20 V with at most 0.15 V span, while the other stays from −0.10 to 0.10 V. These are broad fixture screens, separate from any USB-IF compliance limits.
The result is positive only when both no-Rd controls are off, the dual-Rd controls map opposite paths, opening CC2 removes VBUS only on its mapped orientation, and restoration repeats the map. The retained CC1 median while CC2 is open and both restored active medians must each be within 0.20 V of the corresponding dual-Rd median. A complete but inconsistent data set returns investigate. A malformed or incomplete file exits with code 2.
#!/usr/bin/env python3
"""Compare isolated USB-C orientation captures with a controlled open CC2 pull-down."""
import argparse
import csv
import json
from math import isfinite
from pathlib import Path
from statistics import median
STATES = ('no_rd', 'dual_rd', 'cc2_open', 'restored')
MARKS = ('A', 'B')
REPEATS = ('1', '2', '3')
HEADER = ['configuration', 'orientation', 'repeat', 'cc1_v', 'cc2_v', 'vbus_v']
EPS = 1e-9 # Numerical roundoff only, not a measurement uncertainty allowance.
def read_captures(path):
captures = {(state, mark): {} for state in STATES for mark in MARKS}
with path.open(encoding='utf-8-sig', newline='') as stream:
rows = csv.reader(stream, strict=True)
if next(rows, None) != HEADER:
raise ValueError('expected header ' + ','.join(HEADER))
for row in rows:
if len(row) != len(HEADER):
raise ValueError(f'line {rows.line_num}: expected six fields')
state, mark, repeat = (x.strip() for x in row[:3])
if state not in STATES or mark not in MARKS or repeat not in REPEATS:
raise ValueError(f'line {rows.line_num}: invalid configuration, orientation or repeat')
group = captures[state, mark]
if repeat in group:
raise ValueError(f'line {rows.line_num}: duplicate configuration, orientation and repeat')
try:
volts = tuple(map(float, row[3:]))
except ValueError as exc:
raise ValueError(f'line {rows.line_num}: expected three finite voltages') from exc
if not all(isfinite(v) for v in volts):
raise ValueError(f'line {rows.line_num}: expected three finite voltages')
if not all(-.10 <= v <= 6.0 for v in volts):
raise ValueError(f'line {rows.line_num}: outside the −0.10 to 6.00 V fixture voltage envelope')
group[repeat] = volts
if any(len(g) != len(REPEATS) for g in captures.values()):
raise ValueError('expected all 24 complete capture rows')
return captures
def inside(values, low, high):
return min(values) >= low - EPS and max(values) <= high + EPS
def spread(values):
return max(values) - min(values)
def loaded_cc(values):
return inside(values, .20, 2.20) and spread(values) <= .15 + EPS
def summarize(group):
cc1, cc2, vbus = zip(*(group[i] for i in REPEATS))
if inside(vbus, -.10, .20):
state = 'off'
elif inside(vbus, 4.75, 5.50) and spread(vbus) <= .20 + EPS:
state = 'present'
else:
state = 'inconclusive'
active = None
if state == 'present':
if loaded_cc(cc1) and inside(cc2, -.10, .10):
active = 'CC1'
elif loaded_cc(cc2) and inside(cc1, -.10, .10):
active = 'CC2'
return {'vbus_state': state, 'dual_rd_active_cc_candidate': active,
'median_v': {name:median(values) for name, values in zip(('CC1','CC2','VBUS'), (cc1,cc2,vbus))},
'span_v': {name:spread(values) for name, values in zip(('CC1','CC2','VBUS'), (cc1,cc2,vbus))}}
def analyze(captures, kind):
groups = {state:{mark:summarize(captures[state, mark]) for mark in MARKS} for state in STATES}
for state in ('no_rd', 'cc2_open'):
for row in groups[state].values():
# An open input need not sit low, so only the dual-Rd controls get a candidate.
row['dual_rd_active_cc_candidate'] = None
candidates = {mark:groups['dual_rd'][mark]['dual_rd_active_cc_candidate'] for mark in MARKS}
mapping = candidates if set(candidates.values()) == {'CC1','CC2'} else None
only_cc1 = False
restored = False
if mapping:
works = next(mark for mark, cc in mapping.items() if cc == 'CC1')
fails = next(mark for mark, cc in mapping.items() if cc == 'CC2')
baseline = groups['dual_rd'][works]['median_v']['CC1']
partial = groups['cc2_open'][works]
open_cc1 = [captures['cc2_open', works][i][0] for i in REPEATS]
only_cc1 = (partial['vbus_state']=='present'
and groups['cc2_open'][fails]['vbus_state']=='off'
and loaded_cc(open_cc1)
and abs(partial['median_v']['CC1']-baseline) <= .20 + EPS)
restored = all(groups['restored'][mark]['dual_rd_active_cc_candidate'] == mapping[mark]
and abs(groups['restored'][mark]['median_v'][mapping[mark]]
- groups['dual_rd'][mark]['median_v'][mapping[mark]]) <= .20 + EPS
for mark in MARKS)
checks = {
'source_baseline_off_with_no_rd': all(row['vbus_state']=='off' for row in groups['no_rd'].values()),
'dual_control_maps_opposite_cc_paths': mapping is not None,
'only_mapped_cc1_works_when_cc2_open': only_cc1,
'restored_repeats_dual_control': restored,
}
return {'kind':kind,'result':'consistent_with_open_cc2' if all(checks.values()) else 'investigate',
'checks':checks,'dual_orientation_map':mapping,'groups':groups,
'scope':'Stable readings for this fixture only. No USB compliance, current, PD or timing result.'}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--kind', choices=('synthetic','observed'), required=True)
parser.add_argument('csv', type=Path)
args = parser.parse_args()
try:
report = analyze(read_captures(args.csv), args.kind)
except (OSError, ValueError, UnicodeError, csv.Error) as exc:
parser.error(str(exc))
print(json.dumps(report, indent=2, allow_nan=False))
if __name__ == '__main__':
main()
Blank capture worksheet · Synthetic captures · Full reader experiment and tests ZIP
Challenge
The dual-Rd control maps mark A to CC2 and mark B to CC1. You open CC2. Which orientation should keep VBUS, and does 4 V on the open CC2 pin change your conclusion?
Show the answer
B should retain VBUS because the cable still reaches the fitted CC1 pull-down. A can show voltage on the open CC2 path through the source’s pull-up and the meter. That voltage does not supply a 5.1 kΩ path to ground. Compare the measured VBUS and repeat both orientations after restoring CC2.
Primary references
- USB-IF USB Type-C Release 2.5, March 2026, Table 3-4, Figure 4-22 and Table 4-28.
- Pololu #3411 documentation and board photos and the usb07b schematic, contact breakout, separate resistors and board revision.
- Apple adapter user guide, exact A2305 model, and StarTech USB2CC1M data sheet, exact C-to-C cable.
- Fluke 87V MAX User Manual, voltage input and lead terminals, and Vishay MRS25 documentation, resistor family.