Every phone and computer has a built-in colour picker, and a web page can use it with a single line of HTML. In this lesson the ESP32 serves a page with a colour picker and a few favourite colours. Whatever you choose, the RGB LED module shows it a moment later, which is how many smart bulbs work.
What you'll learn
- How to use the HTML colour picker
- How a colour is written as a hex code like #FF8800
- How to turn a hex code into red, green and blue values on the ESP32
- How to dim an LED with
analogWrite()on the ESP32
What you need
- ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
- RGB LED module (45-in-1 Sensor Kit)
- 3 resistors, 220 Ω (ESP32 IoT Starter Kit)
- A 2.4 GHz Wi-Fi network
- Breadboard and jumper wires (ESP32 IoT Starter Kit)
Wiring

| From | Through | To |
|---|---|---|
| ESP32 GPIO 27 | 220 Ω resistor | RGB module R |
| ESP32 GPIO 26 | 220 Ω resistor | RGB module G |
| ESP32 GPIO 25 | 220 Ω resistor | RGB module B |
| RGB module − | GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
The sketch
Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details (2.4 GHz only). Open the Serial Monitor at 115200 baud to see the address, then open it in a browser on a phone or computer on the same Wi-Fi.
#include <WiFi.h>
#include <WebServer.h>
const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const int RED_PIN = 27;
const int GREEN_PIN = 26;
const int BLUE_PIN = 25;
WebServer server(80);
const char PAGE[] = R"rawliteral(
<!DOCTYPE html><html><head><meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'><title>LED colour</title>
<style>
body { font-family: sans-serif; padding: 16px; text-align: center; }
input[type=color] { width: 160px; height: 160px; border: none; }
.fav { width: 48px; height: 48px; border-radius: 50%; border: 2px solid #ccc; margin: 6px; }
</style></head><body>
<h1>LED colour</h1>
<input type='color' id='picker' value='#ff8800'>
<p>
<button class='fav' style='background:#ff0000' onclick="pick('#ff0000')"></button>
<button class='fav' style='background:#00ff00' onclick="pick('#00ff00')"></button>
<button class='fav' style='background:#0000ff' onclick="pick('#0000ff')"></button>
<button class='fav' style='background:#ffffff' onclick="pick('#ffffff')"></button>
<button class='fav' style='background:#000000' onclick="pick('#000000')"></button>
</p>
<script>
function pick(colour) {
document.getElementById('picker').value = colour;
fetch('/set?c=' + colour.substring(1)); // send "ff8800" without the #
}
document.getElementById('picker').oninput = function () { pick(this.value); };
</script></body></html>
)rawliteral";
void setColour(long rgb) {
analogWrite(RED_PIN, (rgb >> 16) & 0xFF); // first two hex digits
analogWrite(GREEN_PIN, (rgb >> 8) & 0xFF); // middle two
analogWrite(BLUE_PIN, rgb & 0xFF); // last two
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
setColour(0xFF8800);
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.print("Open http://");
Serial.println(WiFi.localIP());
server.on("/", []() { server.send(200, "text/html", PAGE); });
server.on("/set", []() {
String hex = server.arg("c"); // for example "ff8800"
if (hex.length() == 6) {
long rgb = strtol(hex.c_str(), NULL, 16);
setColour(rgb);
Serial.println("Colour #" + hex);
}
server.send(200, "text/plain", "ok");
});
server.begin();
}
void loop() {
server.handleClient();
}
How it works
<input type='color'> makes the browser show its own colour picker. It reports the colour as a hex code: a # followed by six hexadecimal digits, two each for red, green and blue. In hexadecimal, the digits go from 0 to 9 and then A to F, so two digits count from 00 to FF, which is 0 to 255.
The page sends the six digits to the ESP32. strtol(…, 16) turns the text into one number, and the bit-shift operator >> slides it to the right to pick out each pair: shifting by 16 leaves the red part, by 8 the red and green, and & 0xFF keeps only the last two hex digits. analogWrite() then sets each colour's brightness with PWM.
Try this next
- Add a "party" button that cycles through colours on the ESP32.
- Remember the last colour with the Preferences library, so the LED comes back in the same colour after a power cut.
- Add a brightness slider that scales all three colours.
Troubleshooting
- It never connects to Wi-Fi: check the network name and password (both are case-sensitive) and make sure the network is 2.4 GHz.
- The page doesn't open: the phone or computer must be on the same Wi-Fi as the ESP32, not on mobile data.
- The colours are mixed up (red shows as green): swap the wires on R and G, or swap the pin numbers in the sketch.
-
White looks pinkish or bluish: the three colours have different brightness. Lower the strongest one, for example with
* 0.7. -
Black doesn't switch the LED off: you may have a common-anode LED; connect its common pin to 3V3 and use
255 - value.