🖐️ ESP32 Capacitive Touch Sensing

A complete guide to using the ESP32's built-in capacitive touch pins as buttons, switches, and wake-up sources — no physical buttons required.

OVERVIEW

Introduction

The ESP32 microcontroller includes a set of dedicated capacitive touch GPIOs that let you replace mechanical push-buttons with simple conductive surfaces — a piece of wire, a strip of aluminium foil, or even a copper pad on a PCB. Touching the surface with a finger is enough to trigger a detectable change in the pin's electrical reading.

These inputs are extremely versatile: these pins can be easily integrated into capacitive pads to replace mechanical buttons, and the touch pins can also be used as a wake-up source when the ESP32 is in deep sleep. In this guide we'll walk through the theory, the pinout, the wiring, and two complete working examples — reading raw touch values and using touch input to switch an LED on and off.

THEORY

How Capacitive Touch Sensing Works

Human skin holds a small electrical charge. ESP32 uses the electrical properties of the human body as input — when the touch-sensing pin is touched with a finger, a small electric charge is drawn to the point of contact, triggering a capacitance variation that results in an analog signal. Two successive approximation ADCs (SAR ADCs) then convert this analog signal into a digital number that your program can read.

👆 Finger
touches pad
Capacitance
changes
SAR ADC
converts signal
touchRead()
returns lower value
Important: On the original ESP32, the touch reading value decreases when touched and stays high when untouched — the opposite of what many people intuitively expect. Typical untouched readings sit around 60–80, dropping below 30–40 when touched.

Touch detection is not handled by the main CPU core alone — touch detection in ESP32 is managed by the ULP coprocessor, so these touch pins can also be used to wake the ESP32 from deep sleep, making them ideal for battery-powered wake-up buttons.

HARDWARE

Touch-Capable Pins on the ESP32

The ESP32 has 10 capacitive touch GPIOs, labelled T0 through T9. Each touch channel is tied to a specific, fixed GPIO number that cannot be changed.

Touch ChannelGPIO Pin
T0GPIO 4
T1GPIO 0
T2GPIO 2
T3GPIO 15
T4GPIO 13
T5GPIO 12
T6GPIO 14
T7GPIO 27
T8GPIO 33
T9GPIO 32

Source: Touch0 is T0 - GPIO 4, Touch1 is T1 - GPIO 0, Touch2 is T2 - GPIO 2, Touch3 is T3 - GPIO 15, Touch4 is T4 - GPIO 13, Touch5 is T5 - GPIO 12, Touch6 is T6 - GPIO 14, Touch7 is T7 - GPIO 27, Touch8 is T8 - GPIO 33, Touch9 is T9 - GPIO 32.

ESP32 DevKit
T0/GPIO4 T1/GPIO0 T2/GPIO2 T3/GPIO15 T4/GPIO13 T5/GPIO12 T6/GPIO14 T7/GPIO27 T8/GPIO33 T9/GPIO32
Board note: Not every development board exposes all 10 touch pins. Depending on your specific ESP32 board, you may have access to only 8 of the 10 channels. Also note a well-known quirk: GPIO 33 is swapped with GPIO 32 in the Arduino core assignment — if you want to refer to GPIO 32 you should use T8 in code, and if you want to refer to GPIO 33 you should use T9.
HARDWARE

Wiring the Touch Pad

Setting up a touch input is remarkably simple — no external resistors or components are required. No resistors, no other components are needed for the basic touch read. All you need is a single conductive object connected to a touch-capable GPIO.

What you need

  • 1× ESP32 development board
  • 1× male-to-male jumper wire
  • (Optional) LED + short wire for output demo
  • (Optional) Aluminium foil as a larger touch pad

Wiring steps

1 Insert one end of a jumper wire into GPIO 4 (T0) on the board.
2 Leave the other end of the wire exposed as the touch surface.
3 (Optional) Attach the wire to a piece of aluminium foil for a larger pad.
4 Power the board via USB — no extra wiring is needed.

You can use everyday conductive materials as touch surfaces: aluminium is a good conductor, so you can use aluminium foil as a touch plate. Conductive paint, conductive cloth, or even a bare copper PCB pad also work well.

CODE EXAMPLE 1

Reading Raw Touch Values

The core function used for touch sensing is touchRead(). In the Arduino IDE, you use the touchRead() function, which accepts as argument the GPIO you want to read: touchRead(GPIO). You can pass either the GPIO number or the T-channel name — you can either pass the touch sensor number (T0) or the GPIO number (4).

// ESP32 Touch Test — Reads Touch0 (GPIO 4) and prints the value

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("ESP32 Touch Test");
}

void loop() {
  Serial.println(touchRead(4));  // GPIO 4 = Touch0
  delay(1000);
}
    
Expected behaviour: When the wire is untouched, the Serial Monitor shows values in the 60–80 range. As soon as you touch the wire, the reading drops sharply — typically below 30–40 — because the finger's capacitance pulls the reading down.

Uploading and Testing (Arduino IDE)

1 Open Arduino IDE and select File → Examples → ESP32 → Touch → TouchRead
2 Select your board under Tools → Board → ESP32 Dev Module
3 Select the correct COM port under Tools → Port
4 Click Upload
5 Open the Serial Monitor and set the baud rate to 115200
6 Touch the wire and observe the value drop
TOOLS

Visualizing Touch Data

Watching raw numbers scroll by in the Serial Monitor works, but it's much easier to spot the touch/no-touch threshold visually. You have two good options:

Arduino Serial Plotter

Built directly into the Arduino IDE — no extra download needed. It plots incoming numeric values in real time as a live line graph.

Third-party Serial Terminal

A lightweight standalone utility (such as a serial terminal / plotting tool) that can be run without installation and offers similar real-time graphing of serial data.

Using the Arduino Serial Plotter

1 Upload the basic touch-read sketch shown above
2 Open Tools → Serial Plotter in the Arduino IDE
3 Observe the flat high line when the pad is untouched
4 Touch the pad and watch the line dip sharply
5 Release and confirm the line returns to the baseline

This graphical view makes it very easy to pick a reliable numeric threshold that separates "touched" from "untouched" states for your specific wire, pad material, and length.

CODE EXAMPLE 2

Using Touch as a Digital Switch (LED Control)

Once you know your threshold, you can use a simple if comparison to turn the touch pad into a digital button that drives an output — for example, an LED, a relay, or any other load.

// Touch-controlled LED example
const int touchPin = 4;    // GPIO 4 = Touch0
const int ledPin   = 2;    // Built-in LED on many boards
const int threshold = 40;

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  int touchValue = touchRead(touchPin);
  Serial.println(touchValue);

  if (touchValue < threshold) {
    digitalWrite(ledPin, HIGH);   // Touched → LED ON
  } else {
    digitalWrite(ledPin, LOW);    // Not touched → LED OFF
  }
  delay(100);
}
    

This mirrors the classic touch-sense pattern used across ESP32 projects: based on the values, we can set a threshold, so that when the reading drops below the threshold, we will toggle the LED.

Tip — Debouncing: For production-quality projects, add a debounce delay so that brief noise spikes near the threshold don't cause flickering output. A simple debounce technique compares the current reading against the previous one and only accepts a change after it has been stable for a set number of milliseconds.

Uploading and Testing

1 Wire the jumper to GPIO 4 as before
2 Connect an LED (with resistor) to GPIO 2, or use the board's built-in LED
3 Upload the sketch above to the ESP32
4 Open the Serial Monitor at 115200 baud to watch live values
5 Touch the wire and confirm the LED turns on
6 Release the wire and confirm the LED turns off
USE CASES

Real-World Applications

Because touch pins behave like ordinary digital inputs once thresholded, they can substitute for mechanical buttons almost anywhere: you can use these GPIOs to update existing simple push button projects or to create light switches, musical instruments, or custom interactive surfaces.

🔌 Touch light switches

Replace a mechanical wall switch with a hidden copper pad behind a panel.

📋 Menu / navigation buttons

Build sleek, seamless control panels for IoT devices with no moving parts.

😴 Deep-sleep wake source

Wake a battery-powered ESP32 from deep sleep with a single touch.

🎹 Interactive surfaces

Create touch-sensitive musical instruments or gesture pads.

Touch Pad
(foil / wire)
ESP32
touchRead()
Threshold
Comparison
Relay / LED /
Menu Action