Difficulty: Beginner Version: V2
Last week you learned to display things on your OLED. This week, you'll learn to sense touch - the input side of the equation. By the end of this progression, you'll control animated graphics with just your fingertips.
Your body is slightly conductive and carries a small electrical charge. When you touch a conductive surface (like a wire connected to a GPIO pin), you change the capacitance of that circuit. The ESP32-S3 can measure this change.
| Term | Plain English |
|---|---|
| Capacitance | The ability to store electrical charge. Think of it like a tiny battery that charges/discharges very fast. |
| touchRead() | Arduino function that returns a number representing how much capacitance the pin sees. |
| Threshold | The dividing line between "touched" and "not touched" - a number you choose. |
| Calibration | Finding the right threshold for YOUR setup (wire length, humidity, etc. all affect readings). |
| Question | Answer |
|---|---|
| Why does it work through thin materials? | Capacitance works through insulators! Your phone screen has glass over the sensors. You can put tape or thin plastic over your touch wires. |
| Why doesn't it work with gloves? | Thick insulating materials (rubber, leather) block the capacitive coupling. Special "touchscreen gloves" have conductive fingertips. |
| Does humidity affect it? | Yes! Humid air is more conductive. Your thresholds might need adjustment on rainy days vs dry winter days. |
| Can I make larger touch areas? | Yes - use copper tape, aluminum foil, or conductive fabric. Larger area = more capacitance change = easier to detect. |
| Why do readings fluctuate? | Electrical noise from nearby devices, fluorescent lights, and even your body's static charge cause variation. That's why we use thresholds, not exact values. |
| Question | Answer |
|---|---|
| Which pins support touch? | On XIAO ESP32-S3: GPIO 1-7 (D0-D6). That's 7 touch inputs! Not all ESP32 pins have touch capability. |
| What values does touchRead() return? | On ESP32-S3: roughly 20,000-30,000 when NOT touched, 80,000-150,000+ when touched. (Original ESP32 was opposite - lower when touched!) |
| Why is ESP32-S3 different from original ESP32? | Different touch sensing hardware. S3 uses a newer design where touched = HIGHER values. Old tutorials might have it backwards! |
| Can I use touch and I2C on the same pins? | Tricky! GPIO 5 and 6 are default I2C (SDA/SCL) for our OLED. While they CAN do touch, we avoid them to prevent conflicts. |
| How fast can I read touch? | touchRead() takes about 0.5ms. At 60fps (16ms per frame), you can easily read 4+ pins with time to spare. |
We're using 4 of the 7 available touch pins to create a D-Pad (directional pad):
| XIAO Label | GPIO Number | Our Use | Wire Color Suggestion |
|---|---|---|---|
| D0 | GPIO 1 | LEFT | Red |
| D1 | GPIO 2 | UP | Green |
| D2 | GPIO 3 | DOWN | Blue |
| D3 | GPIO 4 | RIGHT | Yellow |
This project uses Adafruit SH110X (different from U8g2 we used in Week 2). Why? It's simpler for beginners and works great with Adafruit's graphics library.
Adafruit SH110XCopy this entire sketch, paste into Arduino IDE, and upload:
/* * CapTouchPinsIntro.ino * V2: D-Pad Visualizer * * Mapping: * D0 (GPIO 1) -> LEFT * D1 (GPIO 2) -> UP * D2 (GPIO 3) -> DOWN * D3 (GPIO 4) -> RIGHT */ #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 int vL, vU, vD, vR; void setup() { Serial.begin(115200); // XIAO ESP32S3 I2C pins 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("D-Pad Init..."); display.display(); delay(500); } void drawArrow(int x, int y, int dir, bool active) { // dir: 0=L, 1=U, 2=D, 3=R if (active) display.fillCircle(x, y, 10, SH110X_WHITE); else display.drawCircle(x, y, 10, SH110X_WHITE); int color = active ? SH110X_BLACK : SH110X_WHITE; if (dir == 0) { // Left display.drawLine(x + 4, y, x - 4, y, color); display.drawLine(x - 4, y, x, y - 4, color); display.drawLine(x - 4, y, x, y + 4, color); } else if (dir == 1) { // Up display.drawLine(x, y + 4, x, y - 4, color); display.drawLine(x, y - 4, x - 4, y, color); display.drawLine(x, y - 4, x + 4, y, color); } else if (dir == 2) { // Down display.drawLine(x, y - 4, x, y + 4, color); display.drawLine(x, y + 4, x - 4, y, color); display.drawLine(x, y + 4, x + 4, y, color); } else if (dir == 3) { // Right display.drawLine(x - 4, y, x + 4, y, color); display.drawLine(x + 4, y, x, y - 4, color); display.drawLine(x + 4, y, x, y + 4, color); } } void loop() { vL = touchRead(PIN_LEFT); vU = touchRead(PIN_UP); vD = touchRead(PIN_DOWN); vR = touchRead(PIN_RIGHT); // Detect touch - threshold calibrated for ESP32-S3 // Touched = value > 80000 bool tL = vL > 80000; bool tU = vU > 80000; bool tD = vD > 80000; bool tR = vR > 80000; display.clearDisplay(); // D-Pad Layout int cx = 64; int cy = 32; int spacing = 22; drawArrow(cx - spacing, cy, 0, tL); // Left drawArrow(cx, cy - spacing, 1, tU); // Up drawArrow(cx, cy + spacing, 2, tD); // Down drawArrow(cx + spacing, cy, 3, tR); // Right // Show raw values (for calibration) display.setTextSize(1); display.setTextColor(SH110X_WHITE); 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); }
The threshold value (80000) might need adjustment for your specific setup:
| Problem | Solution |
|---|---|
| Buttons feel unresponsive | Lower the threshold (try 50000 or 40000) |
| Triggers without touching | Raise the threshold (try 100000 or 120000) |
| Works sometimes, not others | Your threshold is too close to idle values - increase the gap |
| Different pins need different thresholds | Use separate threshold variables for each pin |
Instead of copying, try building piece by piece with Gemini:
| Step | Gemini Prompt to Try |
|---|---|
| 1. Read touch values | "Write ESP32-S3 code using touchRead() to read 4 capacitive touch pins and print values to Serial Monitor" |
| 2. Add threshold | "Add boolean variables that are true when touch value exceeds 80000" |
| 3. Draw circles | "Using Adafruit SH1106G, draw 4 circles in a D-pad layout on a 128x64 OLED" |
| 4. Make interactive | "Fill the circle when its corresponding touch pin is active" |
| 5. Add arrows | "Draw arrow symbols inside each circle pointing in the appropriate direction" |
bool touched = value > 80000; converts analog reading to yes/no.