公告 欢迎您,我的朋友,目前开放免费注册,注册登录后查看更多内容!开通会员送积分,相应文档随便看,积分充值登录后个人中心内按需充值!

🔑 电容触摸开关(自锁)Arduino源码|固件

esp32-s3-wroom,输出IO1(指示灯IO48-红灯,WS2812),实现自锁式开关,传感器TTP223B电容式,默认低电平,触摸后高电平。耐压可以3.3V/5V均可

20261009_193751_692_mobile.jpg

多传感器代码,默认生成固件,电容式触摸开关,针脚IO1,指示灯IO48,3V3/GND供电

/*
* t_touch.cpp — TTP223B 电容触摸 自锁开关(软件双稳态)
* 烧录: platformio run -e touch -t upload
 *
* 硬件: TTP223B 模块 VCC / GND / SIG;  SIG -> IO1;  板载 WS2812(IO48) 红灯作输出指示。
* 行为: 模块本身为【点动型】(常态低, 触摸高, ~12s 无触摸自动转低功耗模式)。
*       本固件把"点动"变成【自锁/双稳态】: 每触摸一下翻转一次,
*             ON  -> 红灯亮;  OFF -> 红灯灭;  再触摸一下反转。
*       自锁状态存 NVS, 断电不丢(像真正的自锁开关)。
 *
* 接线安全: TTP223B 的 VCC 务必接 3.3V(不要 5V)!  ESP32-S3 的 IO 非 5V 耐压,
*           若模块由 5V 供电, SIG 输出 5V 会烧 IO1。模块支持 2.0~5.5V, 接 3.3V 即可稳定工作。
 *
* 串口命令:
*   help              显示帮助
*   state             查询当前自锁状态 / 武装标志 / 原始电平
*   led on|off        手动置位 / 复位(并保存到 NVS)
*   level high|low    翻转触发电平(模块极性接反时免重烧)
*   restart           软重启
 */
#include <Arduino.h>
#include <Preferences.h>
#include "pins.h"

// ====================== 硬件与参数 ======================
#define TOUCH_PIN 1            // IO1:全板唯一空闲 GPIO(J2 排针引出)
#define TOUCH_DEBOUNCE_MS 40   // 软件去抖(TTP223 内部已 ~80ms,这里再保险一点)
// 板载 WS2812 单像素:复用核心 neopixelWrite,红灯=开,灭=关(见 pins.h PIN_WS2812=48)

static const char *NS = "touchlock";
static bool g_on = false;        // 自锁输出:true = 红灯亮
static bool g_armed = true;      // 边沿武装:手指离开后重新武装,防止一次触摸多次翻转
static int  g_level = -1;        // 去抖后的稳定电平(-1 = 未初始化)
static unsigned long g_levelSince = 0;
static int  g_active = HIGH;     // 触发电平(TTP223 默认触摸=高;接反用 level 命令翻)

// ====================== LED 反馈(板载 WS2812 单像素)======================
static void setLed(bool on)
{
  if (on)
    neopixelWrite(PIN_WS2812, 255, 0, 0); // 红灯
  else
    neopixelWrite(PIN_WS2812, 0, 0, 0);   // 灭
  delayMicroseconds(80);                  // WS2812 帧后 LOW>50us 才锁存,补余量确保可靠
}

// 设定自锁状态(可选保存 NVS:断电保持,符合"自锁开关"语义)
static void applyState(bool on, bool save)
{
  g_on = on;
  setLed(g_on);
  if (save)
  {
    Preferences p;
    p.begin(NS, false);
    p.putBool("on", g_on);
    p.putInt("active", g_active);
    p.end();
  }
  Serial.printf("[STATE] 自锁 %s -> 红灯%s\n", g_on ? "ON " : "OFF", g_on ? "亮" : "灭");
}

// ====================== 串口命令 ======================
static void handleCmd(const String &s)
{
  String c = s;
  c.toLowerCase();
  c.trim();
  if (c == "help" || c == "?")
  {
    Serial.println("===== TTP223B 触摸自锁开关 串口命令 =====");
    Serial.println(" help              显示本帮助");
    Serial.println(" state             查询当前自锁状态/武装标志/原始电平");
    Serial.println(" led on|off        手动置位/复位(并保存到 NVS)");
    Serial.println(" level high|low    翻转触发电平(接线极性接反免重烧)");
    Serial.println(" restart           软重启");
  }
  else if (c == "state")
  {
    Serial.printf("[STATE] on=%s  armed=%s  active=%s  raw_pin=%d\n",
                  g_on ? "true" : "false", g_armed ? "true" : "false",
                  g_active == HIGH ? "HIGH" : "LOW", (int)digitalRead(TOUCH_PIN));
  }
  else if (c == "led on")
  {
    applyState(true, true);
  }
  else if (c == "led off")
  {
    applyState(false, true);
  }
  else if (c.startsWith("level "))
  {
    String m = c.substring(6);
    m.trim();
    if (m == "high")
      g_active = HIGH;
    else if (m == "low")
      g_active = LOW;
    else
    {
      Serial.println("level usage: level high|low");
      return;
    }
    applyState(g_on, true); // 顺带保存极性
    Serial.printf("[LEVEL] active=%s\n", g_active == HIGH ? "HIGH" : "LOW");
  }
  else if (c == "restart" || c == "reboot")
  {
    Serial.println("rebooting...");
    delay(200);
    ESP.restart();
  }
  else
  {
    Serial.printf("unknown: '%s' (send help)\n", s.c_str());
  }
}

// ====================== setup / loop ======================
void setup()
{
  Serial.begin(115200);
  Serial.setTxTimeoutMs(0); // 监视器关闭后不阻塞
  delay(1000);
  pinMode(TOUCH_PIN, INPUT);   // TTP223B 推挽输出,无需上/下拉
  pinMode(PIN_WS2812, OUTPUT);

  // 恢复上次自锁状态(断电不丢,符合自锁开关语义)
  Preferences p;
  p.begin(NS, true);
  g_on = p.getBool("on", false);
  g_active = p.getInt("active", HIGH);
  p.end();

  setLed(g_on);
  Serial.println("=== TTP223B 触摸自锁开关 ===");
  Serial.printf("TOUCH_PIN=IO%d  ACTIVE=%s  WS2812=IO%d(红)\n",
                TOUCH_PIN, g_active == HIGH ? "HIGH" : "LOW", PIN_WS2812);
  Serial.printf("恢复上次自锁状态: %s\n", g_on ? "ON(红灯亮)" : "OFF(红灯灭)");
  Serial.println("每触摸一次翻转状态;串口 help 看命令");
}

void loop()
{
  // ---- 串口命令:行尾或 80ms 静默判为一条 ----
  static char buf[48];
  static int len = 0;
  static unsigned long last = 0;
  while (Serial.available() > 0)
  {
    char c = (char)Serial.read();
    last = millis();
    if (c == '\n' || c == '\r')
    {
      if (len > 0)
      {
        buf[len] = 0;
        handleCmd(String(buf));
        len = 0;
      }
    }
    else if (len < (int)sizeof(buf) - 1)
    {
      buf[len++] = c;
    }
  }
  if (len > 0 && (millis() - last) > 80)
  {
    buf[len] = 0;
    handleCmd(String(buf));
    len = 0;
  }

  // ---- 自锁核心:稳定上升沿 + 已武装 -> 翻转一次;电平回低 -> 重新武装 ----
  bool raw = (digitalRead(TOUCH_PIN) == g_active);
  unsigned long now = millis();
  if (g_level < 0)
  {
    // 首帧:建立基线,不翻转、不打印
    g_level = raw ? 1 : 0;
    g_levelSince = now;
  }
  else
  {
    if ((raw ? 1 : 0) != g_level)
    {
      g_level = raw ? 1 : 0;
      g_levelSince = now;
    }
    bool stable = (now - g_levelSince) >= TOUCH_DEBOUNCE_MS;
    if (g_level == 1 && stable && g_armed)
    {
      g_armed = false;       // 一次触摸只翻转一次,直到手指离开
      applyState(!g_on, true);
    }
    else if (g_level == 0)
    {
      g_armed = true;        // 手指离开后重新武装,准备下一次触摸
    }
  }

  delay(5);
}

该文档需要「文档会员」身份

请先登录后继续阅读。