USB-C breakout/Using it/10. Read what the charger offers
Using it · 10 of 11

Read what the charger offers

The CC pads are brought out so you can listen to them. Two wires to an ESP32 and about thirty lines of code turn the board into something that tells you which way up the plug went in and how much current the source is offering.

The idea

The board pulls each CC line down through 5.1 kΩ. The source pulls one of them up through a resistor of its own, and which resistor it chose is how it announces the current it has. That makes a voltage, and a voltage is something a microcontroller can read.

Nothing on the breakout does this. The pads are simply brought out, and that is enough.

What the sketch reads on CC
1.5 A charger
Charger plugged in
Live CC
0.94 V
Other CC
0.00 V
Reported
1.5 A
Two readings tell you both things at once. The CC that is sitting near zero is the one the cable did not connect; the other one carries the answer. Nothing else on the board is doing this — the pull-down is passive, and the number only exists because you went and measured it. That is the difference between a breakout and a PD trigger, which contains a chip whose whole job is to have this conversation for you.

Wiring

Three wires, and none of them is VBUS:

  1. Breakout GND to the ESP32's GND.
  2. Breakout CC1 to GPIO34.
  3. Breakout CC2 to GPIO35.

Power the ESP32 from its own USB cable. There is no need to take power from the breakout for this, and joining the breakout's VBUS to the ESP32's 5V pin while the ESP32 is also plugged into your computer ties two supplies together.

GPIO34 and GPIO35 are input-only pins on ADC1. That matters: ADC2 stops working while Wi-Fi is on, and picking an ADC2 pin here is the kind of thing that works on the bench and fails in the finished project.

It is safe to read CC directly

With the 5.1 kΩ pull-downs fitted, the highest a CC pin reaches on a 5 V source is about 1.69 V — the 10 kΩ case, which is a source advertising 3 A. That is comfortably inside what a 3.3 V input will take, so no divider and no protection is needed between the pad and the pin.

If you ever read more than about 2 V there, something is not what this chapter assumes. Unplug it and find out what before trusting any of the numbers.

What you get

CC1 0.94 V   CC2 0.00 V
  cable is on CC1, source offers 1.5 A

Turn the plug over and the same charger reports on CC2 instead, with CC1 at zero. That is the clearest demonstration in this book of why there are two resistors — the board does not change, the cable does.

What to do with the answer

Treat it as a budget, not a permission. Knowing the source offers 1.5 A tells you what you may design around; it does not make your wiring capable of carrying it, and it does not stop your circuit from asking for more.

And note what this sketch is not. It reads an advertisement made with a resistor. It does not negotiate, it cannot ask for 9 V, and it has no effect on what the source does — the source made that offer the moment the CC resistors introduced themselves, and would have made it whether or not anybody was listening.

The code

usbc_cc_watch.ino

Reads both CC pads once a second and prints what the source is advertising. The CC that is near zero is the one the cable did not connect; the other carries the answer.

// Read what a USB-C source is advertising, from the breakout's CC pads.
//
// Wiring:
//   breakout GND  -> ESP32 GND
//   breakout CC1  -> ESP32 GPIO34
//   breakout CC2  -> ESP32 GPIO35
//   breakout VBUS -> leave it unconnected for this sketch
//
// Power the ESP32 from its own USB cable. Do not also feed VBUS into the
// board's 5V pin while it is plugged into your computer — that joins two
// supplies together. The point here is to listen to CC, and CC needs only
// the ground wire and the two signal wires.
//
// CC never goes above about 1.7 V on a 5 V source with the 5.1k pull-downs
// fitted, so it is safe on an ESP32 input directly. If you ever read more
// than 2 V here, unplug and find out why before trusting the number.
//
// Arduino IDE: Tools > Board > esp32 > ESP32 Dev Module, then Tools > Port
// and pick the port the board appears on. No libraries.

const int CC1_PIN = 34;
const int CC2_PIN = 35;

// Roughly where a sink decides one advertised band has become the next.
// From the USB Type-C specification, not from this board.
const float EDGE_1V5 = 0.66;   // volts
const float EDGE_3A  = 1.23;

// Anything below this is an open CC line: the one the cable left alone.
const float OPEN_MAX = 0.20;

float readVolts(int pin) {
  // Average a few samples - the ADC is noisy enough to wobble the last digit.
  uint32_t mv = 0;
  for (int i = 0; i < 16; i++) mv += analogReadMilliVolts(pin);
  return (mv / 16.0) / 1000.0;
}

const char *advertises(float v) {
  if (v < OPEN_MAX)  return "nothing - this line is open";
  if (v < EDGE_1V5)  return "500 mA (default USB)";
  if (v < EDGE_3A)   return "1.5 A";
  return "3.0 A";
}

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("CC watch - plug the breakout into a USB-C source");
}

void loop() {
  float v1 = readVolts(CC1_PIN);
  float v2 = readVolts(CC2_PIN);

  // The live CC is whichever one the cable actually connected.
  bool onCC1 = v1 > v2;
  float live = onCC1 ? v1 : v2;

  Serial.printf("CC1 %.2f V   CC2 %.2f V\n", v1, v2);

  if (live < OPEN_MAX) {
    Serial.println("  both lines open - no source, or no CC wire in the cable");
  } else {
    Serial.printf("  cable is on %s, source offers %s\n",
                  onCC1 ? "CC1" : "CC2", advertises(live));
  }

  delay(1000);
}

analogReadMilliVolts() applies the chip's factory ADC calibration, which is the difference between a reading you can compare against a table and one you cannot. GPIO34 and GPIO35 are input-only pins on ADC1, so they keep working when Wi-Fi is on — ADC2 does not.

When it does not work

Both readings are near zero

Either nothing is plugged into the breakout, or the cable between the source and the board has no CC wire. A USB-A to USB-C cable is the usual culprit: a USB-A port has no CC line at all, so there is nothing to measure even though VBUS may well be live. Use a C-to-C cable and a source that has a C socket.

Both readings are the same and not zero

Check you have not joined CC1 and CC2 together somewhere — on a breadboard that is one misplaced jumper. Joined, the two 5.1 kΩ resistors are in parallel and the source sees a value it was not looking for, which can also stop VBUS coming up at all.

The voltage is close to a boundary and the answer flickers

Real resistors have tolerances at both ends of the divider, so a source near a boundary will land near a boundary. The sketch averages sixteen samples, which settles the noise but cannot settle a genuinely borderline value. If it matters, treat the lower of the two answers as the truth — under-claiming current is safe and over-claiming is not.

The number is off by a tenth of a volt from the table

That is within what the pull-up tolerance and the ADC calibration together will do, and it does not change which band you are in unless you are already on a boundary. What would be worth chasing is a reading that drifts while nothing changes, which usually means a floating ground between the two boards.

Where this goes next

Four readings in the order that finds the fault fastest, and the one that tells you to stop looking at the board.

When nothing happens

Edit this page — content/books/usb-c-breakout/read-what-the-charger-offers.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

USB-C Breakout Board, 12-Pack with 5.1 kΩ CC Resistors

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum