The physical marking code for a 100 nanofarad (nF) capacitor is 104. This three-digit EIA (Electronic Industries Alliance) code translates to 10 × 10⁴ picofarads (100,000 pF), which equals 100 nF or 0.1 µF. But if you have ever designed a precision analog filter, a 555 timer astable circuit, or a snubber network, you already know that grabbing a random "104" ceramic capacitor off your bench is a gamble.
Depending on the dielectric material, temperature, and applied DC bias, that nominal 100nF capacitor might actually measure anywhere from 80nF to over 150nF in-circuit. To stop guessing and start measuring, we are going to build a high-resolution capacitance meter using an ESP32. By leveraging the ESP32's 32-bit architecture and microsecond timing resolution, we can measure the RC time constant of a resistor-capacitor network and calculate the exact real-world capacitance of your 104 components.
The 100 Nanofarad Capacitor Code and Ceramic Tolerances
Before we wire up the microcontroller, let us establish the baseline data for capacitor markings. The 104 code is the most ubiquitous capacitor marking in hobbyist and commercial electronics, serving as the standard bypass and decoupling capacitor for IC VCC pins.
| EIA Code | Multiplier | Picofarads (pF) | Nanofarads (nF) | Microfarads (µF) | Common Dielectric |
|---|---|---|---|---|---|
| 102 | 10 × 10² | 1,000 pF | 1 nF | 0.001 µF | C0G / NP0 |
| 103 | 10 × 10³ | 10,000 pF | 10 nF | 0.01 µF | X7R / C0G |
| 104 | 10 × 10⁴ | 100,000 pF | 100 nF | 0.1 µF | X7R / Y5V / Z5U |
| 224 | 22 × 10⁴ | 220,000 pF | 220 nF | 0.22 µF | X7R |
| 105 | 10 × 10⁵ | 1,000,000 pF | 1,000 nF | 1.0 µF | X5R / X7R |
Why do we need a meter for a 10-cent part? The answer lies in the dielectric. A 104 capacitor made with C0G/NP0 dielectric will hold its 100nF value within ±5% across extreme temperatures and voltages. However, a 104 made with Y5V dielectric (often found in cheap, unbranded kits) can lose up to 80% of its capacitance when a 5V DC bias is applied, and drift wildly with room temperature changes. Even standard X7R capacitors exhibit a DC bias effect where capacitance drops as voltage increases. Measuring the actual charge time gives you the functional truth of the component.
ESP32 Capacitance Meter: Parts and Pin Mapping
This project targets the ESP32-WROOM-32 DevKit v1 (30-pin variant). We use the ESP32 rather than an Arduino Uno because the ESP32's 32-bit `micros()` function and faster GPIO toggling yield significantly tighter timing margins for sub-microsecond RC measurements.
Bill of Materials
- Microcontroller: ESP32-WROOM-32 DevKit v1 (30-pin)
- R1 (Charge Resistor): 10.0 kΩ 1% Metal Film Resistor (Precision is critical here; a 5% carbon film will ruin your accuracy)
- R2 (Discharge Resistor): 220 Ω 5% Carbon Film Resistor (Limits discharge current to protect the ESP32 GPIO sink limit)
- DUT: 104 (100nF) Ceramic Capacitor (Device Under Test)
- Misc: Half-size breadboard, male-to-female Dupont jumper wires
Pin Mapping Table
| ESP32 GPIO | Code Alias | Direction | Hardware Connection |
|---|---|---|---|
| GPIO 12 | CHARGE_PIN | Output | Connects to 3.3V via R1 (10kΩ) to charge the DUT |
| GPIO 13 | READ_PIN | Input | High-Z input connected directly to the DUT positive leg |
| GPIO 14 | DISCHARGE_PIN | Output | Connects to DUT positive leg via R2 (220Ω) to rapidly dump charge to GND |
| GND | GROUND | Reference | Common ground for ESP32, R2, and DUT negative leg |
Compilable ESP32 C++ Code for RC Timing
The following firmware is written for the Arduino IDE (ESP32 core by Espressif Systems). It handles the GPIO toggling, microsecond timing, and includes explicit error handling for shorted or missing components. Ensure your board manager is set to the ESP32 Dev Module.
/*
* ESP32 RC Time Constant Capacitance Meter
* Target: ESP32-WROOM-32 DevKit v1
* Measures 100nF (104) and similar ceramic capacitors.
*/
#define CHARGE_PIN 12
#define READ_PIN 13
#define DISCHARGE_PIN 14
#define R_CHARGE 10000.0 // 10k ohms (use 1% tolerance resistor)
#define RC_CONSTANT 1.38629 // -ln(1 - 0.75) for ESP32 75% Vcc threshold
#define TIMEOUT_US 50000 // 50ms timeout to prevent infinite loops
unsigned long startTime;
unsigned long chargeTime;
float capacitance_nF;
void setup() {
Serial.begin(115200);
while(!Serial) { delay(10); }
Serial.println("ESP32 Capacitance Meter Initialized.");
Serial.println("Insert 104 (100nF) capacitor and press Reset.");
// Configure Pins
pinMode(CHARGE_PIN, OUTPUT);
pinMode(READ_PIN, INPUT);
pinMode(DISCHARGE_PIN, OUTPUT);
// Initial state: discharge capacitor
digitalWrite(CHARGE_PIN, LOW);
digitalWrite(DISCHARGE_PIN, LOW);
delay(100); // Allow time to fully discharge
}
void loop() {
// 1. Ensure capacitor is fully discharged
pinMode(DISCHARGE_PIN, OUTPUT);
digitalWrite(DISCHARGE_PIN, LOW);
digitalWrite(CHARGE_PIN, LOW);
delayMicroseconds(500);
// 2. Isolate discharge pin (High-Z) to prevent current bleed during charge
pinMode(DISCHARGE_PIN, INPUT);
// 3. Start charging and timing
startTime = micros();
digitalWrite(CHARGE_PIN, HIGH);
// 4. Wait for READ_PIN to cross the Schmitt trigger high threshold
while (digitalRead(READ_PIN) == LOW) {
chargeTime = micros() - startTime;
if (chargeTime >= TIMEOUT_US) {
Serial.println("[ERROR] CAP_READ_TIMEOUT: Charge time exceeded 50000us");
Serial.println("Action: Check for open circuit, missing capacitor, or wrong resistor value.");
// Abort charge and discharge safely
digitalWrite(CHARGE_PIN, LOW);
pinMode(DISCHARGE_PIN, OUTPUT);
digitalWrite(DISCHARGE_PIN, LOW);
delay(2000); // Wait before retrying
return;
}
}
// 5. Stop timing
chargeTime = micros() - startTime;
// 6. Calculate Capacitance
// C = t / (RC_CONSTANT * R)
// Result in Farads, multiply by 1e9 to get nanoFarads
capacitance_nF = (float)chargeTime / (RC_CONSTANT * R_CHARGE) * 1000000000.0;
Serial.print("Measured Charge Time: ");
Serial.print(chargeTime);
Serial.print(" us | Calculated Capacitance: ");
Serial.print(capacitance_nF, 2);
Serial.println(" nF");
// 7. Discharge for next cycle
digitalWrite(CHARGE_PIN, LOW);
pinMode(DISCHARGE_PIN, OUTPUT);
digitalWrite(DISCHARGE_PIN, LOW);
delay(1000); // 1 second between reads
}
Debugging: When the Meter Fails to Read
Embedded hardware debugging is rarely about the code; it is almost always about the physics of the breadboard. If your serial monitor outputs the exact error string:
[ERROR] CAP_READ_TIMEOUT: Charge time exceeded 50000us
The ESP32's internal watchdog did not trip, but the GPIO 13 never saw the 2.475V threshold within the 50ms window. Here are the first three things to check when it fails, ranked by probability:
- Breadboard Miswire or Open Circuit: The most common failure. If the 104 capacitor is not fully seated in the breadboard, or if you accidentally connected the READ_PIN to the ground rail instead of the RC junction, the pin will never see a voltage rise. Use your multimeter in continuity mode to verify the physical trace from the capacitor leg to GPIO 13.
- Wrong Capacitor Value (The "105" Mistake): If you accidentally grabbed a 105 (1µF) capacitor instead of a 104 (100nF), the RC time constant jumps from ~1.4ms to ~14ms. While 14ms is under our 50ms timeout, if your 10kΩ resistor is actually a 100kΩ resistor (misread color bands: brown-black-orange vs brown-black-yellow), the time constant becomes 140ms, triggering the timeout. Verify your resistor with a DMM.
- Shorted Capacitor or GPIO Pin Mapping Mismatch: If the capacitor is internally shorted, the voltage at the junction will remain at 0V. Alternatively, if you wired the circuit to GPIO 25 but left the code `#define READ_PIN 13`, the code will wait forever for a pin that is physically disconnected. Verify your `#define` aliases match your physical jumper wires.
For deeper debugging regarding ESP32 GPIO strapping pins and input hysteresis, consult the Espressif GPIO API Reference. Note that GPIO 12 is a strapping pin for flash voltage; if your specific DevKit variant struggles to boot with GPIO 12 tied to a 10k resistor, move the CHARGE_PIN to GPIO 26 and update the code accordingly.
Extending and Simplifying the Build
Depending on your bench needs, you might want to modify this reference design. Here is how to adapt the circuit for different scenarios.
How to Simplify the Build
If you want to reduce part count and are willing to sacrifice measurement speed, you can eliminate the DISCHARGE_PIN and R2 entirely. Simply connect the capacitor directly between GPIO 13 and GND, and use the 10kΩ R1 resistor to both charge and discharge the capacitor. To discharge, the code simply sets `CHARGE_PIN` to `LOW`. The downside is that discharging a 1µF capacitor through 10kΩ takes roughly 50ms (5 time constants), which slows down your serial output rate. For a simple 104 (100nF) capacitor, however, the discharge time is only ~5ms, making this simplification perfectly viable for quick bench checks.
How to Extend the Build
This RC timing method maxes out around 10µF. Beyond that, the charge currents drop into the microamp range, and the ESP32's GPIO input leakage current (which can be up to 50nA per the datasheet) begins to skew the math heavily. To extend this meter for large electrolytic capacitors (10µF to 4700µF):
- Drop the charge resistor to 100Ω to increase current.
- Add an external LM393 dual comparator to isolate the ESP32's GPIO leakage from the RC node.
- Wire the LM393 output to the ESP32 READ_PIN, and feed a precise 2.5V reference (via a voltage divider or TL431) to the comparator's inverting input.
By understanding the physical 100 nanofarad capacitor code and the underlying physics of the dielectrics, you transition from blindly trusting component markings to empirically verifying your circuit's behavior. Keep your 1% resistors calibrated, trust your oscilloscope, and never assume a Y5V capacitor is doing its job at 5V.






