← All projects
Sensors Beginner

Buttons Without Buttons: A Dimmer on the ESP32's Capacitive Touch Pins

The ESP32 has 10 built-in touch inputs. A piece of foil, a coin or a copper pad under glass becomes a button: tap to toggle an LED and hold to change brightness smoothly.

Difficulty
Beginner
Time
30 min
Category
Sensors

How it works

Each ESP32 touch input measures the capacitance of whatever conductor is attached. When your finger touches it, capacitance goes up and on the classic ESP32 the touchRead() value drops (e.g. from 70 to 20). No mechanical buttons — just a conductor that can hide behind plastic or glass a few millimetres thick.

Parts

  • ESP32 DevKit (classic ESP32; see below for S2/S3)
  • 3 "buttons": foil, coins, copper pads or PCB pads
  • LED + 330 Ω resistor (or the on-board LED on GPIO 2)

Wiring

Pad Input GPIO
On/off T0 4
Brighter T3 15
Dimmer T4 13

Keep the wires to the pads as short as possible — a long wire reacts to an approaching hand by itself.

Code

const int PADS[3] = {T0, T3, T4};   // GPIO 4, 15, 13
const int LED_PIN = 2;

uint32_t baseline[3];
bool ledOn = false;
bool wasTouched = false;
int brightness = 128;
unsigned long lastStep = 0;
unsigned long lastPrint = 0;

bool touched(int i) {
  // Touch = reading dropped below 70 % of the idle level
  return touchRead(PADS[i]) < baseline[i] * 0.7;
}

void apply() {
  ledcWrite(LED_PIN, ledOn ? brightness : 0);
}

void setup() {
  Serial.begin(115200);
  ledcAttach(LED_PIN, 5000, 8);   // 5 kHz PWM, 8 bits

  // Calibration: don't touch the pads for the first 0.5 s
  for (int i = 0; i < 3; i++) {
    uint32_t sum = 0;
    for (int n = 0; n < 20; n++) {
      sum += touchRead(PADS[i]);
      delay(20);
    }
    baseline[i] = sum / 20;
    Serial.printf("Pad %d baseline: %u\n", i, baseline[i]);
  }
  apply();
}

void loop() {
  // Pad 1 is a toggle (react to the moment of touch)
  bool t = touched(0);
  if (t && !wasTouched) {
    ledOn = !ledOn;
    apply();
  }
  wasTouched = t;

  // Pads 2 and 3 — brightness changes while you hold your finger
  if (ledOn && millis() - lastStep > 30) {
    lastStep = millis();
    if (touched(1)) { brightness = min(255, brightness + 3); apply(); }
    if (touched(2)) { brightness = max(5, brightness - 3); apply(); }
  }

  // Raw readings — for tuning the threshold
  if (millis() - lastPrint > 500) {
    lastPrint = millis();
    Serial.printf("%u\t%u\t%u\n", touchRead(PADS[0]), touchRead(PADS[1]), touchRead(PADS[2]));
  }
  delay(10);
}

Tuning sensitivity

  1. Open the Serial Plotter — you'll see three lines.
  2. Touch each pad and see how far the value drops.
  3. If it triggers when a hand merely approaches, lower the 0.7 factor to 0.5; if it doesn't react through thick plastic, raise it to 0.8.

ESP32-S2 / S3

On these chips the sensor works "the other way round": the value rises on touch and is measured in thousands. Change the condition to touchRead(...) > baseline[i] * 1.3.

Wake on touch

A touch input can wake the ESP32 from deep sleep — perfect for battery devices:

touchSleepWakeUpEnable(T3, 40);   // threshold for the classic ESP32
esp_deep_sleep_start();

Ideas

  • A touch light switch panel behind glass.
  • A counter for touches on a "magic" door handle.
  • A 10-key piano made of foil and a piezo speaker.