This project is inspired by my friends at DimSumLabs who created a web server to control the lab’s RGB LED lights, using the ESP8266 wifi module and NodeMCU to program it. I made a smaller version, using the familiar Arduino IDE with an ESP8266 extension. I did use the excellent javascript of the above project (index.html). I made a nicer version of this below project into a wifi lantern, using a neopixel strip which is driven from only 1 pin, vs 3 pins needed below to drive each RGB channel.


As i want this project to be portable, i chose to run a dedicated Access Point on the ESP8266 module: it does not need to connect to any existing wifi network; it creates its own hotspot. It also runs a simple DNS server, so after connecting to the wifi network (“RGB” in below code, no password) our webpage can be accessed simply at http://rgb

The DNS server can also be configured as a ‘captive portal’ so that any URL is redirected to our webpage but that did not seem a good idea for this project.


Most of the sketch consists of the webpage that will be displayed on above URL. It contains a nice javascript to display the full range of RGB colours, and when a user clicks or touches any colour on this screen it passes on the 3 RGB values to the webserver as 3 arguments of a POST request.

The challenge was to find a third I/O pin on the ESP8266 ESP-01 module. It only has GPIO0 and GPIO2 available. However, the Serial pins TX and RX can also be used as GPIO pins (GPIO1 and GPIO3 as explained here). But they cannot be used for Serial connection at the same time, so my final sketch below does not use Serial. I did use Serial to debug, but the lines are commented out below.


In the setup() it does a little test: fading in&out the Red Green Blue LED’s one by one. If you power this by only 3V, the BLUE LED will not be very bright, as it typically has a forward voltage of 3.2V while Red and Green are already happy with 2V. With 2 new AA batteries i got a decent blue.


The ESP8266’s PWM is 10 bit, so the range is 0-1023 instead of the standard Arduino 0-255.

My RGB LED has a common positive/anode, so the PWM value of 1023 corresponds to LED off. The  webpage in the sketch uses the same convention to pass the 3 RGB parameters.

Minimal hardware:

  • ESP8266 ESP-01 module (4×2 pins as above)  you do need a USB-to-Serial converter to program it
  • RGB LED type 5050 (common anode)
  • power supply (3-3.5V), e.g. 2x AA batteries (or 5V USB power or LiPo, both need a voltage regulator)
  • optional: button to reset


  • Vcc to 3V (e.g. 2xAA batteries)
  • GND to GND
  • GPIO0 to Red
  • GPIO2 to Green
  • GPIO3=RX to Blue
  • CH_PD to Vcc (or solder a pull-up resistor on the ESP-01 module)
  • optional: push button from GND to RST to reset the module

A video to show how it works: tap a colour on the tablet to adjust, or even trace a line.

Here is the code as described above:


/* RGB web server with ESP8266-01
* only 2 GPIOs available: 0 and 2
* but RX and TX can also be used as: 3 and 1
* we use 0=red 2=green 3=blue
* analogWrite with values received from web page
* web server with captive portal works but better use fixed domain: http://rgb
* web page returns POST request with 3 RGB parameters
* web page inspired by
* Serial Monitor for debugging but interferes with Blue channel GPIO3=RX
* switch off Serial for full RGB

#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>

const char *ssid = "RGB";
// const char *password = "87654321";

const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);

String webpage = ""
"<!DOCTYPE html><html><head><title>RGB control</title><meta name='mobile-web-app-capable' content='yes' />"
"<meta name='viewport' content='width=device-width' /></head><body style='margin: 0px; padding: 0px;'>"
"<canvas id='colorspace'></canvas></body>"
"<script type='text/javascript'>"
"(function () {"
" var canvas = document.getElementById('colorspace');"
" var ctx = canvas.getContext('2d');"
" function drawCanvas() {"
" var colours = ctx.createLinearGradient(0, 0, window.innerWidth, 0);"
" for(var i=0; i <= 360; i+=10) {"
" colours.addColorStop(i/360, 'hsl(' + i + ', 100%, 50%)');"
" }"
" ctx.fillStyle = colours;"
" ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);"
" var luminance = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);"
" luminance.addColorStop(0, '#ffffff');"
" luminance.addColorStop(0.05, '#ffffff');"
" luminance.addColorStop(0.5, 'rgba(0,0,0,0)');"
" luminance.addColorStop(0.95, '#000000');"
" luminance.addColorStop(1, '#000000');"
" ctx.fillStyle = luminance;"
" ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);"
" }"
" var eventLocked = false;"
" function handleEvent(clientX, clientY) {"
" if(eventLocked) {"
" return;"
" }"
" function colourCorrect(v) {"
" return Math.round(1023-(v*v)/64);"
" }"
" var data = ctx.getImageData(clientX, clientY, 1, 1).data;"
" var params = ["
" 'r=' + colourCorrect(data[0]),"
" 'g=' + colourCorrect(data[1]),"
" 'b=' + colourCorrect(data[2])"
" ].join('&');"
" var req = new XMLHttpRequest();"
"'POST', '?' + params, true);"
" req.send();"
" eventLocked = true;"
" req.onreadystatechange = function() {"
" if(req.readyState == 4) {"
" eventLocked = false;"
" }"
" }"
" }"
" canvas.addEventListener('click', function(event) {"
" handleEvent(event.clientX, event.clientY, true);"
" }, false);"
" canvas.addEventListener('touchmove', function(event){"
" handleEvent(event.touches[0].clientX, event.touches[0].clientY);"
"}, false);"
" function resizeCanvas() {"
" canvas.width = window.innerWidth;"
" canvas.height = window.innerHeight;"
" drawCanvas();"
" }"
" window.addEventListener('resize', resizeCanvas, false);"
" resizeCanvas();"
" drawCanvas();"
" document.ontouchmove = function(e) {e.preventDefault()};"
" })();"


void handleRoot() {
// Serial.println("handle root..");
String red = webServer.arg(0); // read RGB arguments
String green = webServer.arg(1);
String blue = webServer.arg(2);

analogWrite(0, red.toInt());
analogWrite(2, green.toInt());
analogWrite(3, blue.toInt());

// Serial.println(red.toInt()); // for TESTING
// Serial.println(green.toInt()); // for TESTING
// Serial.println(blue.toInt()); // for TESTING
webServer.send(200, "text/html", webpage);


void setup() {

pinMode(0, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);

analogWrite(0, 1023);
analogWrite(2, 1023);
analogWrite(3, 1023);

// Serial.begin(9600);
// Serial.println();

WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));

// if DNSServer is started with "*" for domain name, it will reply with provided IP to all DNS request
dnsServer.start(DNS_PORT, "rgb", apIP);

webServer.on("/", handleRoot);




void loop() {



void testRGB() { // fade in and out of Red, Green, Blue

analogWrite(0, 1023); // R off
analogWrite(2, 1023); // G off
analogWrite(3, 1023); // B off

fade(0); // R
fade(2); // G
fade(3); // B


void fade(int pin) {

for (int u = 0; u < 1024; u++) {
analogWrite(pin, 1023 - u);
for (int u = 0; u < 1024; u++) {
analogWrite(pin, u);

26 thoughts on “RGB control over wifi (with ESP8266 ESP-01)

  • 26/03/2016 at 11:01

    script looks great! but it won’t compile in the arduino ide. it reads the html (string) as normal code to compile. any idea on how to fix this?

    • 26/03/2016 at 21:48

      Hi Mano, thanks for the feedback. Something seems to happen to the code when copied into HTML: the double quotes are changed into special symbols. Can you replace all the double quotes in the code with normal straight double quotes: ”
      This seems to solve it; the String should changes colour to all blue.
      And it seems the minus sign in (1023 – u) also changed into something different so change that back to a normal minus.
      Hope that works for you, let me know. Just for reference, i’m using IDE 1.6.5
      I need to find a better way to include code in my posts.

  • 27/03/2016 at 00:55

    INDEED! It compiled now. but the esp keeps resetting…. any idea?

  • 27/03/2016 at 00:57

    this is the serial output:

    ets Jan 8 2013,rst cause:2, boot mode:(3,7)

    load 0x4010f000, len 1264, room 16
    tail 0
    chksum 0x42
    csum 0x42

  • 27/03/2016 at 09:52

    I finally got it to work. i also had to change the ‘ characters.

    is it correct that the colors are all mixed up? blue is yellow and yellow is kinda blue…
    and blue is very hard to get. Also light is dark and vice versa. Is there maybe a way to change to another colorpicker?

    • 28/03/2016 at 08:57

      hi Mano, it depends on the RGB LED you are using. As i write above, there are 2 types: common positive or common negative. The code is good for common positive: an analogWrite of 1023 turns the colour off. If you’re using a common negative type, try changing the 3 lines in the handleRoot function to:
      analogWrite(0, 1023 – red.toInt());
      analogWrite(2, 1023 – green.toInt());
      analogWrite(3, 1023 – blue.toInt());

      If you want to use another colour picker, you have to change the javascript code and make sure it passes on 3 parameters for the R G B intensity.

  • 29/03/2016 at 01:53

    Yes i got it right now. I indeed changed those 3 lines. also i made an extra piece of code making sure that when the page reloads, the color isn’t reset to white.
    Or in other words, when the value’s send by the webinterface are empty, it does nothing.

    void handleRoot() {
    Serial.println(“handle root..”);
    String red = webServer.arg(0); // read RGB arguments
    String green = webServer.arg(1);
    String blue = webServer.arg(2);

    if((red != “”) && (green != “”) && (blue != “”)){
    analogWrite(REDPIN,1023 – red.toInt());
    analogWrite(GREENPIN,1023 – green.toInt());
    analogWrite(BLUEPIN,1023 – blue.toInt());
    webServer.send(200, “text/html”, webpage);

  • 28/04/2016 at 02:45

    Reading the comments i was successful in complying & uploading the code to Nodemcu board from Arduino IDE . I can connect to broadcasted SSID “RGB” but i am not able to access “http://rgb” instead when i connect to “” i get display of full range of RGB colours and the RGB led turns white(all three LED high) . Can you please guide me in getting it right. I am using 5mm comman anode RGB led.

    Thank You.

    • 28/04/2016 at 07:24

      Hi, if the RGB LED turns white when you connect to the page, that means it’s working; the page returns empty values (interpreted as zeros) by default. Now if you touch any colour on that page (or click if you don’t have a touch screen) then the LED should change colour.
      The access via http://rgb is a DNS feature (see sketch line dnsServer.start), try from another phone/tablet/laptop with another browser to see if that works. You need the full URL: http://rgb

  • 28/04/2016 at 11:59

    Thanks a lot for the reply, I tied everything but still its not working. I think still character issue is there as we copy the code from HTML page. Can u post your code in “” or send a mail to ‘’.

    Appreciate your help.

    Thank You.

    • 29/04/2016 at 12:53

      Hey please can you help.

  • 18/06/2016 at 02:53

    hey there
    i tried to use your skript on my esp8266.

    It all seems to work fine exept the webpage:
    When i go to http://rgb i can only see a blank page, when i take look on the source code it looks like this:

    body style=‘margin

    not like

    body style=’margin

    any suggestions on this?

    • 18/06/2016 at 16:44

      hi, this is the same issue as above; when i paste the code from the Arduino IDE into WordPress, some characters are messed up, such as the single/double quotes so you will have to change them in your version back to the standard ‘ and “

  • Pingback: WiFi lantern with RGB strip (ESP8266) – Cassiopeia Ltd

  • 13/09/2016 at 22:47


    I try to complier code but there are errors

    Arduino: 1.6.5 (Windows 8.1), Map: “Generic ESP8266 Module, Serial, 80MHz, 40MHz, DIO, 115200, 512K (64K spiffs) ck, Disabled, None”

    lamp: 134: error: stray ‘\’ in program
    lamp: 168: error: stray ‘\’ in program
    lamp: 18: error: ‘u201cRGB’ was Not Declared in this scope
    lamp: 26: error: ‘u201c’ was Not Declared in this scope
    lamp: 28: error: ‘padding’ does not name a type of
    lamp: 28: error: ‘u2019’ does not name a type of
    lamp: 89: error: expected unqualified-id before ‘)’ token
    lamp: 89: error: ‘u201d’ does not name a type of
    lamp.ino: In function ‘void handleRoot ()’:
    lamp: 107: error: ‘u201ctext’ was Not Declared in this scope
    lamp: 107: error: ‘html’ was Not Declared in this scope
    lamp.ino: In function ‘void setup ()’:
    lamp: 132: error: ‘u201crgb’ was Not Declared in this scope
    lamp: 134: error: ‘u201c’ was Not Declared in this scope
    lamp: 134: error: ‘u201d’ was Not Declared in this scope
    lamp.ino: In function ‘fade void (int)’:
    lamp: 168: error: expected ‘)’ before ‘u2013’
    stray ‘\’ in program

      can anyone send me the file on pariswalt at gmail

    thank you I am new and I not understand much in the mistakes thank yo

    • 14/09/2016 at 08:23

      hi, thanks for the feedback. i have updated the code format on my blog (WordPress really makes it difficult to include code); can you please try this new format and let me know if it compiles well, thanks- Tom

  • 14/09/2016 at 19:37

    great works great thank you for responsiveness

  • 15/09/2016 at 02:58

    just to see if we can change something for the less dark red

  • Pingback: NodeMCU sorgt für Stimmung in der Winterzeit | Elektronik Bastelecke

  • 30/12/2016 at 04:27

    would it be possible to connect to the existing network? That way you wouldn’t need to disconnect device from existing network just to change colour.

    • 05/01/2017 at 09:22

      Hi, yes it would be easy to re-write the code to let the ESP8266 connect to your local WiFi network, so you can access it by its IP address in your browser, and even with a customised URL if you can configure your router. However, my intention with this project was to make it portable, so it works wherever you take it, without changing the firmware. If you dig into it, you can probably come up with a better solution by including configuration page in the webserver (saved in SPIFFS) where you can configure access to local WiFi networks without having to re-flash any code, as e.g. in this project

  • 16/05/2017 at 12:40

    how can I make to the values RGB save in EEprom memory , so when the power is gone and come back stay in the last color I select.

    What code I need add to the sketch


  • 18/05/2017 at 04:20

    Hi how can I make to save the last color u chose if the power gone out .
    I can store the values in EEprom memory but the problem is I don’t know how do it.
    Can u help me.


    • 31/05/2017 at 09:35

      Hi, that works yes, you have to add these lines to the code:
      #include // in the setup you do the read, to use the stored value in analogWrite

      EEPROM.write(0, red.toInt()); // in handleRoot you store the new values in EEPROM
      EEPROM.write(1, green.toInt());
      EEPROM.write(2, blue.toInt());
      EEPROM.commit(); // needed with ESP8266


Leave a Reply

Your email address will not be published. Required fields are marked *