The 3-digit capacitor code system (like the ubiquitous "104" marking on ceramic capacitors) is the EIA standard for denoting capacitance in picofarads. While reading a code off a tiny component is straightforward, measuring an unmarked or faded capacitor and determining what its code should be requires a different approach. In this guide, we will build an ESP32-based capacitance meter that measures an unknown capacitor via an RC charging curve and automatically calculates and displays its correct 3-digit capacitor code on an OLED screen.
Understanding the 3-Digit Capacitor Code System
Before we wire up the microcontroller, we need to define the math the firmware will execute. The EIA 3-digit code works identically to the resistor color band system. The first two digits are the significant figures, and the third digit is the multiplier (the number of zeros to append), with the base unit always being picofarads (pF).
| Printed Code | Significant Figures | Multiplier (Zeros) | Value in pF | Value in nF / µF |
|---|---|---|---|---|
| 104 | 10 | 4 (0000) | 100,000 pF | 100 nF (0.1 µF) |
| 222 | 22 | 2 (00) | 2,200 pF | 2.2 nF |
| 471 | 47 | 1 (0) | 470 pF | 0.47 nF |
| 180 | 18 | 0 (none) | 18 pF | 0.018 nF |
For a deeper dive into standard markings and tolerances, the All About Circuits capacitor code reference is an excellent bench companion.
Parts List and Pin Mapping
This build targets the ESP32-DevKitC V4 equipped with the ESP32-WROOM-32E module. We use this specific variant because its 12-bit ADC and dedicated input-only pins (GPIO 34-39) are critical for accurate RC timing measurements without internal pull-up interference.
Bill of Materials
- MCU: ESP32-DevKitC V4 (ESP32-WROOM-32E)
- Display: 0.96" SSD1306 I2C OLED (128x64, 4-pin)
- R1 (Charge): 10kΩ 1% Metal Film Resistor (Brown/Black/Black/Red/Brown)
- R2 (Discharge): 1kΩ 1% Metal Film Resistor
- Switch: 6x6mm Tactile Pushbutton
- Test Leads: 2x Alligator clips or female headers for the capacitor under test (CUT)
ESP32 Pin Mapping
| Function | ESP32 GPIO | Destination | Notes |
|---|---|---|---|
| Charge Drive | GPIO 27 | R1 (10kΩ) to CUT | Push-pull output, drives 3.3V |
| Discharge Path | GPIO 26 | R2 (1kΩ) to CUT | Drains CUT to GND safely |
| Analog Read | GPIO 34 | CUT Positive Leg | Input-only ADC, no internal pull-ups |
| Trigger Button | GPIO 25 | Pushbutton to GND | Uses internal pull-up |
| I2C SDA | GPIO 21 | OLED SDA | Default Wire SDA |
| I2C SCL | GPIO 22 | OLED SCL | Default Wire SCL |
Wiring the RC Measurement Circuit
The measurement relies on the RC time constant formula: t = -R * C * ln(1 - Vc/Vs). When the capacitor voltage reaches 63.2% of the supply voltage, the elapsed time t in seconds equals R * C. Follow these steps to wire the bench setup:
- Prepare the Discharge Path: Connect GPIO 26 to one leg of the 1kΩ resistor. Connect the other leg of the 1kΩ resistor to the CUT positive test point.
- Prepare the Charge Path: Connect GPIO 27 to one leg of the 10kΩ resistor. Connect the other leg of the 10kΩ resistor to the same CUT positive test point.
- Wire the ADC Sense: Run a jumper from the CUT positive test point directly to GPIO 34. Do not add any series resistance here, as GPIO 34 has a high-impedance input and adding resistance will create an unintended voltage divider.
- Complete the Ground: Connect the CUT negative test point to the ESP32 GND pin.
- Wire the OLED and Button: Connect the SSD1306 VCC to 3.3V, GND to GND, SDA to 21, and SCL to 22. Wire the tactile button between GPIO 25 and GND.
Complete ESP32 Capacitor Code Firmware
The following C++ code is written for the Arduino IDE. It requires the Adafruit_GFX and Adafruit_SSD1306 libraries. The firmware handles the RC timing, calculates the picofarad value, converts it to the 3-digit EIA code, and includes strict error handling for out-of-range components.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
// Pin Definitions for ESP32-DevKitC V4
#define PIN_CHARGE 27
#define PIN_DISCHARGE 26
#define PIN_READ 34 // Input-only ADC pin
#define PIN_BUTTON 25
#define R_CHARGE 10000.0 // 10k ohms
#define ADC_MAX 4095.0
#define THRESHOLD 0.632 * ADC_MAX // 63.2% of 3.3V
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
pinMode(PIN_CHARGE, OUTPUT);
pinMode(PIN_DISCHARGE, OUTPUT);
pinMode(PIN_BUTTON, INPUT_PULLUP);
digitalWrite(PIN_CHARGE, LOW);
digitalWrite(PIN_DISCHARGE, LOW);
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.println("Capacitor Code Tester");
display.println("Press button to test");
display.display();
}
void loop() {
if (digitalRead(PIN_BUTTON) == LOW) {
measureAndDecode();
delay(500); // Debounce
}
}
void measureAndDecode() {
// 1. Discharge phase
digitalWrite(PIN_DISCHARGE, HIGH);
delay(300); // Allow time to drain
digitalWrite(PIN_DISCHARGE, LOW);
// 2. Charge phase
unsigned long startMicros = micros();
digitalWrite(PIN_CHARGE, HIGH);
while (analogRead(PIN_READ) < THRESHOLD) {
if (micros() - startMicros > 5000000) { // 5 second timeout
showError("ERR: TIMEOUT_CAP_TOO_LARGE");
return;
}
}
unsigned long elapsedMicros = micros() - startMicros;
digitalWrite(PIN_CHARGE, LOW);
// 3. Validate timing
if (elapsedMicros < 50) {
showError("ERR: SHORT_CIRCUIT_DETECTED");
return;
}
// 4. Calculate Capacitance
double t_seconds = elapsedMicros / 1000000.0;
double c_farads = t_seconds / R_CHARGE;
double c_picofarads = c_farads * 1e12;
String code = calculateCode(c_picofarads);
// 5. Display Results
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println("Measured Value:");
display.print(c_picofarads, 1); display.println(" pF");
display.println("\nCapacitor Code:");
display.setTextSize(2);
display.println(code);
display.setTextSize(1);
display.display();
}
String calculateCode(double pf) {
if (pf < 10) return "N/A (<10pF)";
int exp = 0;
double val = pf;
while (val >= 100) {
val /= 10;
exp++;
}
int sig = round(val);
if (sig >= 100) { sig /= 10; exp++; } // Handle 99.9 rounding to 100
return String(sig) + String(exp);
}
void showError(const char* msg) {
digitalWrite(PIN_CHARGE, LOW);
digitalWrite(PIN_DISCHARGE, HIGH); // Safe discharge on error
delay(200);
digitalWrite(PIN_DISCHARGE, LOW);
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println(msg);
display.display();
Serial.println(msg);
delay(2000);
}
Debugging: First Three Things to Check When It Fails
Embedded hardware debugging requires a systematic approach. If your OLED throws an error or the readings are wildly inaccurate, check these three failure modes in order:
- Verify GPIO 34 Input-Only Constraints: If your
analogRead(PIN_READ)always returns 0 or 4095 instantly, you may have wired the sense line to a standard GPIO (like GPIO 2) instead of the ADC1 input-only pins (32-39). Standard GPIOs have internal pull-up/pull-down resistors that will skew the RC curve. Consult the Espressif GPIO Documentation to confirm pin capabilities. - Check the Reference Resistor Tolerance: If the screen outputs "105" when testing a known 104 (100nF) capacitor, your 10kΩ charge resistor is likely a 5% carbon film type that is drifting under load. Swap it for a 1% metal film resistor. The math assumes exactly 10,000 ohms; a 10,500-ohm resistor will artificially inflate the calculated capacitance by 5%.
- Ensure Complete Discharge Before Timing: If you get the exact error string
"ERR: SHORT_CIRCUIT_DETECTED"but the capacitor isn't shorted, the capacitor likely held a residual charge from a previous test. Themicros()timer starts when the pin goes HIGH, but if the capacitor is already at 50% voltage, it crosses the 63.2% threshold almost instantly. Ensure the tactile button isn't being double-pressed too rapidly, bypassing the 300ms discharge delay.
Extending and Simplifying the Build
Depending on your bench needs, you can easily modify this circuit:
- Simplify (Headless Mode): If you don't want to wire the I2C OLED, strip out the
Adafruit_SSD1306library calls and replace them withSerial.printf("Code: %s\n", code.c_str());. This reduces the BOM cost by $4 and frees up GPIO 21/22 for other sensors. - Extend (Automated Bin Sorting): Add an SG90 micro servo to GPIO 13. Program the
calculateCode()function to trigger specific servo angles based on the decoded string (e.g., sweep left for "104", sweep right for "103"). Drop the tested capacitor into the slot, and the servo physically kicks it into the correct sorting bin.
Frequently Asked Questions
What does a 3-digit capacitor code like 104 actually mean?
The code "104" translates to 10 followed by 4 zeros in picofarads (100,000 pF). Because 1,000 pF equals 1 nanofarad (nF), and 1,000 nF equals 1 microfarad (µF), a 104 capacitor is exactly 100 nF or 0.1 µF. This is the most common decoupling capacitor value used in digital logic circuits.
Why do some ceramic capacitors have no code printed on them?
Manufacturers often omit the capacitor code on very small physical packages (like 0402 or 0603 SMD sizes) or on low-value picofarad capacitors where space is limited. Additionally, many bulk-through-hole ceramic disc capacitors are sold unmarked because they are packaged in labeled tape-and-reel or ammo packs at the factory. In these cases, an RC meter like the one built above is required to identify them.
Can this ESP32 circuit measure electrolytic capacitor codes?
Yes, but with caveats. Electrolytic capacitors are polarized and typically have much larger values (e.g., 10µF to 1000µF). A 10µF capacitor charged through a 10kΩ resistor will take roughly 0.1 seconds to reach the threshold, which the ESP32 handles easily. However, a 1000µF capacitor will take 10 seconds, triggering the "ERR: TIMEOUT_CAP_TOO_LARGE" error. To measure large electrolytics, swap the 10kΩ charge resistor for a 100Ω resistor and update the R_CHARGE constant in the code.
How accurate is the RC timing method for reading capacitor codes?
For standard ceramic capacitors between 100pF and 1µF, the RC timing method using the ESP32's micros() function is typically accurate within ±5%, assuming a 1% tolerance reference resistor and a stable 3.3V LDO supply. It is highly accurate for identifying standard E-series values (like distinguishing a 103 from a 104), but it is not a substitute for a dedicated LCR meter when measuring Equivalent Series Resistance (ESR) or precise dielectric absorption.






