An RGB LED packs three LEDs -- red, green, and blue -- into a single package. By controlling the brightness of each color channel independently with PWM, you can mix virtually any color. This is the same principle your computer monitor uses, just at a much smaller scale. In this guide, you will learn the difference between common cathode and common anode RGB LEDs, how to wire one up, and how to mix colors in code.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels and GPIO numbers may differ on your board -- always check your board's pinout diagram.
Need individually addressable LEDs? WS2812B NeoPixel strips let you set a different colour on each LED using a single data pin — ideal for light strips and status bars. For a single on/off indicator, a plain LED is simpler.
🔗Common Cathode vs. Common Anode
RGB LEDs come in two varieties:
| Type | Shared Pin | How It Works |
|---|---|---|
| Common cathode | GND (-) | Shared cathode connects to GND. Apply voltage to R/G/B pins to turn on each color. HIGH = on. |
| Common anode | VCC (+) | Shared anode connects to 3.3V. Pull R/G/B pins LOW to turn on each color. LOW = on (inverted logic). |
This guide uses a common cathode RGB LED. If you have a common anode type, connect the shared pin to 3.3V instead of GND, and invert your PWM values (use 255 - dutyCycle instead of dutyCycle).
Tip: To identify your RGB LED type, connect the longest leg to GND. If individual colors light up when you apply 3.3V through a resistor to the other legs, it is common cathode. If nothing lights up, try connecting the longest leg to 3.3V instead -- it is likely common anode.
🔗Key Specs
| Parameter | Value |
|---|---|
| Forward voltage | Red: $\approx 2.0\,\text{V}$, Green: $\approx 2.2\,\text{V}$, Blue: $\approx 3.0\,\text{V}$ |
| Forward current | $20\,\text{mA}$ per channel (typical max) |
| ESP32 GPIO output | $3.3\,\text{V}$ |
Note that the three color channels have different forward voltages. In practice, using $220\,\Omega$ resistors on all three channels works well and keeps the current within safe limits for both the LED and the ESP32 GPIO pins.
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| RGB LED (common cathode) | 1 | AliExpress | Amazon.de .co.uk .com | |
| 220 ohm resistor | 3 | One per color channel | AliExpress | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 5 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring (Common Cathode)
A common cathode RGB LED typically has four legs. The longest leg is the common cathode (GND).
| RGB LED Pin | Connection |
|---|---|
| Red leg | GPIO 25 through $220\,\Omega$ resistor |
| Common cathode (longest leg) | GND |
| Green leg | GPIO 26 through $220\,\Omega$ resistor |
| Blue leg | GPIO 27 through $220\,\Omega$ resistor |
Important: Each color channel needs its own resistor. Do not share a single resistor on the common pin -- this would cause the brightness of each color to change depending on how many colors are active, making color mixing unpredictable.
Note: GPIOs 34--39 on the ESP32 are input-only and cannot drive PWM output.
🔗Required Libraries
No external libraries are needed. The ESP32 Arduino core includes the LEDC (LED Control) functions for PWM.
🔗Code Example: Basic Color Mixing
This sketch cycles through several colors by setting different PWM duty cycles on each channel.
#define RED_PIN 25
#define GREEN_PIN 26
#define BLUE_PIN 27
void setup() {
// Attach each pin to LEDC with 5000 Hz and 8-bit resolution (0-255)
ledcAttach(RED_PIN, 5000, 8);
ledcAttach(GREEN_PIN, 5000, 8);
ledcAttach(BLUE_PIN, 5000, 8);
}
void setColor(int red, int green, int blue) {
ledcWrite(RED_PIN, red);
ledcWrite(GREEN_PIN, green);
ledcWrite(BLUE_PIN, blue);
}
void loop() {
setColor(255, 0, 0); // Red
delay(1000);
setColor(0, 255, 0); // Green
delay(1000);
setColor(0, 0, 255); // Blue
delay(1000);
setColor(255, 255, 0); // Yellow (red + green)
delay(1000);
setColor(255, 0, 255); // Magenta (red + blue)
delay(1000);
setColor(0, 255, 255); // Cyan (green + blue)
delay(1000);
setColor(255, 255, 255); // White (all channels)
delay(1000);
setColor(0, 0, 0); // Off
delay(1000);
}🔗Code Example: Smooth Rainbow Fade
This sketch smoothly transitions through the entire color spectrum using a hue-based approach.
#define RED_PIN 25
#define GREEN_PIN 26
#define BLUE_PIN 27
void setup() {
ledcAttach(RED_PIN, 5000, 8);
ledcAttach(GREEN_PIN, 5000, 8);
ledcAttach(BLUE_PIN, 5000, 8);
}
void setColor(int red, int green, int blue) {
ledcWrite(RED_PIN, red);
ledcWrite(GREEN_PIN, green);
ledcWrite(BLUE_PIN, blue);
}
void loop() {
// Fade from red to green
for (int i = 0; i <= 255; i++) {
setColor(255 - i, i, 0);
delay(5);
}
// Fade from green to blue
for (int i = 0; i <= 255; i++) {
setColor(0, 255 - i, i);
delay(5);
}
// Fade from blue to red
for (int i = 0; i <= 255; i++) {
setColor(i, 0, 255 - i);
delay(5);
}
}🔗How It Works
🔗Additive Color Mixing
RGB LEDs use additive color mixing -- the same principle as your screen. When two or more color channels are active simultaneously, your eyes perceive the blended result:
| Red | Green | Blue | Perceived Color |
|---|---|---|---|
| 255 | 0 | 0 | Red |
| 0 | 255 | 0 | Green |
| 0 | 0 | 255 | Blue |
| 255 | 255 | 0 | Yellow |
| 255 | 0 | 255 | Magenta |
| 0 | 255 | 255 | Cyan |
| 255 | 255 | 255 | White |
| 128 | 0 | 0 | Dim red |
| 255 | 128 | 0 | Orange |
By varying the duty cycle of each channel from 0 to 255, you can produce $256^3 = 16{,}777{,}216$ possible color combinations.
🔗PWM on the ESP32
The ESP32 does not have analogWrite(). Instead, it uses its LEDC (LED Control) peripheral. The ledcAttach(pin, frequency, resolution) function configures a pin for PWM output, and ledcWrite(pin, dutyCycle) sets the duty cycle. With 8-bit resolution, duty cycle values range from 0 (off) to 255 (fully on).
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| Only one or two colors work | Wrong wiring or bad LED leg connection | Check each leg individually by setting only one channel to 255 |
| Colors look wrong or inverted | Common anode LED with common cathode code | Invert the duty cycle: use 255 - value for each channel |
| LED does not light up at all | Common pin not connected | Verify the longest leg goes to GND (common cathode) or 3.3V (common anode) |
| White looks pinkish or bluish | Different forward voltages per channel | Adjust individual channel values -- try lower red, higher blue |
| Colors are too bright or dim | Resistor values too low or too high | $220\,\Omega$ is a good starting point for all channels |
🔗Next Steps
- Use a potentiometer or rotary encoder to dial in custom colors
- Control the RGB LED over WiFi with a web-based color picker
- Move on to WS2812B NeoPixel LEDs for individually addressable multi-LED strips
- Combine with a sensor to create a color-coded status indicator (for example, green for good air quality, red for poor)