What is Capacitive Touch?
Your body is a conductor
Your skin carries a tiny electrical charge. When you touch a wire connected to a GPIO pin, you change the electrical field around it.
The chip measures change
The ESP32-S3 reads a number. When nothing touches the wire: low number (~22,000). When you touch: high number (~100,000+).
You set the threshold
You decide: "above 50,000 = touched." Every setup is different. Use the live dashboard below to find YOUR numbers.
On ESP32-S3 (our board): higher number = touched. On older ESP32: lower number = touched. Old tutorials may have it backwards!
What the numbers look like
Baseline values depend on your specific board, wire length, humidity, desk surface, and nearby electronics. Always measure YOUR values first using the dashboard. Do not hardcode 80,000 without checking.
Pin Map & Wiring
The XIAO ESP32-S3 has 13 touch-capable pins (pink labels in the diagram). We use 2 for this test, and 4 for the Touch-Pad project.
2-Key Test Wiring (this experiment)
You do not need any extra components. Just a jumper wire plugged into the pin. The wire itself acts as the touch electrode. Longer wire = more sensitive. Larger surface area at the end = more sensitive.
Touch pin reference table
| XIAO Label | GPIO Number | Arduino name | Touch-Pad use | Available? |
|---|---|---|---|---|
| D0 / A0 | GPIO1 | touchRead(1) | LEFT | โ touch |
| D1 / A1 | GPIO2 | touchRead(2) | UP / KEY 1 | โ touch |
| D2 / A2 | GPIO3 | touchRead(3) | DOWN | โ touch |
| D3 / A3 | GPIO4 | touchRead(4) | RIGHT / KEY 2 | โ touch |
| D4 / A4 | GPIO5 | โ | โ | SDA (I2C) |
| D5 / A5 | GPIO6 | โ | โ | SCL (I2C) |
Electrode types โ what works best?
Bare pin tip
Very small area. Works only when directly touched.
Insulated wire
Plastic sleeve increases area + adds dielectric. Better signal.
Foil / copper tape pad
Large flat surface. Works even with hovering nearby.
Conductive fabric
Soft, wearable. Good for toy skins and soft buttons.
Arduino Sketch โ 2-Key Test
This sketch reads two touch pins and sends the values over USB to your computer. You will use the Live Dashboard (next section) to see the values in real time.
-
1
Open Arduino IDE
Make sure you have the esp32 by Espressif board package installed. Board: XIAO_ESP32S3. Port: the USB port your board is on.
-
2
Copy the code below
Click the copy button on the code block. Paste into a new Arduino sketch.
-
3
Upload to your board
Click the Upload arrow (โ). Wait for "Done uploading." The board resets automatically.
-
4
Open the Live Dashboard below
Do NOT open Arduino Serial Monitor โ that will block the dashboard. Go directly to the Dashboard section on this page.
// XIAO ESP32-S3 โ Cap Touch 2-Key Test // KEY 1: A1 pin (GPIO2 / TOUCH2) // KEY 2: A3 pin (GPIO4 / TOUCH4) // Optional GND guards: A0, A2 wired to GND (no code needed) void setup() { Serial.begin(115200); Serial.println("KEY1,KEY2"); // CSV header for dashboard } void loop() { int key1 = touchRead(A1); // GPIO2 / TOUCH2 int key2 = touchRead(A3); // GPIO4 / TOUCH4 // Send as CSV โ dashboard reads this format Serial.print(key1); Serial.print(","); Serial.println(key2); delay(50); // 20 readings per second }
You will see lines like: 22813,23377 โ two numbers separated by a comma. First is KEY 1, second is KEY 2. Numbers jump up when you touch.
Read Your Touch Values
This tool runs directly in Chrome. It reads the serial data from your board and shows live graphs, values, and helps you find the right threshold for your setup.
-
1
Make sure your board is plugged in
USB-C cable connected. Sketch already uploaded (see Code section above).
-
2
Click "Connect Serial" below
A popup will appear. Select the port that says usbmodem (Mac) or COM (Windows). Click Connect.
-
3
Click "Auto-calibrate baseline (3s)"
Keep your hands away from the wires for 3 seconds. This sets the resting value for your specific board.
-
4
Touch KEY 1 and KEY 2 a few times
Watch the values spike. The "suggested thresh" in the Stats section will calculate the best threshold for you automatically.
-
5
Use Macro + Micro sliders to set threshold
Macro = big moves (step 5,000). Micro = fine tuning (step 100). Each key can have its own threshold โ use the Per-Key section if KEY 1 and KEY 2 behave differently.
WebSerial does not work in Safari or Firefox. Use Google Chrome or Microsoft Edge for the dashboard to work.
When Things Don't Work
| Problem | Fix | Why |
|---|---|---|
| Dashboard shows no data | Close Arduino Serial Monitor. It blocks the port. Reconnect in dashboard. | Only one program can use the serial port at a time. |
| Connect Serial button does nothing | Switch to Chrome or Edge browser. Safari does not support WebSerial. | WebSerial API is only in Chromium-based browsers. |
| Touch does not register (stays idle) | Lower the threshold with the Macro slider. Try 30,000 first. Or use a larger electrode (foil pad, more wire). | Your baseline may be lower than expected, or your electrode has low sensitivity. |
| Triggers without touching (false touch) | Raise the threshold. Also check if any wire is touching a metal surface or another wire. | Noise or cross-talk between pins causes false readings. |
| KEY 1 and KEY 2 have very different sensitivities | Use the Per-Key Threshold section. Set different thresholds for each key. | Different wire lengths, bends, or electrode sizes change each pin's baseline independently. |
| Values jump around a lot (noisy) | Move away from power supplies, phone chargers, and fluorescent lights. Try on a wooden surface instead of metal desk. | Electromagnetic interference couples into the unshielded wires. |
| Insulated wire more sensitive than bare pin tip | This is normal! The plastic sleeve increases contact area and acts as a dielectric layer. This is good โ it means more of your finger touches the sensor area. | Capacitance = area ร material factor รท distance. More area + dielectric = more signal. |
| Works on desk but not in finished project | Re-run auto-calibrate in the final assembly. The enclosure material, metal parts, and power supply all affect the baseline. Always calibrate in the final context. | Capacitive touch is affected by everything nearby. Lab results โ field results. |
Before drawing conclusions about your setup, test with at least 3 different electrode types (bare pin, insulated wire, foil pad) in the same conditions. One observation on a desk with loose wires is not enough data to know what is happening.
Common questions
Touch-Pad Controller with OLED
Once you understand the 2-key test, you are ready to build the full Touch-Pad. This uses 4 touch pins to make a directional controller that shows arrows on the OLED display.
Go to: Sketch โ Include Library โ Manage Libraries โ Search Adafruit SH110X โ Install. Also install Adafruit GFX Library when prompted.
Touch-Pad pin mapping
| XIAO Label | GPIO | Direction | Wire Color (suggestion) |
|---|---|---|---|
| D0 | GPIO1 | LEFT โ | Red |
| D1 | GPIO2 | UP โ | Green |
| D2 | GPIO3 | DOWN โ | Blue |
| D3 | GPIO4 | RIGHT โ | Yellow |
/* * CapTouchDPad_V2.ino โ Touch-Pad Visualizer * XIAO ESP32-S3 + SH1106G OLED * * Wiring: * D0 (GPIO 1) โ LEFT wire * D1 (GPIO 2) โ UP wire * D2 (GPIO 3) โ DOWN wire * D3 (GPIO 4) โ RIGHT wire * OLED: SDA=GPIO5, SCL=GPIO6 (I2C default) */ #include <Adafruit_GFX.h> #include <Adafruit_SH110X.h> #include <Wire.h> Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1); // Touch Pins const int PIN_LEFT = 1; // D0 const int PIN_UP = 2; // D1 const int PIN_DOWN = 3; // D2 const int PIN_RIGHT = 4; // D3 // Threshold โ adjust after calibration! const int THRESHOLD = 80000; int vL, vU, vD, vR; void setup() { Serial.begin(115200); Wire.begin(5, 6); Wire.setClock(400000); display.begin(0x3C, true); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SH110X_WHITE); display.setCursor(0, 0); display.println("Touch-Pad Ready!"); display.display(); delay(500); } void drawArrow(int x, int y, int dir, bool active) { if (active) display.fillCircle(x, y, 10, SH110X_WHITE); else display.drawCircle(x, y, 10, SH110X_WHITE); int c = active ? SH110X_BLACK : SH110X_WHITE; if (dir == 0) { // Left display.drawLine(x+4,y, x-4,y, c); display.drawLine(x-4,y, x,y-4, c); display.drawLine(x-4,y, x,y+4, c); } else if (dir == 1) { // Up display.drawLine(x,y+4, x,y-4, c); display.drawLine(x,y-4, x-4,y, c); display.drawLine(x,y-4, x+4,y, c); } else if (dir == 2) { // Down display.drawLine(x,y-4, x,y+4, c); display.drawLine(x,y+4, x-4,y, c); display.drawLine(x,y+4, x+4,y, c); } else { // Right display.drawLine(x-4,y, x+4,y, c); display.drawLine(x+4,y, x,y-4, c); display.drawLine(x+4,y, x,y+4, c); } } void loop() { vL = touchRead(PIN_LEFT); vU = touchRead(PIN_UP); vD = touchRead(PIN_DOWN); vR = touchRead(PIN_RIGHT); bool tL = vL > THRESHOLD; bool tU = vU > THRESHOLD; bool tD = vD > THRESHOLD; bool tR = vR > THRESHOLD; display.clearDisplay(); int cx=64, cy=32, sp=22; drawArrow(cx-sp, cy, 0, tL); drawArrow(cx, cy-sp, 1, tU); drawArrow(cx, cy+sp, 2, tD); drawArrow(cx+sp, cy, 3, tR); // Raw values in corners for calibration display.setCursor(0,0); display.print(vL); display.setCursor(50,0); display.print(vU); display.setCursor(0,56); display.print(vD); display.setCursor(50,56);display.print(vR); display.display(); delay(16); // ~60fps }
The code uses THRESHOLD = 80000 as a starting point. Use the 2-key test + dashboard first to find YOUR board's actual values, then update this number in the Touch-Pad code.
The original Touch-Pad project page with additional exercises and build prompts is at: b.parsons.edu/~gitmany/courses/week3/project1-captouch-dpad.html