一、前言
ESP32 自带的电容式触摸传感器是其非常实用的高级外设之一,无需额外硬件就能实现触摸检测、手势识别等功能,广泛应用于触摸开关、智能穿戴、智能家居等场景。本文将从基础原理到实战代码,手把手教你玩转 ESP32 触摸传感器。
二、触摸传感器核心知识
1. 触摸传感器简介(电容式触摸)
ESP32 内置了 10 个电容式触摸感应通道(T0-T9),对应引脚如下(不同开发板可能略有差异,以常用的 ESP32-DevKitC 为例):(ESP32-DevKitC 系列)
| 触摸通道 | 对应引脚 | 触摸通道 | 对应引脚 |
|---|---|---|---|
| T0 | GPIO4 | T5 | GPIO12 |
| T1 | GPIO0 | T6 | GPIO14 |
| T2 | GPIO2 | T7 | GPIO27 |
| T3 | GPIO15 | T8 | GPIO33 |
| T4 | GPIO13 | T9 | GPIO32 |
工作原理:电容式触摸的核心是「人体电容耦合」。当手指触摸感应引脚时,人体与引脚形成一个额外的电容,ESP32 内部的触摸检测模块会检测到电容值变化,进而判断是否有触摸动作。
- 无触摸时:引脚检测到的原始值(基线值)较高;
- 有触摸时:电容增加,检测值会明显下降(通常下降 100+ 即可判定为触摸)。
2. 核心函数:touchRead ()
Arduino 环境为 ESP32 封装了极简的触摸读取函数,是使用触摸传感器的核心:
int touchValue = touchRead(pin);
- 参数 :
pin为触摸通道对应的引脚号(如 T0 对应 GPIO4,直接传 4 即可); - 返回值:int 类型的触摸原始值(范围 0~4095),值越小表示触摸越明显;
- 特点:无需初始化引脚(不用 pinMode ()),直接读取即可。
三、实战 1:基础触摸开关(控制 LED)
1. 功能需求
触摸 ESP32 的 T0(GPIO4)引脚,控制板载 LED(GPIO2)的亮灭(触摸一次亮,再触摸一次灭)。
2. 硬件接线
- ESP32 板载 LED:多数开发板已默认连接 GPIO2,无需额外接线;
- 触摸引脚:直接用手触摸 GPIO4 引脚即可(也可接一小块金属片提升灵敏度)。
3. 完整代码
// 定义引脚
const int touchPin = 4; // T0 触摸通道对应 GPIO4
const int ledPin = 2; // 板载 LED 对应 GPIO2
// 触摸阈值:低于该值判定为触摸(可根据实际调整)
const int touchThreshold = 300;
// 状态变量
bool ledState = LOW; // LED 初始状态:灭
bool lastTouchState = false; // 上一次触摸状态
unsigned long lastTouchTime = 0; // 上一次触摸时间(防抖)
const unsigned long debounceDelay = 50; // 防抖延时 50ms
void setup() {
// 初始化串口(用于调试输出触摸值)
Serial.begin(115200);
// 初始化 LED 引脚为输出模式
pinMode(ledPin, OUTPUT);
// 初始设置 LED 状态
digitalWrite(ledPin, ledState);
}
void loop() {
// 1. 读取触摸值
int touchValue = touchRead(touchPin);
// 打印触摸值(方便调试)
Serial.print("触摸原始值:");
Serial.println(touchValue);
// 2. 防抖处理:判断是否有效触摸
bool currentTouchState = (touchValue < touchThreshold);
unsigned long currentTime = millis();
if (currentTouchState != lastTouchState && (currentTime - lastTouchTime) > debounceDelay) {
// 3. 检测到触摸动作(状态变化)
if (currentTouchState) {
// 切换 LED 状态
ledState = !ledState;
digitalWrite(ledPin, ledState);
// 打印状态信息
Serial.println(ledState ? "LED 点亮" : "LED 熄灭");
}
// 更新触摸时间和状态
lastTouchTime = currentTime;
lastTouchState = currentTouchState;
}
// 延时 100ms,降低串口输出频率
delay(100);
}
4. 代码详细解释
| 代码段 | 功能说明 |
|---|---|
const int touchThreshold = 300 |
触摸阈值:需根据实际硬件调整(不同环境 / 引脚的基线值不同),建议先串口打印无触摸时的原始值,阈值设为「基线值 - 100」左右 |
debounceDelay = 50 |
防抖延时:避免手指触摸时的抖动导致误触发(比如触摸一次识别成多次) |
touchRead(touchPin) |
核心函数:读取触摸引脚的原始值,无触摸时约 400~800,触摸时会骤降 |
currentTouchState != lastTouchState |
检测触摸状态变化(从「无触摸」到「有触摸」),避免持续触摸时反复切换 LED |
ledState = !ledState |
状态取反:实现「触摸一次切换」的逻辑 |
5. 测试方法
- 将代码上传到 ESP32;
- 打开 Arduino 串口监视器(波特率 115200);
- 触摸 GPIO4 引脚,观察:
- 串口打印的触摸值会从 400+ 降到 300 以下;
- LED 状态随之切换,松开后保持状态。
四、实战 2:进阶应用 ------ 简单手势识别(滑动检测)
1. 功能需求
通过 2 个触摸引脚(T0=GPIO4、T2=GPIO2)实现「左滑」「右滑」识别:
- 先触摸 GPIO4(左)再触摸 GPIO2(右)→ 判定为「右滑」,LED 快闪 3 次;
- 先触摸 GPIO2(右)再触摸 GPIO4(左)→ 判定为「左滑」,LED 慢闪 3 次。
2. 硬件接线
- 触摸引脚 1:GPIO4(接金属片,标记为「左」);
- 触摸引脚 2:GPIO2(接金属片,标记为「右」);
- LED:GPIO13(板载 LED 若占 GPIO2,换用 GPIO13)。
3. 完整代码
// 定义触摸引脚和LED引脚
const int touchLeft = 4; // 左触摸引脚(T0)
const int touchRight = 2; // 右触摸引脚(T2)
const int ledPin = 13; // LED 引脚(避开触摸引脚)
// 触摸阈值
const int touchThreshold = 300;
// 手势检测超时(超过 1s 未完成滑动则重置)
const unsigned long gestureTimeout = 1000;
// 状态变量
enum Gesture { NONE, LEFT_SWIPE, RIGHT_SWIPE }; // 手势枚举
Gesture currentGesture = NONE;
bool leftTouched = false; // 左引脚是否被触摸过
bool rightTouched = false; // 右引脚是否被触摸过
unsigned long touchStartTime = 0; // 首次触摸时间
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
}
void loop() {
// 读取两个触摸引脚的值
int valLeft = touchRead(touchLeft);
int valRight = touchRead(touchRight);
Serial.print("左触摸值:");
Serial.print(valLeft);
Serial.print(" | 右触摸值:");
Serial.println(valRight);
// 检测左触摸
bool isLeftTouch = (valLeft < touchThreshold);
// 检测右触摸
bool isRightTouch = (valRight < touchThreshold);
// 手势检测逻辑
detectGesture(isLeftTouch, isRightTouch);
// 执行手势对应的动作
executeGestureAction();
delay(50); // 降低检测频率
}
// 手势检测函数
void detectGesture(bool isLeft, bool isRight) {
unsigned long now = millis();
// 1. 首次触摸:记录开始时间
if ((isLeft || isRight) && !leftTouched && !rightTouched) {
touchStartTime = now;
leftTouched = isLeft;
rightTouched = isRight;
}
// 2. 检测滑动(第二次触摸)
if (leftTouched && isRight && !isLeft) {
// 左→右:右滑
currentGesture = RIGHT_SWIPE;
resetGestureState(); // 重置状态
} else if (rightTouched && isLeft && !isRight) {
// 右→左:左滑
currentGesture = LEFT_SWIPE;
resetGestureState(); // 重置状态
}
// 3. 超时重置
if ((now - touchStartTime) > gestureTimeout && (leftTouched || rightTouched)) {
resetGestureState();
}
}
// 重置手势状态
void resetGestureState() {
leftTouched = false;
rightTouched = false;
touchStartTime = 0;
}
// 执行手势动作
void executeGestureAction() {
switch (currentGesture) {
case RIGHT_SWIPE:
Serial.println("识别到:右滑 → LED 快闪");
flashLED(100, 3); // 快闪:间隔 100ms,闪 3 次
currentGesture = NONE;
break;
case LEFT_SWIPE:
Serial.println("识别到:左滑 → LED 慢闪");
flashLED(500, 3); // 慢闪:间隔 500ms,闪 3 次
currentGesture = NONE;
break;
default:
break;
}
}
// LED 闪烁函数(interval=间隔时间,times=次数)
void flashLED(int interval, int times) {
for (int i = 0; i < times; i++) {
digitalWrite(ledPin, HIGH);
delay(interval);
digitalWrite(ledPin, LOW);
delay(interval);
}
}
4. 代码核心解释
- 枚举类型
Gesture:定义手势类型(无、左滑、右滑),让代码更易读; detectGesture()函数:核心逻辑是检测「首次触摸→第二次触摸」的顺序,判定滑动方向;- 超时重置:避免单次触摸后长时间未操作导致状态异常;
flashLED()封装:将闪烁逻辑封装为函数,复用性更强。
五、实用技巧与注意事项
- 阈值调整 :
- 不同 ESP32 模块、环境(温度 / 湿度)的基线值不同,建议先串口打印无触摸时的原始值,阈值设为「基线值 - 80~150」;
- 触摸金属片面积越大,灵敏度越高(可接 1~2cm² 的铜片 / 铝片)。
- 抗干扰 :
- 触摸引脚尽量远离电源线、高频信号线;
- 增加防抖延时(50~100ms),避免误触发。
- 引脚注意事项 :
- GPIO0:烧录程序时需接地,作为触摸引脚时烧录前需断开触摸;
- GPIO12:部分模块的启动电压由该引脚决定,使用时需确认硬件兼容。
六、总结
核心知识点回顾
- ESP32 触摸传感器基于电容式检测 ,核心函数是
touchRead(),返回值越小表示触摸越明显; - 基础应用(触摸开关)需关注阈值设置 和防抖处理,避免误触发;
- 进阶手势识别的核心是检测多引脚触摸的时间顺序,并增加超时逻辑保证稳定性。
拓展方向
- 用多个触摸引脚实现更多手势(如双击、长按、三指触摸);
- 结合 PWM 实现触摸调光(根据触摸值大小调整 LED 亮度);
- 接入物联网平台,实现触摸控制智能家居设备(如灯光、风扇)。