Most guides on electronics projects for beginners hand you a wiring diagram and tell you to copy-paste some code, completely skipping the underlying physics. But if you want to move from assembling kits to actually designing circuits, you need to bridge the gap between software commands and hardware reality.

In this build, we are creating a potentiometer-controlled PWM (Pulse Width Modulation) LED fader. More importantly, we are going to use this simple circuit to validate Ohm's Law, calculate power dissipation, and understand the hard electrical limits of a microcontroller's GPIO pins. Grab your multimeter, and let's get to the bench.

Project Spec Sheet & Bill of Materials (BOM)

Before we wire anything, we need to know exactly what we are working with. Component selection in 2026 still heavily favors through-hole parts for prototyping, but the theory applies whether you're breadboarding or designing a custom PCB. Below is the exact BOM and the fundamental theory parameters you need to verify before applying power.

Component Part Number / Spec Qty Est. Cost (2026) Key Theory Parameter
Microcontroller Arduino Uno R3 (ATmega328P) 1 $24.00 5V Logic, 40mA max GPIO
LED Cree C503B-BAS (5mm Blue) 1 $0.35 Vf: 3.2V, If: 20mA (max)
Potentiometer Bourns 3852A (10kΩ Linear) 1 $1.85 Linear taper (B10K)
Resistor Yageo CFR-25 (220Ω 1/4W) 1 $0.10 5% tolerance, 0.25W max
Wiring 22 AWG Solid Core Hookup Wire 1 kit $12.00 Pre-tinned copper, PVC insul.
Bench Tip: Always check the LED datasheet for the exact Forward Voltage ($V_f$). A red LED might be 2.0V, while this blue Cree is 3.2V. Using the wrong $V_f$ in your calculations will result in a dim LED or a burnt-out component.

The Fundamentals: Calculating the Current-Limiting Resistor

Why do we need a resistor at all? An LED is a diode; it has a non-linear I-V curve. Once the voltage across it reaches its forward voltage ($V_f$), its internal resistance drops to near zero. If you connect a 3.2V LED directly to a 5V Arduino pin, the LED will try to pull infinite current, effectively creating a short circuit that will fry the microcontroller's GPIO pin before the LED even pops.

We use Ohm's Law to calculate the exact resistor needed to limit the current to a safe 20mA (0.02A).

Formula: $R = \frac{V_{source} - V_f}{I_f}$

Plugging in our values:
$R = \frac{5V - 3.2V}{0.02A} = \frac{1.8V}{0.02A} = 90\Omega$

So, why is there a 220Ω resistor in the BOM instead of a 90Ω one? This is where practical engineering overrides raw math. The ATmega328P chip on the Arduino Uno R3 has an absolute maximum rating of 40mA per I/O pin, but the recommended operating condition is 20mA. By stepping up to a standard 220Ω resistor, we drop the current to roughly 8.1mA ($1.8V / 220\Omega$). Modern high-efficiency LEDs are blindingly bright at 8mA, and running the pin at less than half its maximum capacity keeps the microcontroller cool and extends its lifespan.

Power Dissipation Check: We must also ensure the resistor won't overheat. Using the power formula $P = I^2 \times R$:
$P = (0.0081A)^2 \times 220\Omega = 0.014W$.
Since our Yageo resistor is rated for 1/4W (0.25W), we are well within the safe thermal limits.

Pin Mapping & Breadboard Wiring Steps

This project targets the Arduino Uno R3 and the Arduino Nano V3 (both use the ATmega328P and share the same pinout logic). Ensure your board is disconnected from USB before wiring.

Arduino Pin Component Function / Theory Note
5V Potentiometer Pin 1 Provides reference voltage for the ADC divider.
GND Potentiometer Pin 3 & LED Cathode Common ground return path.
A0 Potentiometer Wiper (Pin 2) ADC input (reads 0-5V as 0-1023).
9 Resistor (to LED Anode) PWM output (must be a ~ pin).
  1. Power Rails: Connect the Uno's 5V and GND pins to the red and blue breadboard rails, respectively.
  2. The Voltage Divider: Insert the 10kΩ potentiometer. Connect the left pin to 5V, the right pin to GND, and the middle pin (wiper) to Analog Pin A0. Turning the knob varies the voltage at the wiper from 0V to 5V.
  3. The Load: Place the 220Ω resistor with one leg in Digital Pin 9 and the other in an empty row. Insert the LED's Anode (long leg) into the same row as the resistor's free leg. Connect the Cathode (short leg) to the GND rail.

Complete Arduino Code (Target: Uno R3 / Nano V3)

Below is the complete, compilable C++ code. It includes pin definitions, bounds checking to prevent map() overflow, and serial debugging to help you verify the ADC readings in real-time.

/*
 * PWM LED Fader - Ohm's Law Validator
 * Target Boards: Arduino Uno R3, Nano V3 (ATmega328P)
 * Author: ElectricalFlux Bench Team
 */

// Pin Definitions
#define POT_PIN A0
#define LED_PIN 9
#define BAUD_RATE 115200

// Smoothing buffer for cheap potentiometer noise
const int numReadings = 10;
int readings[numReadings];
int readIndex = 0;
int total = 0;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(BAUD_RATE);
  
  // Initialize smoothing array
  for (int i = 0; i < numReadings; i++) {
    readings[i] = 0;
  }
  
  // Brief startup indicator
  digitalWrite(LED_PIN, HIGH);
  delay(200);
  digitalWrite(LED_PIN, LOW);
}

void loop() {
  // Subtract the last reading
  total = total - readings[readIndex];
  
  // Read from the sensor with basic bounds protection
  int rawValue = analogRead(POT_PIN);
  if (rawValue < 0) rawValue = 0;
  if (rawValue > 1023) rawValue = 1023;
  
  readings[readIndex] = rawValue;
  total = total + readings[readIndex];
  readIndex = (readIndex + 1) % numReadings;
  
  // Calculate the average
  int average = total / numReadings;
  
  // Map 10-bit ADC (0-1023) to 8-bit PWM (0-255)
  int pwmValue = map(average, 0, 1023, 0, 255);
  
  // Apply PWM to the LED
  analogWrite(LED_PIN, pwmValue);
  
  // Serial Debug Output (Throttled to avoid flooding the buffer)
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 100) {
    Serial.print("ADC Avg: ");
    Serial.print(average);
    Serial.print(" | PWM Out: ");
    Serial.println(pwmValue);
    lastPrint = millis();
  }
  
  delay(10); // Stability delay
}

Debugging: First Three Things to Check When It Fails

Hardware rarely works perfectly on the first try. If your LED stays dark, is stuck at full brightness, or the Serial Monitor outputs garbage, follow this ranked decision path.

Symptom: The Serial Monitor prints ⸮⸮⸮⸮⸮ or random wingdings instead of data.
Fix: This is a baud rate mismatch. The code uses 115200. Ensure the dropdown in the bottom right corner of the Arduino IDE Serial Monitor is set to 115200, not the default 9600.

1. The LED is Backwards (Polarity Fault)

How to check: LEDs are polarized. If the circuit is complete but the LED is off, look at the legs. The short leg (and the flat side of the LED plastic dome) is the Cathode, which must go to GND. If you wired it backwards, the diode is reverse-biased and blocking current. Swap the legs.

2. Potentiometer Wired as a Variable Resistor

How to check: Open the Serial Monitor. If the 'ADC Avg' value is jumping wildly between 0 and 1023 even when you aren't touching the knob, your wiper is floating. You likely connected the middle pin and one outer pin, but left the other outer pin disconnected. For a voltage divider (which the analogRead() function requires), you must wire 5V to one outer pin, GND to the other outer pin, and A0 to the middle pin.

3. Using a Non-PWM Capable Pin

How to check: If the LED turns on and off, but doesn't fade smoothly (it just jumps between dim and bright), you might have moved the LED to a pin like Digital 8 or 13. The analogWrite() function only outputs hardware PWM on pins marked with a tilde (~) on the Uno R3 (Pins 3, 5, 6, 9, 10, 11). Move the anode resistor back to Pin 9.

How to Extend or Simplify the Build

Every good beginner project should have a clear path forward once the baseline is working. Here is how you can modify this circuit based on your current skill level or project goals.

Simplifying the Build (No Potentiometer)

If you are out of potentiometers or just want to test the LED and resistor theory, remove the pot entirely. Delete the analogRead logic and replace the loop() with a simple sine-wave generator using millis() and the math.h library. This removes the ADC variable and lets you focus purely on the visual output of the PWM duty cycle.

Extending the Build (Driving High-Power Loads)

The ATmega328P can only source 20mA safely. What if you want to fade a 12V LED strip that pulls 2 Amps? You cannot wire that directly to Pin 9.

To extend this project, you must introduce a logic-level N-Channel MOSFET (like the IRLZ44N). Connect Pin 9 to the MOSFET's Gate (through a 100Ω gate resistor to prevent ringing), the Source to GND, and the Drain to the negative terminal of your 12V LED strip. The Arduino still outputs a 5V PWM signal, but the MOSFET acts as a high-speed digital switch, handling the heavy 12V/2A current path while keeping your microcontroller completely isolated from the high-power load. This is the exact bridge between low-voltage embedded theory and real-world power electronics.