Build a do-not-disturb sign that you set with a knob

Turn the knob on the printed door sign to choose "Come in", "Knock first", "Do not disturb" or "Asleep". The sign glows in the matching colour, and the family can check it on their phones.
  • Level:Intermediate
  • Duration: 50 min
  • Ages 10+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a do-not-disturb sign that you set with a knob – EEMENTOR STEM™ lesson

Busy with homework, on a video call, or fast asleep? Tell everyone before they knock. Turn the knob on your door sign to pick one of four states, and the sign glows green, yellow, red or a soft blue. The ESP32 also shows your state on a web page, so the family can check from the kitchen whether it's a good moment to come in, and you can change it from your phone too. Press the knob to make the light blink for a moment, so people notice the change.

What you'll learn

  • How a rotary encoder tells which way it is turned
  • How to mix colours on an RGB LED with PWM
  • How to keep a knob and a web page in step
  • How to describe several states with one table (an array of structs)

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • Rotary encoder module (45-in-1 Sensor Kit)
  • RGB LED module (45-in-1 Sensor Kit)
  • 3 resistors, 220 Ω (ESP32 IoT Starter Kit)
  • A 2.4 GHz Wi-Fi network
  • The printed parts from the 3D printing box below (door sign), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Build a do-not-disturb sign that you set with a knob
Breadboard view. Check each connection against the wiring table below.
From To
Encoder CLK ESP32 GPIO 32
Encoder DT ESP32 GPIO 33
Encoder SW ESP32 GPIO 14
Encoder + and GND 3V3 and GND
ESP32 GPIO 25 220 Ω resistor, then RGB module R
ESP32 GPIO 26 220 Ω resistor, then RGB module G
ESP32 GPIO 27 220 Ω resistor, then 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).

#include <WiFi.h>
#include <WebServer.h>

const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

const int CLK_PIN = 32, DT_PIN = 33, SW_PIN = 14;
const int RED_PIN = 25, GREEN_PIN = 26, BLUE_PIN = 27;

struct State {
  const char* text;
  byte r, g, b;               // colour of the LED, 0-255
  const char* web;            // colour on the web page
};
const State STATES[] = {
  {"Come in", 0, 255, 0, "#2ECC71"},
  {"Knock first", 255, 120, 0, "#F2C94C"},
  {"Do not disturb", 255, 0, 0, "#E63946"},
  {"Asleep", 0, 0, 40, "#1B3A7B"},
};
const int COUNT = 4;

WebServer server(80);
int state = 0;
int lastClk = HIGH;
unsigned long blinkUntil = 0;

void show() {
  const State& s = STATES[state];
  bool off = millis() < blinkUntil && (millis() / 150) % 2;    // blink after a change
  analogWrite(RED_PIN, off ? 0 : s.r);
  analogWrite(GREEN_PIN, off ? 0 : s.g);
  analogWrite(BLUE_PIN, off ? 0 : s.b);
}

void setState(int s) {
  state = (s + COUNT) % COUNT;           // wraps round in both directions
  blinkUntil = millis() + 1500;
  Serial.printf("Now: %s\n", STATES[state].text);
}

void readKnob() {
  int clk = digitalRead(CLK_PIN);
  if (clk != lastClk && clk == LOW) {    // one click of the knob
    if (digitalRead(DT_PIN) != clk) setState(state + 1);   // clockwise
    else setState(state - 1);                               // anticlockwise
  }
  lastClk = clk;
  if (digitalRead(SW_PIN) == LOW && millis() > blinkUntil) blinkUntil = millis() + 1500;   // press: blink
}

void handleRoot() {
  const State& s = STATES[state];
  String html = "<!doctype html><meta name=viewport content='width=device-width'><meta http-equiv=refresh content=10><title>Door</title>"
                "<body style='font-family:sans-serif;max-width:420px;margin:24px auto;padding:0 16px;text-align:center'>"
                "<div style='padding:40px 16px;border-radius:24px;color:#fff;font-size:30px;font-weight:bold;background:" + String(s.web) + "'>" + s.text + "</div><p>";
  for (int i = 0; i < COUNT; i++) html += "<a style='display:inline-block;margin:6px;padding:10px 14px;border-radius:10px;background:#eee;color:#333;text-decoration:none' href='/set?s=" + String(i) + "'>" + STATES[i].text + "</a>";
  html += "</p></body>";
  server.send(200, "text/html", html);
}

void handleSet() {
  setState(server.arg("s").toInt());
  server.sendHeader("Location", "/");
  server.send(303);
}

void setup() {
  Serial.begin(115200);
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  pinMode(SW_PIN, INPUT_PULLUP);
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);

  WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print(" connected. Open http://");
  Serial.println(WiFi.localIP());
  server.on("/", handleRoot);
  server.on("/set", handleSet);
  server.begin();
}

void loop() {
  server.handleClient();
  readKnob();
  show();
}

Try it

Open the Serial Monitor at 115200 baud: after connecting, the ESP32 prints its address, such as http://192.168.1.42. Open it in a browser on a phone or computer in the same Wi-Fi.

  1. Turn the knob one click at a time: the light goes green, yellow, red, blue and round again.
  2. Reload the web page: it shows the same state in big letters. It also reloads itself every 10 seconds.
  3. Tap a state on the page: the sign changes and blinks. Add the page to your family's home screens.

How it works

Inside the rotary encoder, two contacts called CLK and DT switch on and off as you turn, a little out of step with each other. When CLK falls, the sketch looks at DT: if it is still high, CLK changed first and the knob turned clockwise; if DT already fell, it turned the other way. This pattern is called a quadrature signal.

The four states live in one table, an array of structs. Each entry holds the text, the LED colour and the colour for the web page, so adding a fifth state is one new line. (s + COUNT) % COUNT wraps the number round, so turning left from "Come in" goes to "Asleep".

analogWrite() mixes colours with PWM: it switches each colour on and off so fast that your eye sees a steady mix. Full red and about half green make yellow; a little blue makes a soft night colour.

Try this next

  • Add "Back in 15 minutes": a long press starts a timer that switches back to "Come in".
  • Play a short beep on a buzzer when someone changes the state from the web page.
  • Make the colours fade smoothly into each other instead of switching.

Troubleshooting

  • Turning the knob jumps two states, or goes the wrong way: swap the CLK and DT wires, or the two pin numbers in the sketch.
  • Yellow looks orange, or green looks wrong: RGB modules differ. Change the numbers in the STATES table until you like the colours.
  • One colour never lights: check that wire and its resistor, and that − goes to GND.
  • 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.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit