The direct answer: Capacitor code 104 means 0.1 µF (100 nF or 100,000 pF). It is the most common decoupling and bypass capacitor used in embedded electronics. If you are building an ESP32 or Arduino project and experiencing random resets, ADC jitter, or the dreaded Brownout detector was triggered UART error, a missing, failed, or incorrectly specified 104 capacitor on your power rails is usually the culprit.

In this guide, we will break down the math behind the 104 code, explain why dielectric material matters more than the printed number, and walk through a complete ESP32 relay-driver build where proper 104 decoupling is the difference between a stable deployment and a continuous reset loop.

The 104 Capacitor Spec Sheet and Code Math

Ceramic capacitors use a three-digit EIA (Electronic Industries Alliance) code to denote capacitance in picofarads (pF). The first two digits are the significant figures, and the third digit is the multiplier (number of zeros to add).

For 104: The significant digits are 10. The multiplier is 4 (add four zeros).
10 + 0000 = 100,000 pF.
Since 1,000 pF = 1 nF, and 1,000 nF = 1 µF, we divide by 1,000 twice to get 0.1 µF.

Common Ceramic Capacitor Codes and Applications
Code Picofarads (pF) Nanofarads (nF) Microfarads (µF) Primary Embedded Application
101 100 pF 0.1 nF 0.0001 µF RF filtering, oscillator tank circuits (MHz range)
102 1,000 pF 1 nF 0.001 µF High-frequency I2C/SPI line filtering, EMI suppression
103 10,000 pF 10 nF 0.01 µF Mid-frequency decoupling, audio coupling, snubber networks
104 100,000 pF 100 nF 0.1 µF Standard digital IC decoupling (VCC to GND), bypassing logic gates
105 1,000,000 pF 1,000 nF 1.0 µF Bulk local energy storage, low-frequency power rail smoothing
224 220,000 pF 220 nF 0.22 µF Alternative decoupling for high-draw SoCs, I2C pull-up filtering
Expert Insight: Dielectric Matters More Than the Code
A 104 capacitor printed on a Y5V dielectric can lose up to 80% of its capacitance at room temperature under a 3.3V DC bias, effectively becoming a 102 (1nF) capacitor. For embedded power rail decoupling, always source X7R or X5R dielectrics. They maintain stable capacitance across temperature and voltage variations. Check the SparkFun capacitor guide for deeper dielectric breakdowns.

Project Build: ESP32 Relay Driver with 104 Decoupling

The ESP32-WROOM-32E is notorious for current spikes. During 802.11b WiFi transmission, the chip can draw transient spikes of 350mA to 500mA. The onboard AMS1117-3.3 LDO cannot react fast enough to this high-frequency transient. Without a low-ESL (Equivalent Series Inductance) 104 capacitor placed physically close to the ESP32's 3V3 and GND pins, the local voltage sags, triggering the internal brownout detector.

We will build a relay switching circuit that explicitly monitors for this brownout condition and uses proper decoupling to prevent it.

Parts List

  • MCU: ESP32-DevKitC V4 (featuring the ESP32-WROOM-32E module)
  • Relay Module: Songle SRD-05VDC-SL-C (5V coil, opto-isolated input variant)
  • Decoupling Capacitor 1: 104 (0.1 µF) X7R Ceramic Capacitor (SMD 0805 or 2.54mm through-hole disc)
  • Bulk Capacitor: 10 µF 16V Electrolytic Capacitor
  • Power Supply: 5V 2A USB-C wall adapter with a 20AWG USB cable (avoid cheap 28AWG cables)

Pin Mapping Table

ESP32-DevKitC V4 Pin Relay Module Pin Capacitor Connection Function
3V3 104 Cap (+) & 10µF Cap (+) MCU Logic Power Rail
GND GND 104 Cap (-) & 10µF Cap (-) Common Ground Reference
GPIO 26 IN (Signal) Relay Coil Trigger (Active LOW)
VIN (5V) VCC (Coil Power) Relay Coil 5V Supply

Compilable Code and Error Handling

This code targets the ESP32-DevKitC V4 board in the Arduino IDE (Board Manager: esp32 by Espressif Systems v2.0.14+). It toggles the relay every 3 seconds while actively monitoring the hardware reset reason. If the 104 capacitor is missing and the rail sags, the ESP32 will reset and this code will catch the exact error on the next boot.

#include <rom/rtc.h>

// Pin Definitions
const int RELAY_PIN = 26;
const int STATUS_LED = 2; // Built-in blue LED on DevKitC V4

// Function to decode hardware reset reasons
void printResetReason(RESET_REASON reason) {
  switch (reason) {
    case 1:  Serial.println('POWERON_RESET'); break;
    case 3:  Serial.println('SW_RESET'); break;
    case 4:  Serial.println('OWDT_RESET'); break;
    case 5:  Serial.println('DEEPSLEEP_RESET'); break;
    case 6:  Serial.println('SDIO_RESET'); break;
    case 7:  Serial.println('TG0WDT_SYS_RESET'); break;
    case 8:  Serial.println('TG1WDT_SYS_RESET'); break;
    case 9:  Serial.println('RTCWDT_SYS_RESET'); break;
    case 10: Serial.println('INTRUSION_RESET'); break;
    case 11: Serial.println('TGWDT_CPU_RESET'); break;
    case 12: Serial.println('SW_CPU_RESET'); break;
    case 13: Serial.println('RTCWDT_CPU_RESET'); break;
    case 14: Serial.println('EXT_CPU_RESET'); break;
    case 15: Serial.println('RTCWDT_BROWN_OUT_RESET'); break; // The target error
    case 16: Serial.println('RTCWDT_RTC_RESET'); break;
    default: Serial.println('NO_MEAN');
  }
}

void setup() {
  Serial.begin(115200);
  delay(500); // Allow serial monitor to connect
  
  Serial.println('\n--- ESP32 Boot Sequence ---');
  Serial.print('CPU0 reset reason: ');
  printResetReason(rtc_get_reset_reason(0));
  
  // Explicitly check for the brownout condition
  if (rtc_get_reset_reason(0) == RTCWDT_BROWN_OUT_RESET) {
    Serial.println('CRITICAL: Brownout detector was triggered!');
    Serial.println('Action: Check 104 (0.1uF) capacitor on 3V3/GND pins.');
    Serial.println('Action: Verify USB cable gauge and 5V source ampacity.');
  }

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(STATUS_LED, OUTPUT);
  
  // Ensure relay starts in the OFF state (Active LOW for most opto-modules)
  digitalWrite(RELAY_PIN, HIGH); 
  digitalWrite(STATUS_LED, LOW);
  
  Serial.println('System initialized. Toggling relay every 3s.');
}

void loop() {
  // Turn Relay ON
  digitalWrite(RELAY_PIN, LOW); 
  digitalWrite(STATUS_LED, HIGH);
  Serial.println('Relay ENGAGED');
  delay(3000);
  
  // Turn Relay OFF
  digitalWrite(RELAY_PIN, HIGH); 
  digitalWrite(STATUS_LED, LOW);
  Serial.println('Relay DISENGAGED');
  delay(3000);
}

Debugging: First Three Things to Check When It Fails

If your serial monitor prints the exact string Brownout detector was triggered (or the code above catches RTCWDT_BROWN_OUT_RESET), your 3.3V rail dipped below the ESP32's default threshold of ~2.43V. According to Random Nerd Tutorials, this is rarely a software bug; it is almost always a power delivery failure. Here are the first three things to check, ranked by probability.

1. Inspect the 104 Capacitor Placement and Health

The Fix: The 104 (0.1 µF) capacitor must be placed as physically close as possible to the VCC and GND pins of the ESP32 module. If you are using a breadboard, plug it directly into the power rails adjacent to the DevKitC headers. If you are on a custom PCB, route it directly to the module pads with short, wide traces.
The Gotcha: Cheap, unbranded ceramic capacitors from bulk kits are often mislabeled. Use a multimeter with a capacitance setting (or an LCR meter) to verify the 104 cap actually reads between 90nF and 110nF. If it reads 2nF, it is actually a 102 and will not suppress the WiFi TX transient.

2. Measure the USB Cable Voltage Drop

The Fix: A standard 28AWG USB cable has a resistance of about 0.23 ohms per meter. If your ESP32 and relay draw a combined 600mA, a 1-meter cable will drop nearly 0.3V before the power even reaches the DevKit's USB port. If your wall adapter outputs 4.9V, the DevKit sees 4.6V. The AMS1117 LDO requires a dropout voltage of ~1V to regulate 3.3V cleanly.
The Measurement: Use your multimeter to measure the voltage directly at the ESP32's 5V (VIN) and GND header pins while the relay is engaged and WiFi is transmitting. If it reads below 4.7V, replace the cable with a 20AWG or thicker USB cable.

3. Check the Relay Flyback Diode

The Fix: When the relay coil is de-energized, the collapsing magnetic field generates a high-voltage reverse spike (inductive kickback). If your relay module lacks a flyback diode (or the diode is blown), this spike travels back through the GND plane, momentarily lifting the ESP32's GND reference and causing a relative brownout on the 3V3 rail.
The Verification: Ensure your relay module has a 1N4148 or 1N4007 diode soldered in reverse bias across the coil pins. If building a bare-bones circuit, you must add this diode manually.

Safety Note: When debugging power rail sags with an oscilloscope, ensure your scope probe ground clip is attached to the circuit GND, not to a live AC mains wire if you are switching AC loads with the relay. Use isolated probes or differential probes when measuring across non-isolated AC/DC boundaries.

Extending and Simplifying the Build

Once your 104 decoupling is verified and the brownout errors are eliminated, you can scale this project up or down based on your deployment environment.

How to Extend: Adding I2C Sensors

If you add an I2C sensor (like a BME280 or SHT31) to the same 3.3V rail, the ESP32's WiFi spikes can induce noise onto the SDA/SCL lines, causing I2C bus lockups.
The Extension: Add a second 104 capacitor directly across the VCC and GND pins of the I2C sensor module itself. Furthermore, place 103 (10nF) ceramic capacitors in parallel with your 4.7kΩ I2C pull-up resistors to filter out high-frequency EMI without degrading the bus rise-time beyond the I2C specification.

How to Simplify: Switching to Solid State

Mechanical relays require high coil current (70mA - 100mA) and generate massive inductive kickback. If you are only switching low-voltage DC loads or small AC loads, replace the Songle mechanical relay with a Solid State Relay (SSR) like the Omron G3MB-202P.
The Simplification: An SSR draws less than 15mA from the GPIO pin, requires no flyback diode, and eliminates the massive current transient that causes the 3.3V rail to sag in the first place. This often allows you to remove the bulk 10µF electrolytic capacitor, leaving only the high-frequency 104 ceramic capacitor to handle the ESP32's internal RF noise.